首页 > 解决方案 > `python`:将返回类实例的类方法放入单独的文件中

问题描述

我有一个大类定义,为了清楚起见,我想在一个单独的文件中分离出算术 dunder 方法。然而,这些方法需要能够返回类的新实例,这会导致循环导入、名称错误或难看的不可维护性。

我想要这样做的动机是最后。

这是一个最小的工作示例。将所有内容放在一个文件中...

#classa.py
class A:
    def __init__(self, val):
        self.val = val
    def __add__(self, other):
        return A(self.val + other.val)

...有用:

from .classa import A
a1 = A(30)
a2 = A(21)
(a1 + a2).val # 51

现在,尝试将该__add__方法放入单独的文件中。

什么不起作用:

尝试 1

#classa.py

from .classa_add import Aarithmatic

class A(Aarithmatic):
    def __init__(self, val):
        self.val = val


#classa_add.py

from .classa import A

class Aarithmatic:
    def __add__(self, other):
        return A(self.val + other.val)

在这里,由于循环导入,我们可以理解地得到一个错误:

from .classa import A  # <-- ImportError
a1 = A(30)
a2 = A(21)
(a1 + a2).val

尝试 2

以防万一人们指出我这个答案:)

#classa.py

from .classa_add import Aarithmatic

class A(Aarithmatic):
    def __init__(self, val):
        self.val = val


#classa_add.py

from __future__ import annotations
from typing import TYPE_CHECKING

if TYPE_CHECKING: 
    from .classa import A

class Aarithmatic:
    def __add__(self, other):
        return A(self.val + other.val)

在这里,我们在尝试添加实例时遇到名称错误,这A是未知的。这是有道理的,这里使用的“解决方案”A仅用于注释中:

from .classa import A
a1 = A(30)
a2 = A(21)
(a1 + a2).val # <-- NameError: name 'A' is not defined # (in __add__)

尝试 3

我可以这样...

#classa.py

class A:
    def __init__(self, val):
        self.val = val

#classa_add.py

from .classa import A

def _a_add(self, other):
    return A(self.val + other.val)

A.__add__ = _a_add

# Or as oneliner: 
A.__add__ = lambda self, other: A(self.val + other.val)

但这首先是非常丑陋和不可读的,其次我必须导入classa_add只是为了添加添加功能,这令人困惑:

from .classa import A
import .classa_add  # <- unclear to reader, why this needs to be here
a1 = A(30)
a2 = A(21)
(a1+a2).val # 51

如果这是一个包,我可以添加一个__init__.py文件来执行此操作,但它仍然很难看,我相信有更好的解决方案。

有谁知道,更好的解决方案是什么?

非常感谢!

我正在使用python 3.8,顺便说一句。


编辑

推理。为什么我什至想这样做?

我有两个相似的类 (AB),每个类都有很多方法。沿着pandas DataFrameSeries类的思路思考,只是稍微小一点并且不太相似,以至于可以让它们从一个共同的父级继承。目前,每个类都有一个定义它的文件(classa.pyclassb.py)。

现在,这些文件首先太长,其次,实例上的算术非常相似。

这让我想创建一个单独的文件来定义它的含义a1 + a2, b1 + b2, a1 * a2, b1 * b2,a + b以及 and a * b.

我希望这可以更清楚地说明我为什么要这样做,并且我希望您同意以这种方式拆分类定义更易于维护。

标签: pythonclass

解决方案


您的Aarithmatic类可以A通过参数获得调用其方法的时间的引用self。它根本不需要导入A。如果在实例__add__上被调用,那么(或)应该给我们。Aself.__class__type(self)A

所以你可以使用:

def __add__(self, other):
    return self.__class__(self.val + other.val)

但我也想挑战这个问题的前提,将这些类分开是个好主意(尤其是将它们放在单独的文件中)。如果您发现您正在编写非常紧密集成的代码,那么这些代码都应该放在一起。继承和模块是非常好的工具,但如果你发现它们妨碍了你,这可能表明它们不是解决你当前问题的正确工具。


推荐阅读