декоратор внутри класса и декорированный метод класса без «я» дает странные результаты - PullRequest
1 голос
/ 28 сентября 2010

Пример кода:

# -*- coding: utf-8 -*-
from functools import wraps

class MyClass(object):
  def __init__(self):
    pass

  #decorator inside class
  def call(f):
    @wraps(f)
    def wrapper(*args):
      print 'Wrapper: ', args
    return wrapper

  #decorated 'method' without self
  @call
  def myfunc(a): 
    pass

c = MyClass()
c.myfunc(1)

Возвращает:

Wrapper:  (<test3.MyClass object at 0xb788a34c>, 1)

Это нормально?Может кто-нибудь объяснить?

Если это функция , я бы использовал ее в своей библиотеке.

Ответы [ 2 ]

1 голос
/ 28 сентября 2010
@call
def myfunc(a):
    ...

эквивалентно

def myfunc(a):
    ...
myfunc=call(myfunc)

Первоначальный myfunc мог ожидать только один аргумент a, но после добавления call новый myfunc может принимать любое количество позиционных аргументов, и все они будут помещены в args.

Обратите внимание, что

def call(f)

никогда не звонит f. Так что тот факт, что

def myfunc(a)

не хватает нормального аргумента self, это не проблема. Это просто никогда не подходит.

Когда вы звоните c.myfunc(1), вызывается wrapper(*args).

Что такое арг? Итак, поскольку c.myfunc является вызовом метода, c отправляется в качестве первого аргумента, за которым следуют любые последующие аргументы. В этом случае последующим аргументом является 1. Оба аргумента отправляются на wrapper, поэтому args представляет собой 2-кортеж (c,1).

Таким образом, вы получаете

Wrapper:  (<test3.MyClass object at 0xb788a34c>, 1)
1 голос
/ 28 сентября 2010

Это совершенно нормально.

Функция myfunc заменяется экземпляром wrapper. Подпись wrapper является (*args). поскольку это связанный метод, первым аргументом является экземпляр MyClass, который выводится после строки `Wrapper: '.

Что тебя смущает?

Стоит отметить, что если вы используете call в качестве декоратора из за пределами из MyClass, он сгенерирует TypeError. Одним из способов решения этой проблемы является применение к нему декоратора staticmethod, но тогда вы не сможете вызвать его во время создания класса.

Это немного глупо, но я обращаюсь к тому, как это сделать обоими способами здесь .

обновление после комментария

он получает экземпляр в качестве первого аргумента независимо от того, введете ли вы self в списке параметров, потому что после создается класс и создается экземпляр экземпляра, это связанный метод . когда вы называете это в форме

@instance.call
def foo(bar):
    return bar + 1

расширяется до

def foo(bar):
    return bar + 1
foo = instance.call(f)

но учтите, что вы вызываете его в инстансе! автоматически расширится до вызова вида

def foo(bar):
    return bar + 1
foo = MyClass.call(instance, f)

Так работают методы. Но вы только определили call, чтобы принять один аргумент, так что это поднимает TypeError.

Что касается вызова во время конструирования класса, он работает нормально. но функция, которую она возвращает, получает экземпляр MyClass, когда it вызывается по той же причине, что я объяснил выше. В частности, любые аргументы, которые вы передаете ему в явном виде, приходят после неявного и автоматического размещения экземпляра, к которому он вызывается, в начале списка аргументов.

...