무한 재귀 오류없이 __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
제거해야합니다.
이 메서드에서 무한 재귀를 방지하기 위해 해당 구현은 항상 동일한 이름으로 기본 클래스 메서드를 호출하여 필요한 속성 (예 :)에 액세스해야합니다
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.test
20이되고 두 번째 경우에는 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 (probablyobject
's) implementation - calling
__getattr__
- implementing your own dotted lookup algorithm somehow
- using
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
'Programing' 카테고리의 다른 글
시뮬레이터는 이미 사용 중이기 때문에 시작할 수 없습니다. (0) | 2020.09.18 |
---|---|
es6 모듈 구현, json 파일로드 방법 (0) | 2020.09.18 |
파마 공간이란? (0) | 2020.09.18 |
(n & -n) == n이면 n이 2의 거듭 제곱 인 이유는 무엇입니까? (0) | 2020.09.18 |
bash에서 기능 키를 명령에 어떻게 바인딩합니까? (0) | 2020.09.18 |