2012-06-20 2 views
13

나는 __getattr__과 고민 중이다. 복잡한 재귀 코드베이스가 있는데 예외를 전파하는 것이 중요합니다. 에서파이썬 : __getattr__이 AttributeErrors를 잡는 이유는 무엇입니까?

class A(object): 
    @property 
    def a(self): 
     raise AttributeError('lala') 

    def __getattr__(self, name):  
     print('attr: ', name) 
     return 1  

print(A().a) 

결과 :

('attr: ', 'a') 
1 

왜 이런 행동을? 예외가 발생하지 않는 이유는 무엇입니까? 이 동작은 설명되어 있지 않습니다 (__getattr__ documentation). getattr()A.__dict__을 사용할 수 있습니다. 이견있는 사람? 우리가 보는 바와 같이

+2

문서의 실패는'AttributeError'가'__get__'에서 제기 될 수 있다고 말하면서 (실제로 여기에서하는 것입니다.) 어떤 효과가 있는지 설명하지 않습니다. 'AttributeError'는 애트리뷰트가 발견되지 않은 것처럼 행동해야한다는 것을 파이썬에 알려 주어야합니다 (평소와 같이'__getattr__'가 백업으로 시도됩니다). - 당신이 원하는 행동이 아니라면 너는 다른 것을 모아야한다. – James

답변

9

난 그냥 참 속성이 먼저 시도되고,

class A(object): 
    @property 
    def a(self): 
     print "trying property..." 
     raise AttributeError('lala') 
    def __getattr__(self, name):  
     print('attr: ', name) 
     return 1  

print(A().a) 

에 코드를 변경합니다. 그러나 거기에 있지 않기 때문에 (AttributeError 제기), __getattr__()을 "최후의 수단"이라고합니다.

명확하게 문서화되어 있지는 않지만 "속성 조회가 일반적인 장소에서 속성을 찾지 못했을 때 호출됩니다"에서 계산 될 수 있습니다.

+0

대신이 작업을 수행하는 데 권장되는 방법은 무엇입니까? 속성을 사용하지 않거나'__getattr__' 대신'__getattribute__'을 사용합니까? –

+2

나는'AttributeError'를 발생시키지 않거나'__getattr__'에서 그 케이스를 다루지 않거나'__getattribute__'를 사용하고'AttributeError'를 잡아서'super (...) .__ getattribute__'라고 부를 것입니다. – glglgl

+1

속성에서 명시 적으로 'AttributeError'를 명시 적으로 발생시키려는 이유를 짐작할 수 없습니다. 아니면'__ (get | set) attr [ibute] __'메소드가 아닌 다른 곳에서. –

4

__getattr__은 AttributeError로 속성 액세스가 실패 할 때 호출됩니다. 아마도 이것이 당신이 오류를 '잡는'이유라고 생각할 수 있습니다. 그러나, Python의 속성 액세스 기능을 사용하여이를 catch 한 다음 __getattr__을 호출합니다.

그러나 __getattr__은 오류를 포착하지 않습니다. __getattr__에 AttributeError를 발생 시키면 무한 재귀가 발생합니다.

6

__getattribute__ documentation는 말한다 : 클래스는 __getattr__()을 정의

경우 __getattribute__() 명시 적으로 호출하거나 AttributeError을 제기하거나하지 않는 한, 후자는 호출되지 않습니다.

나는 (에 의해 inclusio unius 추정 exclusio alterius)이 읽어 속성 액세스 것이다 전화를 말 __getattr__AttributeError을 올리는 일 (" 액세스 속성을 구현하기 위해 무조건 소위"되는) object.__getattribute__ 경우 - 직접 또는 내부의 서술자 __get__ (예 : 속성 fget); __get__은 "(계산 된) 속성 값을 반환하거나 AttributeError 예외"을 발생시켜야합니다. 예를 들어, 연산자 특수 메서드는 NotImplementedError을 발생시켜 다른 연산자 메서드 (예 : __add__의 경우 __radd__)를 시도 할 수 있습니다.

6

__getattr__ 같은 클래스의 속성을 사용하면 디버그하기가 매우 어려운 오류가 발생할 수 있으므로 위험합니다.

