Заставить isinstance работать с декораторами - PullRequest
2 голосов
/ 22 сентября 2011

Как работает Python isinstance внутри системы? Могу ли я что-нибудь сделать, чтобы изменить его результаты, например определить специальную функцию внутри класса или что-то еще? Вот мой вариант использования:

class Decorator:
    def __init__(self, decorated):
        self._decorated = decorated

    def __call__(self):
        return self._decorated()

@Decorator
class Foo:
    pass

f = Foo()

# How can I make this be true?
isinstance(f, Foo)

Decorator действует почти как миксин, только микширование здесь не подходит. Можно ли как-то заставить этот код работать? Следует также отметить, что строка isinstance также выдает следующую ошибку:

isinstance (f, Foo)
Ошибка типа: isinstance () arg 2 должно быть типом или кортежем типов

Ответы [ 4 ]

5 голосов
/ 22 сентября 2011

Как насчет следующего:

def Decorator(decorated):
    class Dec(decorated):
        def __call__(self):
            print 'in decorated __call__'
            return decorated.__call__(self)
    return Dec

@Decorator
class Foo(object):
    def __call__(self):
        print 'in original __call__'

f = Foo()

# How can I make this be true?
print isinstance(f, Foo)

С кодом выше:

  • isinstance(f, Foo) работает;
  • f() вызывает декорированный метод, которыйзатем переходим к исходному методу.

Основная идея - убедиться, что декорированный Foo все еще является классом, а также убедиться, что декорированный Foo является подкласс оригинала Foo.

PS Цель всего этого мне не совсем понятна;возможно, метаклассы - лучший способ достичь того, что вы пытаетесь сделать.

1 голос
/ 22 сентября 2011

Проблема в том, что Foo в вашем примере не является классом.

Этот код:

@Decorator
class Foo:
    pass

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

class Foo:
    pass
Foo = Decorator(Foo)

Это означает, что Foo является экземпляром класса Decorator.Поскольку Foo не относится к классу или типу, isinstance жалуется.

0 голосов
/ 22 сентября 2011

При оформлении класса часто бывает полезно или желательно, чтобы оформленное возвращаемое значение также было типом;Наиболее очевидный способ добиться этого - создать декоратор и напрямую вернуть новый класс.

Эта функциональность уже обрабатывается метаклассами;На самом деле, метаклассы немного более мощные, чем декораторы, так как вы можете описать новый класс еще до того, как декорированный класс будет создан.

Другой вариант - вернуть тот же объект, который был передан;но с некоторыми изменениями.Это лучше использовать для декораторов, так как он хорошо работает, когда вы вкладываете декораторы.Поскольку вы изменяете поведение при использовании Foo(), вы, вероятно, захотите изменить __init__ в Foo, что может выглядеть следующим образом:

>>> def Decorator(cls):
...     assert isinstance(cls, type)
...     try:
...         old_init = cls.__init__.im_func
...     except AttributeError:
...         def old_init(self): pass
...     def new_init(self):
...         # do some clever stuff:
...         old_init(self)
...     cls.__init__ = new_init
...     return cls
... 
>>> @Decorator
... class Foo(object):
...     def __init__(self): pass
... 
>>> @Decorator
... class Bar(object):
...     pass
... 
>>> f = Foo()
>>> isinstance(f, Foo)
True
0 голосов
/ 22 сентября 2011

Вы не можете получить тип объекта, который Foo возвращает без вызова Foo.

isinstance жалуется на свой второй аргумент, потому что это экземпляр - в вашем случае экземпляр Decorated. Хотя вы думаете о Foo как о классе, но на самом деле это просто вызываемый объект, а не класс.

Может быть, следующий поможет вам переосмыслить / решить вашу проблему:

>>> isinstance(f, Foo._decorated)
True
...