问题
How does the Python isinstance
function work internally? Is there anything I can do to alter its results, like define a special function inside a class or something? Here's my use case:
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
acts almost like a mixin, except a mixing wouldn't be appropriate here. Is there any way I can make the above code work? I should also note that the isinstance
line also gives the following error:
isinstance(f, Foo)
TypeError: isinstance() arg 2 must be a type or tuple of types
回答1:
How about the following:
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)
With the above code:
isinstance(f, Foo)
works;f()
calls the decorated method which then forwards to the original method.
The basic idea is to make sure that the decorated Foo
is still a class, and to also make sure that the decorated Foo
is a subclass of the original Foo
.
P.S. The purpose of all this is not entirely clear to me; it might be that metaclasses are a better way to achieve what you're trying to do.
回答2:
The problem is that Foo
in your example isn't a class.
This code:
@Decorator
class Foo:
pass
is equivalent to:
class Foo:
pass
Foo = Decorator(Foo)
Which means that Foo
is an instance of class Decorator
. Because Foo
is not a clas or type, isinstance
complains.
回答3:
When decorating a class, it's often useful or desirable to for the decorated return value to also be type; The most obvious way of achieving this is to have the decorator construct and return a new class directly.
That functionality is already handled by metaclasses; In fact, metaclasses are a bit more powerful than decorators, since you get to describe the new class before a decorated class has even been constructed.
Another option is to return the same object that was passed in; but with some changes. That's a better use for decorators, since it works well when you nest decorators. Since you're modifying the behavior when Foo()
is used, then you probably want to modify Foo's __init__
, which might look like this:
>>> 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
回答4:
You can't get type of the object that Foo
returns without calling Foo
.
isinstance
complains about its second argument because it is an instance - in you case instance of Decorated
. Although you think of Foo
like a class but actually it is just a callable object and it is not a class.
Maybe the next will help you to rethink/solve your problem:
>>> isinstance(f, Foo._decorated)
True
来源:https://stackoverflow.com/questions/7514968/making-isinstance-work-with-decorators