2011-06-21 7 views
2

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

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 «s само проходит, а объект, который вызывает f), но я не знаю, как реши это.

Любые идеи?


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

ответ

3

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

+0

Не могли бы вы привести несколько примеров? Я не получил этого, я что-то упустил. (В C++ нет ничего подобного) –

+0

Дескриптор - это просто класс, который Python передает дополнительную информацию об этом классе и экземпляре, к которому он привязан при вызове дескриптора ' __get __() 'или' __set __() '. –

+0

Хм, я вижу. Я думаю, что это сработает для меня. Но тогда, как вызвать функцию с аргументами. '__get__' принимает 3 аргумента -' self, obj, objtype'. В '__init__' я могу сохранить функцию как член, но как насчет аргументов? Кроме того, в '__get__' я не могу вернуть' self.func (obj) '? ('self.func' - это сохраненная cunstion, а obj - вызывающий объект, когда я возвращаю' self.func', это действительно возвращает функцию, но если return 'self.func (obj)' 'None':? I нужно, по-видимому, что первый параметр (self) должен быть явно передан? В противном случае я получаю ошибку за меньшие параметры) Большое спасибо! –

6

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

>>> 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 
>>> 
+0

Закрытие не является единственным ингредиентом. Вы получаете только 'self', потому что класс признает' wrapper' как метод и генерирует для него дескриптор метода экземпляра. – delnan

+1

Ницца! +1 на данный момент. не могли бы вы объяснить, как это работает. Я имею в виду - оболочку. Я видел такие обертки, но все они возвращают функцию справки, в данном случае - '_wrapped_meth'. Здесь функция возвращает свой аргумент - 'meth'.Когда и как '_wrapped_meth' вызывается/используется. –

+0

@ delnan - правда? Я могу сделать это с помощью класса? Как? Потому что мне нужно сохранить некоторую дополнительную информацию - некоторые другие члены (метаданные) и т. Д. Внутри обертки. –

1

Вы можете сделать свой wrapper класс дескриптор без данных, как описано в Functions and Methods секции Raymond Hettinger отличными How-To Guide for Descriptors - что в данном случае довольно легко, так как ему просто означает предоставление классу метода __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 

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

+0

+1 - Это точно, что я сделал, объединив другие два ответа (= –

Смежные вопросы