Programing

무한 재귀 오류없이 __getattribute__을 어떻게 구현합니까?

lottogame 2020. 9. 18. 19:14
반응형

무한 재귀 오류없이 __getattribute__을 어떻게 구현합니까?


클래스의 한 변수에 대한 액세스를 재정의하고 싶지만 다른 모든 변수는 정상적으로 반환됩니다. 이 작업을 어떻게 수행 __getattribute__합니까?

나는 다음을 시도했지만 (내가하려는 것을 설명해야 함) 재귀 오류가 발생합니다.

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

self.__dict__내부 속성 에 액세스하려는 시도가 다시 __getattribute__호출 되기 때문에 재귀 오류가 발생 __getattribute__합니다. 대신 object's 를 사용 __getattribute__하면 작동합니다.

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return object.__getattribute__(self, name)

이것은 object(이 예제에서) 기본 클래스 이기 때문에 작동합니다 . 기본 버전을 호출하면 __getattribute__이전에 있었던 재귀 지옥을 피할 수 있습니다.

foo.py의 코드가 포함 된 Ipython 출력 :

In [1]: from foo import *

In [2]: d = D()

In [3]: d.test
Out[3]: 0.0

In [4]: d.test2
Out[4]: 21

최신 정보:

현재 문서에서 새로운 스타일의 클래스대한 추가 속성 액세스 라는 섹션 에는 무한 재귀를 피하기 위해 정확히이 작업을 수행하는 것이 좋습니다.


사실, __getattr__대신 특별한 방법 을 사용하고 싶다고 생각합니다 .

Python 문서에서 인용 :

__getattr__( self, name)

속성 조회가 일반적인 위치에서 속성을 찾지 못했을 때 호출됩니다 (즉, 인스턴스 속성이 아니고 self에 대한 클래스 트리에서도 찾을 수 없음). name은 속성 이름입니다. 이 메서드는 (계산 된) 속성 값을 반환하거나 AttributeError 예외를 발생시켜야합니다.
일반 메커니즘을 통해 속성을 찾은 경우는 __getattr__()호출되지 않습니다. (이 사이의 의도적 인 비대칭이다 __getattr__()__setattr__().) 그렇지 않으면 때문에 효율성을 이유로 모두를 수행하고 __setattr__()인스턴스의 액세스 다른 속성에 방법이 없습니다. 적어도 인스턴스 변수의 경우 인스턴스 속성 사전에 값을 삽입하지 않고 대신 다른 개체에 삽입하여 전체 제어를 위조 할 수 있습니다. 참조__getattribute__() 새로운 스타일의 클래스에서 실제로 완전한 제어를 얻는 방법은 아래의 방법입니다.

참고 :이 작업을 수행하려면, 인스턴스가해야 하지test라인이 있으므로, 속성을 self.test=20제거해야합니다.


Python 언어 참조 :

이 메서드에서 무한 재귀를 방지하기 위해 해당 구현은 항상 동일한 이름으로 기본 클래스 메서드를 호출하여 필요한 속성 (예 :)에 액세스해야합니다 object.__getattribute__(self, name).

의미:

def __getattribute__(self,name):
    ...
        return self.__dict__[name]

라는 속성을 호출하고 __dict__있습니다. 속성이기 때문에 어떤 호출 을 호출 하는지 __getattribute__검색에서 호출됩니다 ... yada yada yada__dict____getattribute__

return  object.__getattribute__(self, name)

기본 클래스를 사용하면 __getattribute__실제 속성을 찾는 데 도움이됩니다.


사용 __getattribute__하시겠습니까? 실제로 달성하려는 것은 무엇입니까?

요청한 작업을 수행하는 가장 쉬운 방법은 다음과 같습니다.

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    test = 0

또는:

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    @property
    def test(self):
        return 0

편집 :의 인스턴스는 각 경우에 D다른 값을 갖습니다 test. 첫 번째 경우에는 d.test20이되고 두 번째 경우에는 0이됩니다. 이유를 알아 내기 위해 여러분에게 맡기겠습니다.

