파이썬에서 객체 속성을 반복


162

몇 가지 속성과 메소드가있는 python 객체가 있습니다. 객체 속성을 반복하고 싶습니다.

class my_python_obj(object):
    attr1='a'
    attr2='b'
    attr3='c'

    def method1(self, etc, etc):
        #Statements

모든 객체 속성과 현재 값을 포함하는 사전을 생성하고 싶지만 동적 방식으로 수행하고 싶습니다 (따라서 나중에 다른 속성을 추가하면 함수를 업데이트해야한다는 것을 기억할 필요가 없습니다).

PHP에서 변수는 키로 사용할 수 있지만 파이썬의 객체는 설명 할 수 없으며 점 표기법을 사용하면 내 var의 이름으로 새로운 속성이 만들어집니다.

더 명확하게하기 위해 :

def to_dict(self):
    '''this is what I already have'''
    d={}
    d["attr1"]= self.attr1
    d["attr2"]= self.attr2
    d["attr3"]= self.attr3
    return d

·

def to_dict(self):
    '''this is what I want to do'''
    d={}
    for v in my_python_obj.attributes:
        d[v] = self.v
    return d

업데이트 : 속성을 사용하면 메서드가 아닌이 객체의 변수 만 의미합니다.


3
stackoverflow.com/questions/1251692/… 도움이 될 수 있습니다.
sean

@sean 특히, stackoverflow.com/a/1251789/4203 이가 는 길입니다. 선택적 predicate인수를 사용 하여 메서드를 제거하는 함수를 필터링 (바운드?)하는 콜 러블을 전달합니다.
행크 게이

sean 당, 이것이 귀하의 질문에 대답합니까? 파이썬에서 객체의 속성을 열거하는 방법은 무엇입니까?
Davis Herring

답변:


227

다음과 같은 수업이 있다고 가정

>>> class Cls(object):
...     foo = 1
...     bar = 'hello'
...     def func(self):
...         return 'call me'
...
>>> obj = Cls()

dir객체를 호출 하면 파이썬 특수 속성을 포함하여 해당 객체의 모든 속성이 반환됩니다. 메소드와 같은 일부 오브젝트 속성을 호출 할 수 있지만

>>> dir(obj)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'bar', 'foo', 'func']

목록 이해를 사용하여 항상 특수한 방법을 필터링 할 수 있습니다.

>>> [a for a in dir(obj) if not a.startswith('__')]
['bar', 'foo', 'func']

또는지도 / 필터를 선호하는 경우

>>> filter(lambda a: not a.startswith('__'), dir(obj))
['bar', 'foo', 'func']

메소드를 필터링하려면 내장 callable을 점검으로 사용할 수 있습니다 .

>>> [a for a in dir(obj) if not a.startswith('__') and not callable(getattr(obj, a))]
['bar', 'foo']

사용하여 클래스와 인스턴스 객체의 차이점을 검사 할 수도 있습니다.

>>> set(dir(Cls)) - set(dir(object))
set(['__module__', 'bar', 'func', '__dict__', 'foo', '__weakref__'])

1
이것은 나쁜 생각입니다, 나는 심지어 그것을 제안하지는 않을 것입니다, 그러나 당신이 가고 싶다면, 적어도 경고를 제공하십시오.
Julian

2
@Meitham @Pablo 문제는 주로이 작업을 수행 할 필요가 없다는 것입니다. 관심있는 속성은 배타적이 아닌 포괄적 이어야합니다 . 이미 본 것처럼, 당신은 그들이 같은 불쾌한 일을 포함하기 위하여려고하고 있기 때문에, 결함이 될 것입니다 방법을 포함하고 제외하려는 모든 시도하고 나 . 중간 결과 또는 내부 데이터 구조를 저장하는 "_foo"라는 속성을 추가하면 어떻게됩니까? 클래스에 속성을 무해하게 추가하면 어떻게됩니까? 라고 말하면 갑자기 갑자기 속성 이 포함됩니다. callabletypes.MethodTypename
Julian

3
코드 @Julian 위를 모두 선택합니다 _fooname그들이 지금이기 때문에 객체의 속성. 포함하지 않으려면 목록 이해 조건에서 제외 할 수 있습니다. 위의 코드는 일반적인 파이썬 관용구이며 파이썬 객체를 조사하는 일반적인 방법입니다.
Meitham

30
실제로 obj .__ dict__가 아마도이 목적에 더 좋습니다.
Dave

5
@Julian dir()은 메타 클래스 (가장 극단적 인 경우) 또는 속성이있는 클래스 @DynamicClassAttribute(다른 극단적 인 경우)를 전달할 때만 "거짓"됩니다 . 두 경우 모두 dirs()래퍼 inspect.getmembers()를 호출하면 이 문제가 해결됩니다. 표준 개체의 경우, 그러나,이 솔루션의 지능형리스트 접근 방식은 비 callables를 필터링 절대적으로 충분하다. 일부 Pythonistas가 그것을 "나쁜 생각"이라고 표시한다고 말하면 나를 당황하게하지만 ... 각각의 사람들에게 나는 추측합니다.
세실 카레

