__getattr__과 __getattribute__의 차이점


418

__getattr__또는 사용시기를 이해하려고합니다 __getattribute__. 언급 된 문서__getattribute__새로운 스타일의 클래스에 적용됩니다. 새로운 스타일의 수업은 무엇입니까?



5
@Trilarion 아직 그 질문은 여기에서 대답을 언급합니다 ...
JC Rocamonde

답변:


497

사이의 키 차이 __getattr__와는 __getattribute____getattr__속성이 일반적인 방법을 찾을 수 없습니다 경우에만 호출됩니다. 누락 된 속성에 대한 폴백을 구현하는 데 유용하며 원하는 두 가지 중 하나 일 수 있습니다.

__getattribute__객체의 실제 속성을보기 전에 호출되므로 올바르게 구현하기가 까다로울 수 있습니다. 무한 재귀로 매우 쉽게 끝날 수 있습니다.

새로운 스타일 클래스는 object, 구식 클래스에서 파생되며 명시 적 기본 클래스가없는 Python 2.x의 클래스입니다. 그러나 이전 스타일과 새로운 스타일의 클래스의 차이는 사이의 선택 중요한 것이 아니다 __getattr____getattribute__.

당신은 거의 확실하게 원합니다 __getattr__.


6
둘 다 같은 클래스에서 구현할 수 있습니까? 가능하다면 둘 다 구현하기 위해 무엇입니까?
Alcott

13
@Alcott : 둘 다 구현할 수 있지만 왜 그런지 잘 모르겠습니다. __getattribute__액세스 할 때마다 __getattr__호출 __getattribute__되며을 발생시킨 시간 동안 호출됩니다 AttributeError. 왜 모두 하나로 유지하지 않습니까?
Ned Batchelder

10
@NedBatchelder, 기존 메소드에 대한 호출을 (조건부) 재정의하려면을 사용하려고합니다 __getattribute__.
Jace Browning

28
"이 메소드에서 무한 재귀를 피하려면 구현시 항상 동일한 이름으로 기본 클래스 메소드를 호출하여 필요한 속성 (예 : object .__ getattribute __ (self, name))에 액세스해야합니다."
kmonsoor

1
@kmonsoor. 이것이 두 가지를 모두 구현하는 좋은 이유입니다. 올바른 상황에서 objec.__getattribute__호출합니다 myclass.__getattr__.
Mad Physicist

138

마술 방법 __getattr____getattribute__마술 방법 의 간단한 예를 살펴 보겠습니다 .

__getattr__

파이썬은 __getattr__아직 정의되지 않은 속성을 요청할 때마다 메소드 를 호출 합니다. 다음 예제에서 내 클래스 Count 에는 __getattr__메소드 가 없습니다 . 이제 주에서 나는 액세스하려고 모두 때 obj1.myminobj1.mymax모든 것이 잘 작동 속성. 그러나 obj1.mycurrent속성 에 액세스하려고하면 Python이AttributeError: 'Count' object has no attribute 'mycurrent'

class Count():
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent)  --> AttributeError: 'Count' object has no attribute 'mycurrent'

이제 내 클래스 Count 에는 __getattr__메소드가 있습니다. 이제 obj1.mycurrent속성 에 액세스하려고하면 파이썬은 내 __getattr__메소드 에서 구현 한 모든 것을 반환합니다 . 내 예제에서 존재하지 않는 속성을 호출하려고 할 때마다 파이썬은 해당 속성을 만들고 정수 값 0으로 설정합니다.

class Count:
    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax    

    def __getattr__(self, item):
        self.__dict__[item]=0
        return 0

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.mycurrent1)

__getattribute__

이제 __getattribute__방법을 봅시다. __getattribute__클래스에 메소드가있는 경우 python은 존재 여부에 관계없이 모든 속성에 대해이 메소드를 호출합니다. 왜 우리는 __getattribute__방법이 필요 합니까? 한 가지 좋은 이유는 다음 예제와 같이 속성에 대한 액세스를 방지하고보다 안전하게 만들 수 있기 때문입니다.

누군가가 하위 문자열 'cur'로 시작하는 내 속성에 액세스하려고 할 때마다 AttributeError예외 가 발생합니다. 그렇지 않으면 해당 속성을 반환합니다.

class Count:

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item) 
        # or you can use ---return super().__getattribute__(item)

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

중요 : __getattribute__메소드 에서 무한 재귀를 피하려면 구현시 항상 동일한 이름을 가진 기본 클래스 메소드를 호출하여 필요한 속성에 액세스해야합니다. : 예를 들어, object.__getattribute__(self, name)또는 super().__getattribute__(item)하지self.__dict__[item]

중대한

클래스에 getattrgetattribute 매직 메소드 가 모두 포함 된 경우 __getattribute__먼저 호출됩니다. 그러나 예외 __getattribute__AttributeError발생하면 예외가 무시되고 __getattr__메소드가 호출됩니다. 다음 예를 참조하십시오.

class Count(object):

    def __init__(self,mymin,mymax):
        self.mymin=mymin
        self.mymax=mymax
        self.current=None

    def __getattr__(self, item):
            self.__dict__[item]=0
            return 0

    def __getattribute__(self, item):
        if item.startswith('cur'):
            raise AttributeError
        return object.__getattribute__(self,item)
        # or you can use ---return super().__getattribute__(item)
        # note this class subclass object

obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)

