Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

issubclass() returns False on the same class imported from different paths

Tags:

python

class

The intention was to implement some sort of plugin framework, where plugins are subclasses (i.e. B) of the same base class (i.e. A). The base class is loaded with standard import, whereas subclasses are loaded with imp.load_module() from the path of a well-known package (i.e. pkg).

pkg/
    __init__.py
    mod1.py
        class A
    mod2.py
        class B(pkg.mod1.A)

This worked fine with real subclasses, i.e.,

# test_1.py
import pkg
from pkg import mod1
import imp
tup = imp.find_module('mod2', pkg.__path__)
mod2 = imp.load_module('mod2', tup[0], tup[1], tup[2])
print(issubclass(mod2.B, mod1.A)) # True

But the problem came when testing the base class itself,

# test_2.py
import pkg
from pkg import mod1
import imp
tup = imp.find_module('mod1', pkg.__path__)
mod0 = imp.load_module('mod1', tup[0], tup[1], tup[2])
print(issubclass(mod0.A, mod1.A)) # False

But mod0.A and mod1.A are actually the same class from the same file (pkg/mod1.py).

This issue appears in both python 2.7 and 3.2.

Now the question is two-fold, a) Is it an expected feature or a bug of issubclass(), and b) How to get rid of this without changing contents of pkg?

like image 736
liuyu Avatar asked Jul 12 '12 21:07

liuyu


Video Answer


2 Answers

They aren't the same class. They were created with the same code, but since you executed that code twice (once in the import and once in load_module) you get two different class objects. issubclass is comparing the identities of the class objects and they're different.

Edit: since you can't rely on issubclass, one possible alternative is to create a unique attribute on the base class that will be inherited by the derived classes. This attribute will exist on copies of the class as well. You can then test for the attribute.

class A:
    isA = True

class B(A):
    pass

class C:
    pass

def isA(aclass):
    try:
        return aclass.isA
    except AttributeError:
        return False

print isA(A)
True
print isA(B)
True
print isA(C)
False
like image 52
Mark Ransom Avatar answered Oct 14 '22 14:10

Mark Ransom


Since I spent some time fiddling with this, I thought I would share my solution:

import inspect

...

def inherits_from(child, parent_name):
    if inspect.isclass(child):
        if parent_name in [c.__name__ for c in inspect.getmro(child)[1:]]:
            return True
    return False

print inherits_from(possible_child_class, 'parent_class')
#True

Of course this only really checks that the child class inherits from a class CALLED parent_class, but for my purpose (and most I suspect) that's fine.

Note: this returns false if possible_child_class is an instance of parent_class due to the [1:].

like image 29
SColvin Avatar answered Oct 14 '22 13:10

SColvin