Edit2 : Greg는 속성이 읽기 전용이고 __init__메서드 가 속성 을 20으로 설정하려고 했기 때문에 예제 2가 실패 할 것이라고 지적했습니다 . 이에 대한보다 완전한 예제는 다음과 같습니다.

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21

    _test = 0

    def get_test(self):
        return self._test

    def set_test(self, value):
        self._test = value

    test = property(get_test, set_test)

분명히 클래스로서 이것은 거의 전적으로 쓸모가 없지만 다음 단계로 넘어갈 아이디어를 제공합니다.


다음은 더 안정적인 버전입니다.

class D(object):
    def __init__(self):
        self.test = 20
        self.test2 = 21
    def __getattribute__(self, name):
        if name == 'test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

부모 클래스에서 __ getattribute __ 메서드를 호출 하고 결국 객체로 폴백 합니다. __ getattribute __ 메서드 (다른 조상이 재정의하지 않는 경우).


How is the __getattribute__ method used?

It is called before the normal dotted lookup. If it raises AttributeError, then we call __getattr__.

Use of this method is rather rare. There are only two definitions in the standard library:

$ grep -Erl  "def __getattribute__\(self" cpython/Lib | grep -v "/test/"
cpython/Lib/_threading_local.py
cpython/Lib/importlib/util.py

Best Practice

The proper way to programmatically control access to a single attribute is with property. Class D should be written as follows (with the setter and deleter optionally to replicate apparent intended behavior):

class D(object):
    def __init__(self):
        self.test2=21

    @property
    def test(self):
        return 0.

    @test.setter
    def test(self, value):
        '''dummy function to avoid AttributeError on setting property'''

    @test.deleter
    def test(self):
        '''dummy function to avoid AttributeError on deleting property'''

And usage:

>>> o = D()
>>> o.test
0.0
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0

A property is a data descriptor, thus it is the first thing looked for in the normal dotted lookup algorithm.

Options for __getattribute__

You several options if you absolutely need to implement lookup for every attribute via __getattribute__.

  • raise AttributeError, causing __getattr__ to be called (if implemented)
  • return something from it by
    • using super to call the parent (probably object's) implementation
    • calling __getattr__
    • implementing your own dotted lookup algorithm somehow

For example:

class NoisyAttributes(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self, name):
        print('getting: ' + name)
        try:
            return super(NoisyAttributes, self).__getattribute__(name)
        except AttributeError:
            print('oh no, AttributeError caught and reraising')
            raise
    def __getattr__(self, name):
        """Called if __getattribute__ raises AttributeError"""
        return 'close but no ' + name    


>>> n = NoisyAttributes()
>>> nfoo = n.foo
getting: foo
oh no, AttributeError caught and reraising
>>> nfoo
'close but no foo'
>>> n.test
getting: test
20

What you originally wanted.

And this example shows how you might do what you originally wanted:

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:
            return super(D, self).__getattribute__(name)

And will behave like this:

>>> o = D()
>>> o.test = 'foo'
>>> o.test
0.0
>>> del o.test
>>> o.test
0.0
>>> del o.test

Traceback (most recent call last):
  File "<pyshell#216>", line 1, in <module>
    del o.test
AttributeError: test

Code review

Your code with comments. You have a dotted lookup on self in __getattribute__. This is why you get a recursion error. You could check if name is "__dict__" and use super to workaround, but that doesn't cover __slots__. I'll leave that as an exercise to the reader.

class D(object):
    def __init__(self):
        self.test=20
        self.test2=21
    def __getattribute__(self,name):
        if name=='test':
            return 0.
        else:      #   v--- Dotted lookup on self in __getattribute__
            return self.__dict__[name]

>>> print D().test
0.0
>>> print D().test2
...
RuntimeError: maximum recursion depth exceeded in cmp

참고URL : https://stackoverflow.com/questions/371753/how-do-i-implement-getattribute-without-an-infinite-recursion-error

반응형