Python:类型检查所需的循环导入

发布于 2024-08-25 15:27:30 字数 1031 浏览 11 评论 0原文

首先:我确实知道关于循环进口的话题已经有很多问题和答案。

答案或多或少是:“正确设计你的模块/类结构,你将不需要循环导入”。确实如此。我非常努力地为我当前的项目做出适当的设计,我认为我是成功的。

但我的具体问题如下:我需要在模块中进行类型检查,该模块已由包含要检查的类的模块导入。但这会引发导入错误。

像这样:

foo.py:

from bar import Bar

class Foo(object):

    def __init__(self):
        self.__bar = Bar(self)

bar.py:

from foo import Foo

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        if not isinstance(arg_instance_of_foo, Foo):
            raise TypeError()

解决方案 1: 如果我修改它以通过字符串比较检查类型,它将起作用。但我不太喜欢这个解决方案(字符串比较对于简单的类型检查来说相当昂贵,并且在重构时可能会出现问题)。

bar_modified.py:

from foo import Foo

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        if not arg_instance_of_foo.__class__.__name__ == "Foo":
            raise TypeError()

解决方案 2: 我还可以将这两个类打包到一个模块中。但是我的项目有很多不同的类,例如“Bar”示例,我想将它们分成不同的模块文件。

在我自己的 2 个解决方案对我来说没有选择之后:有没有人对这个问题有更好的解决方案?

First of all: I do know that there are already many questions and answers to the topic of the circular imports.

The answer is more or less: "Design your Module/Class structure properly and you will not need circular imports". That is true. I tried very hard to make a proper design for my current project, I in my opinion I was successful with this.

But my specific problem is the following: I need a type check in a module that is already imported by the module containing the class to check against. But this throws an import error.

Like so:

foo.py:

from bar import Bar

class Foo(object):

    def __init__(self):
        self.__bar = Bar(self)

bar.py:

from foo import Foo

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        if not isinstance(arg_instance_of_foo, Foo):
            raise TypeError()

Solution 1: If I modified it to check the type by a string comparison, it will work. But I dont really like this solution (string comparsion is rather expensive for a simple type check, and could get a problem when it comes to refactoring).

bar_modified.py:

from foo import Foo

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        if not arg_instance_of_foo.__class__.__name__ == "Foo":
            raise TypeError()

Solution 2: I could also pack the two classes into one module. But my project has lots of different classes like the "Bar" example, and I want to seperate them into different module files.

After my own 2 solutions are no option for me: Has anyone a nicer solution for this problem?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

晚风撩人 2024-09-01 15:27:30

您可以针对接口(ABC - 抽象基类进行编程python),而不是特定类型 Bar。这是解决许多语言中的包/模块相互依赖关系的经典方法。从概念上讲,它还应该带来更好的对象模型设计。

在您的情况下,您可以在其他模块中定义接口IBar(甚至在包含Foo类的模块中 - 取决于该abc的使用)。您的代码如下所示:

foo.py:

from bar import Bar, IFoo

class Foo(IFoo):
    def __init__(self):
        self.__bar = Bar(self)

# todo: remove this, just sample code
f = Foo()
b = Bar(f)
print f
print b
x = Bar('do not fail me please') # this fails

bar.py:

from abc import ABCMeta
class IFoo:
    __metaclass__ = ABCMeta

class Bar(object):
    def __init__(self, arg_instance_of_foo):
        if not isinstance(arg_instance_of_foo, IFoo):
            raise TypeError()

You can program against interface (ABC - abstract base class in python), and not specific type Bar. This is classical way to resolve package/module inter-dependencies in many languages. Conceptually it should also result in better object model design.

In your case you would define interface IBar in some other module (or even in module that contains Foo class - depends on the usage of that abc). You code then looks like this:

foo.py:

from bar import Bar, IFoo

class Foo(IFoo):
    def __init__(self):
        self.__bar = Bar(self)

# todo: remove this, just sample code
f = Foo()
b = Bar(f)
print f
print b
x = Bar('do not fail me please') # this fails

bar.py:

from abc import ABCMeta
class IFoo:
    __metaclass__ = ABCMeta

class Bar(object):
    def __init__(self, arg_instance_of_foo):
        if not isinstance(arg_instance_of_foo, IFoo):
            raise TypeError()
宫墨修音 2024-09-01 15:27:30

最好的解决方案是不检查类型。

另一个解决方案是在加载 FooBar 两个类之前不创建它们的实例,也不引用它们。如果首先加载第一个模块,则在执行 class Foo 语句之前不要创建 Bar 或引用 Bar。同样,如果先加载第二个模块,则在执行 class Bar 语句之前不要创建 Foo 或引用 Foo

这基本上是 ImportError 的来源,如果您改为执行“import foo”和“import bar”,并在您现在使用的位置使用 foo.Foo ,则可以避免这种情况Foobar.Bar(您现在使用 Bar)。这样做时,在创建 FooBar 之前,您不再引用它们中的任何一个,希望在创建这两个对象之前不会发生这种情况(否则您'将得到一个AttributeError)。

The best solution is to not check types.

The other solution is to not create an instance of, and not reference at all, Foo or Bar until both classes are loaded. If the first module is loaded first, don't create a Bar or refer to Bar until after the class Foo statement is executed. Similarly, if the second module is loaded first, don't create a Foo or reference Foo until after the class Bar statement is executed.

This is basically the source of the ImportError, which could be avoided if you did "import foo" and "import bar" instead, and used foo.Foo where you now use Foo, and bar.Bar where you now use Bar. In doing this, you no longer refer to either of them until a Foo or Bar is created, which hopefully won't happen until after both are created (or else you'll get an AttributeError).

把人绕傻吧 2024-09-01 15:27:30

您可以像这样推迟 bar.py 中的导入:

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        from foo import Foo
        if not isinstance(arg_instance_of_foo, Foo):
            raise TypeError()

You could just defer the import in bar.py like this:

class Bar(object):

    def __init__(self, arg_instance_of_foo):
        from foo import Foo
        if not isinstance(arg_instance_of_foo, Foo):
            raise TypeError()
二手情话 2024-09-01 15:27:30

可能重复:没有循环导入的Python类型提示

您应该使用 前向引用 (PEP 484 - 类型提示):

当类型提示包含尚未定义的名称时,该定义可能会表示为字符串文字,稍后解析。

因此,不要:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

做:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right

Possible duplicate: Python type hinting without cyclic imports

You should use Forward Reference (PEP 484 - Type Hints):

When a type hint contains names that have not been defined yet, that definition may be expressed as a string literal, to be resolved later.

So instead of:

class Tree:
    def __init__(self, left: Tree, right: Tree):
        self.left = left
        self.right = right

do:

class Tree:
    def __init__(self, left: 'Tree', right: 'Tree'):
        self.left = left
        self.right = right
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文