2017-02-16 34 views
3

這裏是我的簡單類(錯誤):實施了自己的__getattr__;得到了意外的錯誤

$ ipython 

Python 2.7.6 (default, Oct 26 2016, 20:30:19) 
Type "copyright", "credits" or "license" for more information. 

IPython 5.1.0 -- An enhanced Interactive Python. 
?   -> Introduction and overview of IPython's features. 
%quickref -> Quick reference. 
help  -> Python's own help system. 
object? -> Details about 'object', use 'object??' for extra details. 


In [1]: import numpy as np 

In [2]: class Foo(object): 
    ...:  def __init__(self): 
    ...:   self.one = 1 
    ...:   self.dct = dict(a='aaa', b='bbb') 
    ...: 
    ...:  @property 
    ...:  def two(self): 
    ...:   # import ipdb; ipdb.set_trace() 
    ...:   np.ann # This was the spelling error I had 
    ...:   return 2 
    ...: 
    ...:  def __getattr__(self, key): 
    ...:   """ 
    ...:   Provide convenient access to values that are 
    ...:   somewhat inconvient to access, eg 
    ...:   
    ...:    >>> foo = Foo() 
    ...:    >>> foo.dct['a'] # this obviously works 
    ...:    'aaa' 
    ...:    >>> foo.a # but this is easier 
    ...:    'aaa' 
    ...:   
    ...:   In reality I have something a bit more complicated than 
    ...:   a simple dictionary (`self.dct = dict(...)`) 
    ...:   
    ...:   """ 
    ...:   print('__getattr__ with "{}"'.format(key)) 
    ...:   try: 
    ...:    return self.dct[key] 
    ...:   except KeyError: 
    ...:    raise AttributeError("Can't find '{}'".format(key)) 
    ...: 

使用它......

In [3]: foo = Foo() 

In [4]: foo.one 
Out[4]: 1 

In [5]: foo.dct['a'] 
Out[5]: 'aaa' 

In [6]: foo.a 
__getattr__ with "a" 
Out[6]: 'aaa' 

In [7]: foo.two 
__getattr__ with "two" 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-9-29e77587894c> in <module>() 
----> 1 foo.two 

<ipython-input-4-955e08b9c001> in __getattr__(self, key) 
    12    return self.dct[key] 
    13   except KeyError: 
---> 14    raise AttributeError("Can't find '{}'".format(key)) 
    15 

AttributeError: Can't find 'two' 

爲什麼拼寫錯誤np.ann調用foo.__getattr__和我自己的raise AttributeError與結束了key == 'two' ??

我已經通過了代碼(通過取消註釋set_trace()行),但沒有真正理解它。這裏的輸出是有用的。

In [15]: foo.two 
> <ipython-input-13-915f93b88a22>(8)two() 
     7   import ipdb; ipdb.set_trace() 
----> 8   np.ann 
     9   return 2 

ipdb> n 
AttributeError: "'module' object has no attribute 'ann'" 
> <ipython-input-13-915f93b88a22>(8)two() 
     7   import ipdb; ipdb.set_trace() 
----> 8   np.ann 
     9   return 2 

ipdb> n 
--Return-- 
None 
> <ipython-input-13-915f93b88a22>(8)two() 
     7   import ipdb; ipdb.set_trace() 
----> 8   np.ann 
     9   return 2 

ipdb> n 
--Call-- 
> <ipython-input-13-915f93b88a22>(10)__getattr__() 
     9   return 2 
---> 10  def __getattr__(self, key): 
    11   print('__getattr__ with "{}"'.format(key)) 

ipdb> n 
> <ipython-input-13-915f93b88a22>(11)__getattr__() 
    10  def __getattr__(self, key): 
---> 11   print('__getattr__ with "{}"'.format(key)) 
    12   try: 

ipdb> n 
__getattr__ with "two" 
> <ipython-input-13-915f93b88a22>(12)__getattr__() 
    11   print('__getattr__ with "{}"'.format(key)) 
