Как украсить метод экземпляра другим классом в Python 2.7?

В Python 2.7 я хотел бы украсить метод экземпляра test в классе Foo с помощью декоратора, который также является классом с именем FooTestDecorator. Из вопроса пользователя Chirstop и документа Python 2 Руководство по использованию дескриптора Я создал этот пример.

Однако, похоже, есть проблема: когда я печатаю свой декорированный объект метода, его (проверенное?) имя неверно, потому что оно отмечено как вопросительный знак, например Foo.?.

import types
class FooTestDecorator(object):
    def __init__(self,func):
        self.func=func
        self.count=0
        # tried self.func_name = func.func_name, but seemed to have no effect

    def __get__(self,obj,objtype=None):
        return types.MethodType(self,obj,objtype)
    def __call__(self,*args,**kwargs):
        self.count+=1
        return self.func(*args,**kwargs)

class Foo:
    @FooTestDecorator
    def test(self,a):
        print a
    def bar(self,b):
        print b

если вы проверите это:

f=Foo()
print Foo.__dict__['test']
print Foo.test
print f.test
print Foo.__dict__['bar']
print Foo.bar
print f.bar

ты получаешь

<__main__.FooTestDecorator ...object...>
<unbound method Foo.?>
<bound method Foo.? of ...instance...>
<function bar at 0x...>
<unbound method Foo.bar>
<bound method Foo.bar of ...instance...>

Вы можете видеть, что метод замены показан как Foo.?. Это кажется неправильным.

Как мне правильно получить метод экземпляра, декорированный классом?

примечание: моя причина в том, что я хочу использовать переменные из экземпляра FooDecorator self, которые я бы установил при инициализации. Я не включил это в пример, чтобы упростить его.


person n611x007    schedule 16.04.2014    source источник


Ответы (1)


У вашего экземпляра декоратора нет атрибута __name__, поэтому вместо этого Python имеет дело со знаком вопроса.

Используйте functools.update_wrapper(), чтобы скопировать имя функции, а также несколько других интересные специальные атрибуты (такие как строка документации, имя функционального модуля и любые пользовательские атрибуты, которые могут быть у функции):

import types
from functools import update_wrapper

class FooTestDecorator(object):
    def __init__(self,func):
        self.func=func
        self.count=0
        update_wrapper(self, func)

    def __get__(self,obj,objtype=None):
        return types.MethodType(self,obj,objtype)
    def __call__(self,*args,**kwargs):
        self.count+=1
        return self.func(*args,**kwargs)

Демо:

>>> f=Foo()
>>> print Foo.__dict__['test']
<__main__.FooTestDecorator object at 0x11077e210>
>>> print Foo.test
<unbound method Foo.test>
>>> print f.test
<bound method Foo.test of <__main__.Foo instance at 0x11077a830>>
person Martijn Pieters    schedule 16.04.2014
comment
Вы печатаете слишком быстро. :( - person kojiro; 16.04.2014
comment
@kojiro На самом деле, я считаю, что он заранее ответил на все возможные вопросы по SO, и единственная причина, по которой он не отвечает мгновенно, заключается в том, что поиск ответов для копирования и вставки занимает некоторое время. - person SethMMorton; 17.04.2014
comment
@SethMMorton правда? Я думал, что он тоже автоматизировал поиск, и единственная причина, по которой он занимает так много времени, — глобальная блокировка интерпретатора. - person kojiro; 17.04.2014