Автопараметрия и метапрограммирование - PullRequest
2 голосов
/ 21 июня 2011

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

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 ]

6 голосов
/ 21 июня 2011

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

>>> 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
>>>
3 голосов
/ 21 июня 2011

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

1 голос
/ 22 июня 2011

Вы можете сделать свой класс 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

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

...