---> 12   try: 
    13    return self.dct[key] 

ipdb> n 
> <ipython-input-13-915f93b88a22>(13)__getattr__() 
    12   try: 
---> 13    return self.dct[key] 
    14   except KeyError: 

ipdb> n 
KeyError: ('two',) 
> <ipython-input-13-915f93b88a22>(13)__getattr__() 
    12   try: 
---> 13    return self.dct[key] 
    14   except KeyError: 

ipdb> n 
> <ipython-input-13-915f93b88a22>(14)__getattr__() 
    13    return self.dct[key] 
---> 14   except KeyError: 
    15    raise AttributeError("Can't find '{}'".format(key)) 

ipdb> n 
> <ipython-input-13-915f93b88a22>(15)__getattr__() 
    14   except KeyError: 
---> 15    raise AttributeError("Can't find '{}'".format(key)) 
    16 

ipdb> n 
AttributeError: Attribut... 'two'",) 
> <ipython-input-13-915f93b88a22>(15)__getattr__() 
    14   except KeyError: 
---> 15    raise AttributeError("Can't find '{}'".format(key)) 
    16 

ipdb> n 
--Return-- 
None 
> <ipython-input-13-915f93b88a22>(15)__getattr__() 
    14   except KeyError: 
---> 15    raise AttributeError("Can't find '{}'".format(key)) 
    16 

ipdb> n 
AttributeError: Attribut... 'two'",) 
> <ipython-input-15-29e77587894c>(1)<module>() 
----> 1 foo.two 

ipdb> n 
--Return-- 
None 
> <ipython-input-15-29e77587894c>(1)<module>() 
----> 1 foo.two 

ipdb> n 
AttributeError: Attribut... 'two'",) 
> /home/venv/local/lib/python2.7/site-packages/IPython/core/interactiveshell.py(2881)run_code() 
    2880     #rprint('Running code', repr(code_obj)) # dbg 
-> 2881     exec(code_obj, self.user_global_ns, self.user_ns) 
    2882    finally: 

如果我註釋掉@property線,那麼錯誤的行爲與預期,這使我相信,這事做與裝飾...:

In [19]: foo.two 
Out[19]: <bound method Foo.two of <__main__.Foo object at 0x7fc248156450>> 

In [20]: foo.two() 
--------------------------------------------------------------------------- 
AttributeError       Traceback (most recent call last) 
<ipython-input-20-25d037a9762c> in <module>() 
----> 1 foo.two() 

<ipython-input-16-9999eb93c349> in two(self) 
     6  def two(self): 
     7   # import ipdb; ipdb.set_trace() 
----> 8   np.ann 
     9   return 2 
    10  def __getattr__(self, key): 

AttributeError: 'module' object has no attribute 'ann' 

回答

1
  • (正常運行得),只要正常屬性查找引發AttributeError__getattr__被調用。

  • property更改屬性查找,以便在訪問屬性時執行該功能。

在你的情況,你要訪問的two -attribute,這反過來又調用時,它試圖查詢np.ann這引發AttributeError方法Foo.two。這個AttributeError被捕獲,並且您的__getattr__被稱爲key='two'。但由於'two'不在您的字典中,因此您在那裏提升AttributeError

1

__getattr__被稱爲如果通過__getattribute__的屬性查找產生AttributeError。這不知道或在意爲什麼AttributeError發生,所以AttributeErrornp.ann導致退回到__getattr__

0

在Python對象模型中,__getattr__是在屬性查找在通常位置未找到該屬性時調用的。由於您的錯誤np.arr實際上引發了AttributeError,Python感到困惑,並認爲foo.two屬性根本不存在。因此調用__getattr__

如果更改線路

np.arr 

這不是

errorerrorerror 

這將提高NameError而不是AttributeError,你不會看到混亂的行爲了。

順便說一句,你可能不應該打擾自己實施類似AttrDict類,因爲很多食譜已經存在,包括one in the standard libraries

相關問題