【问题标题】:`python`: put class method, that returns a class instance, into seperate file`python`:将返回类实例的类方法放入单独的文件中
【发布时间】:2021-11-03 18:42:22
【问题描述】:

我有一个大类定义,为了清楚起见,我想在一个单独的文件中分离出算术 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

以防万一人们将我指向this 答案:)

#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 + a2b1 + b2a1 * a2b1 * b2 以及a + ba * b 的含义。

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

【问题讨论】:

  • 我想我以前从未见过这样的事情。看起来是个 XY 问题
  • 你可以写一个 mixin 类,或者找出一些更简洁的继承结构。但是A.__add__ = _a_add 感觉非常奇怪,我想我会对此感到非常惊讶。我不明白为什么它不在主类定义中
  • 我同意 XY 的问题,这绝不是你想做的任何事情的最佳方式
  • 我已将我的推理添加到问题的末尾

标签: python class


【解决方案1】:

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

所以你可以使用:

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

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

【讨论】:

  • 感谢 Blckknght,看起来它可能会起作用。但我认为我们对看到 dunder-method 被这样调用时有同样的下意识反应——必须有更好的方法来做事。我已经在问题的末尾添加了我的推理,这应该可以更好地解释我为什么要这样做
  • __add__ 方法返回与其参数之一相同的类的实例可能比将名称A 硬编码为返回的类型更具可扩展性。不喜欢self.__class__的也可以写type(self)
猜你喜欢
  • 2012-12-25
  • 1970-01-01
  • 2015-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-10-13
  • 2023-02-09
相关资源
最近更新 更多