11

目的是实现某种插件框架,其中插件是同一基类(即 A)的子类(即 B)。基类使用标准导入加载,而子类使用 imp.load_module() 从知名包(即 pkg)的路径加载。

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

这适用于真正的子类,即

# 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

但是在测试基类本身的时候问题就来了,

# 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

但是 mod0.A 和 mod1.A 实际上是来自同一个文件 (pkg/mod1.py) 的同一个类。

这个问题出现在 python 2.7 和 3.2 中。

现在问题有两个方面,a) 它是预期的特性还是 issubclass() 的错误,以及 b) 如何在不改变 pkg 内容的情况下摆脱这个问题?

4

3 回答 3

10

他们不是同一个班级。它们是使用相同的代码创建的,但是由于您执行了两次该代码(一次在 import 中,一次在 load_module 中),您得到了两个不同的类对象。issubclass正在比较类对象的身份,它们是不同的。

编辑:由于您不能依赖issubclass,因此一种可能的替代方法是在基类上创建一个唯一属性,该属性将由派生类继承。该属性也将存在于类的副本中。然后,您可以测试该属性。

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
于 2012-07-12T22:20:35.857 回答
6

由于我花了一些时间摆弄这个,我想我会分享我的解决方案:

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

当然,这只真正检查子类是否继承自类CALLED parent_class,但出于我的目的(我怀疑是大多数),这很好。

注意:如果possible_child_class是 parent_class 的实例,则返回 false,因为[1:].

于 2013-09-12T17:58:33.117 回答
1
#!/usr/bin/env python

import os
import sys
import pkg
from pkg import mod1
import imp


def smart_load_module(name, path):
    # get full module path
    full_path = os.path.abspath(os.path.join(path[0], name))

    for module_name, module in sys.modules.items():
        # skip empty modules and ones without actual file
        if not module or not hasattr(module, '__file__'):
            continue

        # remove extension and normalize path
        module_path = os.path.abspath(os.path.splitext(module.__file__)[0])
        if full_path == module_path:
            return module

    # if not found, load standard way
    tup = imp.find_module(name, path)
    return imp.load_module(name, tup[0], tup[1], tup[2])


if __name__ == '__main__':
    mod00 = smart_load_module('mod1', pkg.__path__)
    print(issubclass(mod00.A, mod1.A))  # True

    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

这对我有用。我通过 sys.modules 中的完整路径搜索类,如果找到,则返回加载的实例。

于 2013-09-12T11:54:02.820 回答