3

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

Paul Manta
  • 30,618
  • 31
  • 128
  • 208
  • 3
    What are you actually trying to achieve with this? – Cat Plus Plus Sep 22 '11 at 13:00
  • You haven't told us what your decorator does, so it's hard to know what would work. Consider changing attributes of the class itself with the decorator and then returning the class, or rewriting the class with a metaclass instead of a decorator. – agf Sep 22 '11 at 13:17

4 Answers4

6

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.

NPE
  • 486,780
  • 108
  • 951
  • 1,012
1

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.

yak
  • 8,851
  • 2
  • 29
  • 23
  • 2
    This only addresses his "should also note", it doesn't tell him how to make isinstance work. – agf Sep 22 '11 at 13:13
0

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
SingleNegationElimination
  • 151,563
  • 33
  • 264
  • 304
-1

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
Roman Bodnarchuk
  • 29,461
  • 12
  • 59
  • 75