为什么我的想法不能在python2中工作?

为什么我的想法不能在python2中工作?

问题描述:

这是一个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

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__属性;这大多只用于反思。