1
재정의를위한 유스 케이스가 무엇인지 __getattribute__확실하지 않지만 확실하지 않습니다. 귀하의 예에 따라, 당신이하고있는 모든 때문에 __getattribute__인상이다 AttributeError속성이에없는 경우 예외를 __dict__개체의; 이 디폴트 구현이기 때문에하지만 당신은 정말이 필요하지 않습니다 __getattribute__및 가리키고은 __getattr__정확히 대체 메커니즘으로해야 할 것입니다.
Rohit

@Rohit는 current인스턴스에 정의된다 Count(참조 __init__), 그래서 단순히 인상 AttributeError이로 연기 - 속성이 꽤 무슨 일이야하지가되지 않는 경우 __getattr__에 대한 모든 포함하여, 이름을 '똥개'를 시작 current뿐만 아니라 curious, curly...
joelb

16

이것은 Ned Batchelder의 설명을 기반으로 한 예일뿐입니다 .

__getattr__ 예:

class Foo(object):
    def __getattr__(self, attr):
        print "looking up", attr
        value = 42
        self.__dict__[attr] = value
        return value

f = Foo()
print f.x 
#output >>> looking up x 42

f.x = 3
print f.x 
#output >>> 3

print ('__getattr__ sets a default value if undefeined OR __getattr__ to define how to handle attributes that are not found')

그리고 같은 예제를 사용 __getattribute__하면 >>>RuntimeError: maximum recursion depth exceeded while calling a Python object


9
실제로 이것은 끔찍합니다. 실제 __getattr__()구현에서는 유효 AttributeError하지 않은 속성 이름 을 제기하여 유한 한 유효한 속성 이름 세트 만 허용 하므로 미묘하고 디버그하기 어려운 문제 를 피할 수 있습니다 . 이 예제는 모든 속성 이름을 무조건 허용 합니다 – 기괴한 (그리고 솔직하게 오류가 발생하기 쉬운) 오용 __getattr__(). 이 예제에서와 같이 속성 작성에 대한 "전체 제어"를 원한다면 __getattribute__()대신에 원하는 것입니다.
Cecil Curry 1

3
@CecilCurry : 당신이 연결 한 모든 문제는 값이 아니라 암시 적으로 None을 반환하는 것과 관련이 있습니다. 모든 속성 이름을 수락하는 데 어떤 문제가 있습니까? 와 동일합니다 defaultdict.
Nick Matteo

2
문제는 수퍼 클래스 조회 전에__getattr__ 호출 된다는 것 입니다. 직접 서브 클래스의 경우 괜찮습니다. 어쨌든 인스턴스를 무시하는 매직 메소드가 있기 때문에 더 복잡한 상속 구조의 경우 부모에서 아무것도 상속하는 기능을 완전히 제거합니다. object
미친 물리학 자

1
@Simon K Bhatta4ya, 마지막 인쇄 진술은 의견입니다. 권리? 길고 읽기가 지루합니다 (오른쪽에서 많이 스크롤해야 함). 코드 섹션 뒤에 줄을 넣는 것은 어떻습니까? 또는 코드 섹션에 넣으려면 두 줄로 나누는 것이 좋습니다.
Md. Abu Nafee Ibna Zahid

14

새 스타일 클래스는 object또는 다른 새 스타일 클래스에서 상속합니다 .

class SomeObject(object):
    pass

class SubObject(SomeObject):
    pass

구식 수업은하지 않습니다 :

class SomeObject:
    pass

이것은 파이썬 2에만 적용됩니다-파이썬 3에서는 위의 모든 것이 새로운 스타일의 클래스를 만듭니다.

참조 9. 클래스 (파이썬 자습서를) NewClassVsClassicClass파이썬 오래 된 스타일과 새로운 스타일 클래스의 차이점은 무엇입니까? 자세한 내용은.


6

새로운 스타일 클래스는 "직접"또는 "직접"서브 클래스 인 클래스입니다. 그들은 __new__수업 방법 __init__이 있으며 다소 합리적인 수준의 저수준 행동을합니다.

일반적으로 재정의하고 싶을 __getattr__경우 (중재하는 경우) 메서드 내에서 "self.foo"구문을 지원하는 데 어려움이 있습니다.

추가 정보 : http://www.devx.com/opensource/Article/31482/0/page/4


2

Beazley & Jones PCB를 통해 읽을 __getattr__때 OP 질문의 "언제"부분에 대한 답변 을 제공하는 명시적이고 실용적인 사용 사례를 발견했습니다 . 책에서 :

"이 __getattr__()방법은 일종의 속성 조회와 비슷합니다. 코드가 존재하지 않는 속성에 액세스하려고하면 호출되는 방법입니다." 우리는 위의 답변에서 이것을 알고 있지만 PCB 레시피 8.15 에서이 기능은 위임 디자인 패턴 을 구현하는 데 사용됩니다 . 오브젝트 A에 오브젝트 B의 메소드를 호출하기 위해 오브젝트 A의 모든 오브젝트 B 메소드를 재정의하는 대신 오브젝트 A가 위임하려는 많은 메소드를 구현하는 오브젝트 B 속성이있는 경우 __getattr__()다음과 같이 메소드를 정의하십시오 .

def __getattr__(self, name):
    return getattr(self._b, name)

여기서 _b는 오브젝트 B 인 오브젝트 A의 속성 이름입니다. 오브젝트 B에 정의 된 __getattr__메소드가 오브젝트 A에서 호출 되면 메소드는 검색 체인의 끝에서 호출됩니다. 다른 객체에 위임하기 위해 정의 된 메소드 목록이 없기 때문에 코드도 깔끔해집니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.