2014-12-02 19 views
7

Oto pomysł dla podklasy dict, która może mutować klucze. Jest to prosty samodzielny przykład, który jest podobny do dict, ale jest niewrażliwy na wielkość liter dla kluczy str.Dlaczego mój pomysł nie działa w 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) 

dev note: jeśli kopia mojego kodu do własnych zastosowań, należy wdrożyć LowerDict.__init__ celu wykrycia kolizji kluczowych - Nie przejmowałem się to, że dla celów tego pytania

na python3 to wszystko wydaje się działa dobrze:

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

W python2 to nawet nie import, tutaj jest traceback:

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__' 

Jaki może być problem? Nie widzę żadnego kodu specyficznego dla wersji z wyjątkiem przypadku str/basestring, który jest tylko drobnym szczegółem, a nie problemem z łamaniem kodów.

+1

Różnica polega na tym, w jaki sposób implementowany jest "update_wrapper" w obu wersjach.https: //hg.python.org/cpython/file/2.7/Lib/functools.py#l17 i https://hg.python.org/ cpython/file/3.4/Lib/functools.py # l43 –

+2

Jest to prawdopodobnie powiązane: http://bugs.python.org/issue3445 –

+0

Możesz być także zainteresowany PEP 455: https://www.python.org/ dev/peps/pep-0455 / –

Odpowiedz

4

Wersja functools.wraps() w Pythonie 3 może obsługiwać obiekty funkcyjne z niektórymi atrybutami, które kopiuje w całym brakującym; ten w Pythonie 2 nie może. Było tak, ponieważ issue #3445 został naprawiony tylko dla Pythona 3; metody dict są zdefiniowane w kodzie C i nie mają atrybutu __module__.

Pominięcie @wraps(f) dekorator sprawia, że ​​wszystko działa w Pythonie 2 zbyt:

>>> 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' 

można replikować tyle co wraps robi ręcznie:

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 

lub ograniczyć atrybuty wraps próbuje kopia:

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 

Nie musisz aktualizować tutaj atrybutu __module__; jest to głównie przydatne tylko w introspekcji.

Powiązane problemy