57

일반적으로 클래스에__iter__ 메소드를 넣고 객체 속성을 반복하거나이 믹스 인 클래스를 클래스에 넣습니다.

class IterMixin(object):
    def __iter__(self):
        for attr, value in self.__dict__.iteritems():
            yield attr, value

수업 :

>>> class YourClass(IterMixin): pass
...
>>> yc = YourClass()
>>> yc.one = range(15)
>>> yc.two = 'test'
>>> dict(yc)
{'one': [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14], 'two': 'test'}

이것은 onetwo이후에 정의 된 경우에만 작동합니다 yc = YourClass(). 질문은의 기존 속성을 통한 루핑에 대해 묻습니다 YourClass(). 또한 IterMixin클래스를 상속받는 것이 항상 솔루션으로 사용 가능한 것은 아닙니다.
Xiao

4
vars(yc)다른 클래스에서 상속하지 않고도 동일한 결과를 제공합니다.
Nathan

1
위의 의견은 옳지 않습니다. vars(yc)거의 동일하지만 다시 제공하는 사전은 인스턴스의 자체 __dict__이므로 수정하면 인스턴스에 반영됩니다. 주의하지 않으면 문제가 발생할 수 있으므로 위의 방법이 더 좋을 수도 있습니다 (사전을 복사하는 것이 더 쉬울 수도 있음). 또한 위에서 반환 된 사전은 인스턴스의 __dict__값 이 아니지만 값은 동일하므로 변경 가능한 값을 수정해도 인스턴스의 속성에 반영됩니다.
Nathan

25

일부 답변 / 설명에서 언급했듯이 Python 객체는 이미 속성 사전을 저장합니다 (방법은 포함되지 않음). 이것은로 액세스 할 수 __dict__있지만 더 좋은 방법은 사용하는 것입니다 vars(출력은 동일합니다). 이 사전을 수정하면 인스턴스의 속성이 수정됩니다! 이 기능은 유용 할 수 있지만이 사전을 사용하는 방법에주의해야합니다. 다음은 간단한 예입니다.

class A():
    def __init__(self, x=3, y=2, z=5):
        self.x = x
        self._y = y
        self.__z__ = z

    def f(self):
        pass

a = A()
print(vars(a))
# {'x': 3, '_y': 2, '__z__': 5}
# all of the attributes of `a` but no methods!

# note how the dictionary is always up-to-date
a.x = 10
print(vars(a))
# {'x': 10, '_y': 2, '__z__': 5}

# modifying the dictionary modifies the instance attribute
vars(a)["_y"] = 20
print(vars(a))
# {'x': 10, '_y': 20, '__z__': 5}

사용하는 dir(a)것은이 문제에 대한 명백한 나쁜 접근은 아니지만 이상합니다. 클래스의 모든 속성 과 메소드 를 반복 해야하는 경우 (예 :와 같은 특수 메소드 포함 __init__) 좋습니다. 그러나 이것은 당신이 원하는 것 같지 않으며, 심지어 받아 들여진 대답 조차도 아닙니다. 취성 필터링을 적용하여 메소드를 제거하고 속성 만 남겨 두는 것으로 잘못 나타납니다. A위에서 정의한 클래스에서 이것이 어떻게 실패하는지 알 수 있습니다 .

(사용 __dict__하는 것은 몇 가지 답변으로 이루어졌지만 모두 직접 사용하는 대신 불필요한 방법을 정의합니다. 주석 만 사용을 제안합니다 vars).


1
vars () 접근 방식으로 파악하지 못한 것은 클래스 A에 유형이 사용자 정의 클래스 B 인 다른 객체 인 멤버가있는 상황을 처리하는 방법입니다. vars (a)는 __repr __ () 내가 알고 있듯이 __repr __ ()은 문자열을 반환해야합니다. 그러나 vars (a)를 호출하면이 호출이 문자열 표현이 B 인 dict 대신 중첩 된 dict를 반환하는 것이 합리적입니다.
plafratt

1
당신이 경우 a.b일부 사용자 정의 클래스로 B다음 vars(a)["b"] is a.b과 같은 하나는 기대; 다른 의미는 없습니다 (의 a.b구문 설탕으로 생각하십시오 a.__dict__["b"]). 가지고 d = vars(a)있고 호출 하면 클래스 만 실제로 문자열로 표현되어야하는 방법을 알고 있기 때문에 자체 repr-string을 반환하는 과정에서 repr(d)호출 합니다. repr(d["b"])B
네이선

2

