Python Class Decorator - PullRequest
       5

Python Class Decorator

3 голосов
/ 21 октября 2010

Я пытаюсь украсить реальный класс, используя этот код:

def my_decorator(cls):
    def wrap(*args, **kw):
        return object.__new__(cls)
    return wrap

@my_decorator
class TestClass(object):
    def __init__(self):
        print "__init__ should run if object.__new__ correctly returns an instance of cls"


test = TestClass() # shouldn't TestClass.__init__() be run here?

Я не получаю ошибок, но также не вижу сообщения от TestClass.__init__().

Согласно документам для классов нового стиля :

Типичные реализации создают новый экземпляр класса, вызывая метод __new__() суперкласса, используя super(currentclass, cls).__new__(cls[, ...]) с соответствующими аргументами, и затем изменяя вновь созданный экземпляр по мере необходимости перед его возвратом.

Если __new__() возвращает экземпляр cls, то будет вызываться метод __init__() нового экземпляра, например __init__(self[, ...]), где self - это новый экземпляр, а остальные аргументы такие же, как были переданы в __new__().

Есть идеи, почему __init__ не работает?

Также я попытался позвонить __new__ так:

return super(cls.__bases__[0], cls).__new__(cls)

но он вернет TypeError:

TypeError: super.__new__(TestClass): TestClass is not a subtype of super

Ответы [ 2 ]

11 голосов
/ 21 октября 2010

__init__ не работает, потому что object.__new__ не знает, как его вызвать. Если вы измените его на cls.__call__(*args, **kwargs) или лучше cls(*args, **kwargs), должно работать. Помните, что класс является вызываемым: вызов его создает новый экземпляр. Просто вызов __new__ возвращает экземпляр, но не проходит инициализацию. В качестве альтернативы можно было бы вызвать __new__, а затем вручную вызвать __init__, но это лишь замена логики, уже воплощенной в __call__.

В документации, которую вы цитируете, содержится ссылка на вызов super из в методе __new__ класса. Здесь вы звоните со стороны, а не как обычно, как я уже обсуждал.

0 голосов
/ 21 октября 2010

Не могу сказать вам причину, но этот хак работает __init__

def my_decorator(cls):
    print "In my_decorator()"
    def wrap(*args, **kw):
        print "In wrap()"
        return cls.__init__(object.__new__(cls), *args, **kw)
    return wrap

@my_decorator
class TestClass(object):
    def __init__(self):
        print "__init__ should run if object.__new__ correctly returns an instance of cls"
...