首页 > 解决方案 > `my_instance = MyClass(a, b).my_method(c)` 是可接受的(Pythonic)模式吗?

问题描述

我已经考虑了好几个小时了。我有一个类最终需要(但会在没有的情况下初始化)一些关键属性。此类的子类需要不同的属性。

如果有帮助,这是一个半边数据结构中的顶点、边和面的基本 MeshElement 类。

通常,我需要创建一个与另一个实例相似但不相同的 istance,因此我有如下方法borrow_b将属性从一个实例复制到另一个实例。

我想不出一个borrow_b构造函数,a)保留/复制来自 init 的类型提示,b)可以由具有不同必需属性的子类继承。

这是我有过的最理智的想法,但它打破了“修改对象的方法返回无”的规则。

class AB:

    def __init__(self, a: Optional[int] = None, b: Optional[int] = None):
        if a is not None:
            self.a = a
        if b is not None:
            self.b = b

    def borrow_b(self, other):
        self.b = other.b # already type hinted when other was initialized
        return self

# >>> one_two = AB(1, 2)
# >>> six_two = AB(6).borrow_b(one_two)

我当然意识到这可以分为两行:

six_two = AB(6)
six_two.borrow_b(one_two)

...并允许borrow_b返回 None,但我喜欢第一个看起来更像构造函数,而且我不相信第一个不是“Pythonic”。

当然,耳朵愿意寻求更好的解决方案。

FWIW,

six_two = AB(6, six_two.b)

...不是“可行”的解决方案,因为并非我要复制的所有属性都是“必需的”。我通常想复制一些必需的参数以及通常但不一定附加到网格元素(颜色、紫外线、硬度等)的杂项标签。

标签: pythonpython-3.xconventions

解决方案


使用类方法来实现替代构造函数。对象的目的是跨多个方法调用或函数边界保持状态;创建一个仅在一个范围内使用并立即丢弃的对象表明您首先需要一个具有适当参数的函数。

class AB:

    def __init__(self, a: Optional[int] = None, b: Optional[int] = None):
        if a is not None:
            self.a = a
        if b is not None:
            self.b = b

    @classmethod
    def borrow_b(cls, o1: AB, o2: AB):
        return cls(o1.a, o2.b)

one_two = AB(1, 2)
six = AB(6)
six_two = AB.borrow_b(one_two, six)

是的,six似乎只是为了初始化而创建的six_two,但可能它已经因为其他原因而存在。否则,您可以定义一个类方法,该方法专门采用一个对象和一组任意其他参数来覆盖“原型”。就像是

@classmethod
def make_from_prototype(cls, prototype, **kwargs):
    new_obj = cls(prototype.a, prototype.b)
    for attr, value in kwargs:
        setattr(new_obj, attr, value)
    return new_obj 

...

one_two = AB(1,2)
six_two = AB.make_from_prototype(one_two, b=6)

一个缺点是make_from_prototype绕过类型提示,因为**kwargs可以采用任意一组关键字参数。它最好用作私有类方法,仅用作专用类方法的基本实现,例如borrow_b

@classmethod
def _make_from_prototype(cls, prototype: AB, **kwargs):
    # Same definition as above

@classmethod
def borrow_b(cls, o1: AB, o2:AB):
    return cls._make_from_prototype(o1, b=o2.b)

推荐阅读