속성의 getter가 AttributeError이면, AttributeError이 자동으로 catch되고 __getattr__이 호출됩니다. 보통, 이로 인해 __getattr__이 예외로 실패하지만, 당신이 극도로 불길한 경우에는 그렇지 않습니다. 따라서 문제를 쉽게 __getattr__으로 추적 할 수조차 없습니다.

귀하의 재산 취득자가 사소한 경우가 아니면, 결코 AttributeError을 포기하지 않을 100 % 확신 할 수 없습니다. 예외는 여러 수준으로 발생할 수 있습니다. 여기

당신이 할 수있는 것입니다 :

  1. 않도록 사용하여 속성과 같은 클래스에서 __getattr__.
  2. 는 단순한 프로퍼티 게터를 유지 사소한 아닌 모든 속성 게터에 try ... except 블록을 추가, 그래서 당신은 그들이 AttributeError
  3. AttributeError을 잡는 @property 장식의 자신의 버전을 쓰기 재 포기하지 알고 RuntimeError으로 표시합니다. 사람이 솔루션 4 (나는 권장하지 않는) 고려 경우,이 같은 수행 할 수 있습니다 :

    def property_(f): 
        def getter(*args, **kwargs): 
         try: 
          return f(*args, **kwargs) 
         except AttributeError as e: 
          raise RuntimeError, "Wrapped AttributeError: " + str(e), sys.exc_info()[2] 
    
        return property(getter) 
    

    그리고 대신 @property_를 사용

http://blog.devork.be/2011/06/using-getattr-and-property_17.html

편집 참조 __getattr__을 재정의하는 클래스의 @property

0

당신이 __getattr__@property을 결합 할 때 당신은 어쨌든 운명있어 :

class Paradise: 
    pass 

class Earth: 
    @property 
    def life(self): 
     print('Checking for paradise (just for fun)') 
     return Paradise.breasts 
    def __getattr__(self, item): 
     print("sorry! {} does not exist in Earth".format(item)) 

earth = Earth() 
try: 
    print('Life in earth: ' + str(earth.life)) 
except AttributeError as e: 
    print('Exception found!: ' + str(e)) 

다음과 같은 출력을 제공합니다 :

Checking for paradise (just for fun) 
sorry! life does not exist in Earth 
Life in earth: None 

을 당신의 진짜 문제는 Paradise.breasts를 호출하여 때.

__getattr__은 항상 AtributeError이 발생하면 호출됩니다. 예외 내용은 무시됩니다.

슬픈 것은이 True를 (단지 때문에 __getattr__이 정의) 반환 hasattr(earth, 'life') 주어이 문제에 대한 해결책 없지만 여전히 존재하지 않았다 같은 속성 '삶'에 의해 도달 실제 기본 반면 될 것입니다 문제는 Paradise.breasts입니다.

내 부분 해결 방법은 블록에서 try-except를 사용하여 AttributeError 예외가 발생하는 것으로 알려져 있습니다.

0

__getattr__을 많이 구현하고 @property 개의 메소드를 많이 가지고 있기 때문에이 문제가 발생합니다.

class C(object): 

    def __getattr__(self, name): 
     ... 

    @property 
    @replace_attribute_error_with_runtime_error 
    def complicated_property(self): 
     ... 

    ... 

기본 예외의 오류 메시지가 인스턴스 클래스의 이름을 포함합니다 :

def replace_attribute_error_with_runtime_error(f): 
    @functools.wraps(f) 
    def wrapped(*args, **kwargs): 
     try: 
      return f(*args, **kwargs) 
     except AttributeError as e: 
      # logging.exception(e) 
      raise RuntimeError(
       '{} failed with an AttributeError: {}'.format(f.__name__, e) 
      ) 
    return wrapped 

그리고 다음과 같이 사용 : 여기에 더 유용한 오류 메시지를 얻을 내가 생각 해낸 데코레이터이다 상승 기반의 AttributeError. 원한다면 로그인 할 수도 있습니다.

+0

나는 실제적으로 그것을 해결하기 위해 유사한 것을했고 그것을'safe_property'라고 불렀다. 그것은 하나의 장식자를 가지고 똑같은 일을합니다. –

관련 문제