파이썬의 객체는 (함수를 포함한) 속성을이라는 dict에 저장합니다 __dict__. 이 속성을 사용하여 속성에 직접 액세스 할 수는 있지만 일반적으로 사용해서는 안됩니다. 리스트 만 원하면을 호출 dir(obj)하면 모든 속성 이름을 가진 iterable을 리턴 할 수 있습니다.getattr 있습니다.

그러나 변수 이름으로 무엇이든해야하는 것은 일반적으로 잘못된 설계입니다. 왜 컬렉션에 보관하지 않습니까?

class Foo(object):
    def __init__(self, **values):
        self.special_values = values

그런 다음 키를 통해 반복 할 수 있습니다. for key in obj.special_values:


컬렉션을 사용하여 원하는 것을 달성하는 방법에 대해 좀 더 설명해 주시겠습니까?
Pablo Mescher

1
객체에 속성을 동적으로 추가하지 않습니다. 내가 가진 변수는 오랫동안 동일하게 유지되지만, 내가 원하는 것과 같은 것을 할 수 있으면 좋을 것이라고 생각합니다.
Pablo Mescher

1
class someclass:
        x=1
        y=2
        z=3
        def __init__(self):
           self.current_idx = 0
           self.items = ["x","y","z"]
        def next(self):
            if self.current_idx < len(self.items):
                self.current_idx += 1
                k = self.items[self.current_idx-1]
                return (k,getattr(self,k))
            else:
                raise StopIteration
        def __iter__(self):
           return self

그런 다음 iterable이라고 부르십시오.

s=someclass()
for k,v in s:
    print k,"=",v

너무 복잡해 보입니다. 내가 선택의 여지가 없다면, 지금 내가하고있는 일을 고수하고 싶습니다.
Pablo Mescher

0

이것에 대한 정답은하지 말아야한다는 것입니다. 이 유형의 것을 원한다면 dict를 사용하거나 컨테이너에 명시 적으로 속성을 추가해야합니다. 데코레이터에 대해 배우면이를 자동화 할 수 있습니다.

특히, 예제의 method1은 속성만큼 좋습니다.


2
그래, 내가해서는 안된다는 건 알지만 .. 어떻게 작동하는지 이해하는 데 도움이됩니다. 나는 PHP 배경에서 왔으며 매일 이런 종류의 일을하는 데 사용되었습니다
Pablo Mescher

당신은 저를 설득했습니다. to_dict () 메소드를 업데이트 된 상태로 유지하는 것이 지금까지의 대답보다 훨씬 간단합니다.
Pablo Mescher

1
교의 조언을 자제 할 사람이 남아 있습니까? 주의하지 않으면 동적 프로그래밍으로 인해 화상을 입을 수 있지만 기능은 사용할 언어로되어 있습니다.
Henrik Vendelbo 2016 년

0

파이썬 3.6

class SomeClass:

    def attr_list(self, should_print=False):

        items = self.__dict__.items()
        if should_print:
            [print(f"attribute: {k}    value: {v}") for k, v in items]

        return items

6
전문가가 아닌 비전문가도 귀하의 답변을 활용할 수 있도록 게시물에 설명을 추가 할 수 있습니까?
not2qubit

-3

모든 파이썬 열성들에게 Johan Cleeze가 당신의 독단주의를 승인 할 것이라고 확신합니다.). 나는이 답변을 계속 명예롭게합니다. 실제로 나를 더 자신감있게 만듭니다. 닭에게 의견을 남겨주세요!

파이썬 3.6

class SomeClass:

    def attr_list1(self, should_print=False):

        for k in self.__dict__.keys():
            v = self.__dict__.__getitem__(k)
            if should_print:
                print(f"attr: {k}    value: {v}")

    def attr_list(self, should_print=False):

        b = [(k, v) for k, v in self.__dict__.items()]
        if should_print:
            [print(f"attr: {a[0]}    value: {a[1]}") for a in b]
        return b

이 질문에 대해 두 가지 답변이있는 이유가 있습니까?
Nathan

@Nathan은 더 장황하고 다른 파이썬이 있기 때문에 있습니다. 또한 어느 쪽이 더 빨리 달렸는지 알지 못해서 독자가 선택하도록 결정했습니다.
고무 오리

@ nathan 파이썬이나 스택 오버플로 경찰입니까? 다른 코딩 스타일이 있습니다. 나는 많은 언어와 기술로 일하기 때문에 비 파이썬 적 인 것을 선호하며 그것이 독특하다고 생각합니다. 그럼에도 불구하고 Iist 이해력은 시원하고 간결합니다. 그렇다면 깨달은 독자 모두에게 여유가없는 이유는 무엇입니까?
고무 오리

모든 파이썬 열성들에게 Johan Cleeze가 당신의 독단주의를 승인 할 것이라고 확신합니다.). 나는이 답변을 계속 명예롭게합니다. 실제로 나를 더 자신감있게 만듭니다. 닭에게 의견을 남겨주세요!
고무 오리
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.