2014-12-02 59 views
7

這是一個dict子類的一個想法,它可以改變密鑰。這是一個簡單的自包含示例,就像dict,但對str鍵不區分大小寫。爲什麼我的想法不能在python2中工作?

from functools import wraps 

def key_fix_decorator(f): 
    @wraps(f) 
    def wrapped(self, *args, **kwargs): 
     if args and isinstance(args[0], str): 
      args = (args[0].lower(),) + args[1:] 
     return f(self, *args, **kwargs) 
    return wrapped 

class LowerDict(dict): 
    pass 

for method_name in '__setitem__', '__getitem__', '__delitem__', '__contains__', 'get', 'pop', 'setdefault': 
    new_method = key_fix_decorator(getattr(LowerDict, method_name)) 
    setattr(LowerDict, method_name, new_method) 

開發注:,如果你複製我的代碼爲自己的用途,你應該實現LowerDict.__init__來檢查任何鍵衝突 - 我沒有打擾到包括對這個問題的目的

在python3這一切似乎正常工作:

>>> d = LowerDict(potato=123, spam='eggs') 
>>> d['poTATo'] 
123 
>>> d.pop('SPAm') 
'eggs' 
>>> d['A'] 
# KeyError: 'a' 

在python2,它甚至不進口,這裏是回溯:

File "/tmp/thing.py", line 15, in <module> 
    new_method = key_fix_decorator(getattr(LowerDict, method_name)) 
    File "/tmp/thing.py", line 4, in key_fix_decorator 
    @wraps(f) 
    File "/usr/lib/python2.7/functools.py", line 33, in update_wrapper 
    setattr(wrapper, attr, getattr(wrapped, attr)) 
AttributeError: 'wrapper_descriptor' object has no attribute '__module__' 

可能是什麼問題?除了str/basestring之外,我看不到任何特定於版本的代碼,這只是一個小細節而不是破解代碼的問題。

+1

不同之處在於'update_wrapper'在兩個版本中的實現方式.https://hg.python.org/cpython/file/2.7/Lib/functools.py#l17和https://hg.python.org/ cpython/file/3.4/Lib/functools.py#l43 – 2014-12-02 22:26:13

+2

這可能與以下相關:http://bugs.python.org/issue3445 – 2014-12-02 22:28:43

+0

您可能也對PEP 455感興趣:https://www.python.org/開發/ PEPS/PEP-0455 / – 2014-12-02 23:40:09

回答

4

Python 3中的functools.wraps()版本可以處理函數對象,其中一些屬性被複制丟失; Python 2中的那個不能。這是因爲issue #3445僅適用於Python 3; dict的方法在C代碼中定義,並且沒有__module__屬性。

省略@wraps(f)裝飾使一切工作在Python 2太:

>>> def key_fix_decorator(f): 
...  def wrapped(self, *args, **kwargs): 
...   if args and isinstance(args[0], str): 
...    args = (args[0].lower(),) + args[1:] 
...   return f(self, *args, **kwargs) 
...  return wrapped 
... 
>>> class LowerDict(dict): 
...  pass 
... 
>>> for method_name in '__setitem__', '__getitem__', '__delitem__', '__contains__', 'get', 'pop', 'setdefault': 
...  new_method = key_fix_decorator(getattr(LowerDict, method_name)) 
...  setattr(LowerDict, method_name, new_method) 
... 
>>> d = LowerDict(potato=123, spam='eggs') 
>>> d['poTATo'] 
123 
>>> d.pop('SPAm') 
'eggs' 
>>> d['A'] 
Traceback (most recent call last): 
    File "<stdin>", line 1, in <module> 
    File "<stdin>", line 5, in wrapped 
KeyError: 'a' 

可以複製的東西wraps手工做足夠

def key_fix_decorator(f): 
    def wrapped(self, *args, **kwargs): 
     if args and isinstance(args[0], str): 
      args = (args[0].lower(),) + args[1:] 
     return f(self, *args, **kwargs) 
    wrapped.__name__ = f.__name__ 
    wrapped.__doc__ = f.__doc__ 
    return wrapped 

或限制的屬性,這些屬性wraps嘗試複製全部:

def key_fix_decorator(f): 
    @wraps(f, assigned=('__name__', '__doc__')) 
    def wrapped(self, *args, **kwargs): 
     if args and isinstance(args[0], str): 
      args = (args[0].lower(),) + args[1:] 
     return f(self, *args, **kwargs) 
    return wrapped 

您並不需要在此處更新__module__屬性;這大多隻用於反思。

相關問題