【问题标题】:Python circular dependency with the inclusion of member functions包含成员函数的 Python 循环依赖
【发布时间】:2019-05-08 01:18:27
【问题描述】:

当我尝试将现有函数作为成员函数合并到类中时,就会出现问题。我有一个类似这样的设置:

类:

base(object)
    primitive(base)
        square(primitive)
        union(primitive)

我有一个名为union 的函数,用户调用它返回一个union 原始对象。

obj1 = square()
obj2 = square()
obj3 = union(obj1, obj2) #this is the union function which returns a union primitive

我希望用户也可以这样做

obj3 = obj1.union(obj2)

这就是问题出现的地方。 primitive 类需要导入union 函数,该函数又导入union 类,该类又导入primitive 类,我遇到了循环依赖错误。是否有一种聪明的方法来重构代码或更改导入语句以使其正常工作?

编辑:

为了清楚起见,这是代码的结构:

operations/union.py(函数)

from objects import union as _union #the union class
def union(obj1, obj2): #the union function
    #CODE
    return _union(args)

objects/union.py(类)

from objects import primitive
class union(primitive):
    #CODE

objects/primitive.py

from operations import union #the function
class primitive(base):
    #CODE
    def union(self, obj2):
        return union(self, obj2)

有一个名为union 的类,它是一个包含联合输入对象信息的对象。用户不与此交互。然后是用户可以调用的union 函数,它返回一个union 对象。我希望primitive 类包含一个名为union 的成员函数,它使用我已经编写的union 函数。问题是union 函数返回一个继承自primitive 类的union 对象。这会导致循环依赖问题。我可以删除 union 成员函数,但用户不能这样做

obj3 = obj1.union(obj2)

【问题讨论】:

  • 你能提供一个minimal reproducible example吗?你的图表对我来说没有意义。 Union 是一个类还是一个函数? 究竟发生了什么?无论如何,只要把这些东西放在同一个模块中,你的循环依赖就可以解决了。
  • 让我知道该编辑是否可以解决问题
  • 为什么不把所有的类放在同一个模块中呢?还是把union类和union函数放在同一个模块中?

标签: python circular-dependency


【解决方案1】:

在不同的模块/文件中定义primitivesquare 听起来好像给自己带来了很多麻烦。如果您在同一个模块中定义它们,我怀疑您会遇到任何麻烦。例如,以下工作正常:

class Primitive:
    pass

class Square(Primitive):
    def union(self, other):
        return Union(self, other)

class Union(Primitive):
    def __init__(self, *members):
        self.members = members

obj1 = Square()
obj2 = Square()
obj3 = obj1.union(obj2)

print(type(obj3))
print(obj3.members)

如果你坚持将你的类放在不同的文件中,你可以这样做:

primitive.py:

    class Primitive:
        pass

square.py:

    from .primitive import Primitive

    class Square(Primitive):
        def union(self, other):
            from .union import Union
            return Union(self, other)

union.py:

    from .primitive import Primitive

    class Union(Primitive):
        def __init__(self, *members):
            self.members = members

test.py:

    from .square import Square

    obj1 = Square()
    obj2 = Square()
    obj3 = obj1.union(obj2)

    print(type(obj3))
    print(obj3.members)

关键点是将from .union import Union 语句移动到union() 方法中,直到需要它才会被调用。

这是关于 Python 循环导入的 good resource

【讨论】:

  • 它们不在同一个文件中的主要原因是有几个原语,每个都有很多代码。拥有 1000+ 行文件是不是很糟糕?
  • @Chris。是的。在函数中导入是不好的形式。只需在最后粘贴导入即可。
  • @ChrisUchytil 无论哪种方式都可以。当我编写一个递归下降解析器(它自然有很多循环依赖)时,我将所有内容放在一个长文件中以避免这个确切的问题。其他人喜欢按文件将它们分开,以便他们的项目更易于浏览。来自 Java 的程序员倾向于将每个类放在一个单独的文件中,这在 Python 中既没有必要也没有帮助。像 scikit-learn 或 Django 这样的优秀 Python 代码库往往会在同一个文件中包含一些密切相关的类。
【解决方案2】:

如果您在模块级别不依赖导入的任何内容,则可以将导入放在文件末尾。在union的情况下,Primitive需要在模块范围内定义一个类,所以保持union.py原样:

from objects.primitive import Primitive
class Union(Primitive):
    @classmethod
    def union(cls, a, b):
        return Union(a, b)

但是primitive 在一个方法中只需要Union,而不是在模块范围内创建任何东西,所以你只需要在调用方法时导入的模块就存在。这意味着您可以这样做:

class Primitive(Base):
    #CODE
    def union(self, obj2):
        return union.Union.union(self, obj2)

from objects import union

您需要将导入放在最后的原因是确保无论哪个模块首先导入,它都能正常工作。如果您导入objects.union,它将在到达模块主体之前正确导入objects.primitive。如果先导入objects.primitive,它将尝试导入objects.union,这需要Primitive 类已经存在。因此在类主体之后导入。

我建议将union 设置为@classmethodUnion,这样您就可以正确地将其用作备用构造函数。此外,使用在 CamelCase 中编写类名的 Python 约定使命名更容易混淆。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-03-23
    • 2012-02-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-09-03
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多