Автопараметрия и метапрограммирование

Ну, код говорит больше (я жестко закодировал некоторые вещи, чтобы изолировать проблему и сделать вопрос короче):

class wrapper:
    def __init__( self, func ):
        self.func = func
    def __call__( self, *args ):
        print( "okay, arg = ", args[0] )
        self.func( self, args )

class M( type ):
    def __new__( klass, name, bases, _dict ):
        _dict[ "f" ] = wrapper( _dict[ "f" ] ) 
        return type.__new__( klass, name, bases, _dict )

class AM( metaclass = M ):
    def __init__( self ):
        self.a = 0 
    def f( self, a ):
        self.a = a 

am = AM()
print( am.a ) # prints 0, expected
am.f( 1 )  # prints: "okay, arg = 1"
print( am.a ) # prints 0 again, also expected

Я хочу показать второй отпечаток 1, вместо 0, Другими словами, возможно ли, и если да, то каким образом передать "настоящее я" моей обертке?

Примечание: я знаю, почему это печатает 0 и я знаю в чем тут проблема (wrapperпередается сам, а не объект, который вызывает f), но я не знаю, как это решить.

Есть идеи?


РЕДАКТИРОВАТЬ - спасибо всем за ответы, +1 от меня. Но я думаю, что мне нужно сделать это с классом, так как мне нужно хранить некоторую дополнительную информацию (например, метаданные) (это упрощенная версия моей настоящей проблемы). Возможно ли это и как, если да? Извините, что не указали это в самом начале.

3 ответа

Решение

Делать wrapper дескриптор, чтобы вы знали конкретный экземпляр тыкают.

Используйте функцию-оболочку вместо одного класса. Закрытие позаботится об остальном:

>>> def wrapper(meth):
...     def _wrapped_meth(self, *args):
...             print('okay, arg = ', args)
...             meth(self, *args)
...     return _wrapped_meth
...
>>> class M(type):
...     def __new__(klass, name, bases, dct):
...             dct['f'] = wrapper(dct['f'])
...             return type.__new__(klass, name, bases, dct)
...
>>> class AM(metaclass=M):
...     def __init__(self):
...             self.a = 0
...     def f(self, a):
...             self.a = a
...
>>> am = AM()
>>> print(am.a)
0
>>> am.f(1)
okay, arg = (1,)
>>> print(am.a)
1
>>>

Вы можете сделать свой wrapper классифицировать дескриптор, не связанный с данными, как описано в разделе " Функции и методы " превосходного практического руководства по дескрипторам Рэймонда Хеттингера, что в данном случае довольно просто, так как это просто означает __get__() метод, который создает и возвращает требуемый упакованный метод.

Вот что я имею в виду:

class wrapper:
    def __init__( self, func ):
        self.func = func

    def __get__( self, instance, owner ):
        def wrapped( *args ):
            print( "okay, arg = ", args[0] )
            return self.func( instance, *args )
        return wrapped

Использование класса означает, что вы можете легко добавлять других членов и / или метаданные по мере необходимости.

Другие вопросы по тегам