在不导入F类型的情况下确定对象是否为Foo类型

2024-04-20 08:19:31 发布

您现在位置:Python中文网/ 问答频道 /正文

假设我在一个类中定义了:

import stuff
import more stuff
import stuff that takes a long time to import
class Foo(object):
    def __init__(self, arg1, arg2 etc.):
        self.arg1 = arg1 
        self.arg2 = arg2
        # Lots of other stuff
    # Lots of methods

在另一个文件里我有这样的代码:

^{pr2}$

假设由于我无法控制的原因,Foo文件需要很长时间才能导入。如何重构此代码以不导入Foo,但仍然可靠地检查类型?我认为鸭子打字不适合我的特殊情况。在

例如,我是否应该检查obj基的字符串表示形式?或者还有其他更规范的方法吗?在


Tags: 文件of代码importselfthat定义foo
2条回答

如果big_file确实因为任何非标准的原因而需要很长时间导入,那么您确实可以使用str表示。下面是一个相当健壮的实现:

from big_file import Foo

def isFoo(obj):
    try:
        return obj.__module__ == 'big_file' and type(obj).__name__ == 'Foo'
    except:
        return False

print(isFoo(Foo(...)))
print(isFoo(42))

isFoo函数测试传递的obj是否是在名为big_file的模块中定义的某个名为Foo的类的实例。原则上,如果您有多个同名的模块,例如在不同的包中,这可能会失败,但当然这对您来说很可能不是问题。在

编辑,处理Foo

的子类

正如sytech指出的,上面的解决方案在子类上失败了。也就是说,isFoo(obj)返回{},如果objFoo的子类的实例,而isinstance(obj, Foo)返回{}。以下代码是上述代码的通用版本,用于解决此问题:

^{pr2}$

这使用了与以前相同的测试,但现在不仅对objclass进行了测试,而且还对其所有的超类进行了测试。在

编辑,使想法不会失败

上面唯一需要注意的是,我们只测试模块名称而不是绝对路径。如前所述,只有当您的项目包含多个同名的模块,并且包含同名的类时,这才是一个问题。我们可以测试路径,当然这需要您在代码中指定模块的绝对路径:

import inspect

def my_isinstance(obj, classinfo):
    if isinstance(classinfo[0], str):
        classinfo = (classinfo, )
    for module_path, cls_name in classinfo:
        for cls in inspect.getmro(type(obj)):
            try:
                if inspect.getmodule(cls).__file__ == module_path and cls.__name__ == cls_name:
                    return True
            except:
                pass
    return False

print(my_isinstance(Foo(1, 2), ('/path/to/big_file.py', 'Foo')))
print(my_isinstance(42, ('/path/to/big_file.py', 'Foo')))

为了使函数完全类似于内置的isinstance,它现在还支持多个类作为输入(例如tuple,形式为(('/path/to/module1.py', 'Foo'), ('/path/to/module2.py', 'Bar')),用于检查{}是否是{}或{})的实例。在

虽然这个版本是防弹的,但我个人更喜欢前面的isFoo,因为指定模块的绝对路径有点难看。在

通常,这不是问题。如果您有一个big_file.Foo的实例,那么它的父模块已经在早些时候导入了,即使没有从其他文件显式引用。Python模块只在第一次导入时加载一次(前提是您没有进行任何显式的重新加载或弄乱sys.modules)。因为它已经被导入,所以在另一个文件中执行import big_file操作应该立即运行。在

但是,如果您的另一个文件在某些情况下只会遇到big_file.Foo,而{}只在实际需要时导入到其他地方,那么您可以这样检查对象的类(这不支持子类):

def do_stuff(obj):
    if (obj.__class__.__module__, obj.__class__.__name__) == ('big_file', 'Foo'):
        do_stuff
    else:
        do_other_stuff

因为您已经指出big_file.Foo可以在应用程序中的任何时候导入,并且您希望支持子类,所以可以检查它的模块是否已导入,并有条件地检查类型。在

^{pr2}$

相关问题 更多 >