3

所以,Python 很高兴我能写出这样的代码,

class A(): pass

a1 = A()
a2 = A()
a1.some_field = 5
a2.other_field = 7

现在,在将对象传递给方法时,我已经学会了停止担心并喜欢鸭子类型。而且我接受允许一个类的不同实例具有不同的字段有时会很方便。

我的问题是,我正在与一个由 4 名开发人员组成的团队构建一个中型 Web 应用程序,我不禁认为向对象添加任意字段会使推理应用程序状态变得更加困难。

我想我的问题是:向对象添加任意字段的做法只是鸭子类型的自然扩展,还是应该避免?

这是一个具体的例子:

class Visitor():
    def __init__(self, name, address, dob):
        self.name = name
        self.address = address
        self.dob = dob

    def summarize_visits(visits):
        self.most_recent_visit = find_most_recent_visit(visits)

在这种情况下,处理对象的代码必须意识到除非有人先前调用过同一个对象,否则会引发一个Visitor事实。似乎它会导致很多块,不是吗?visitor.most_recent_visitAttributeErrorsummarize_visitsif hasattr(...)

4

4 回答 4

2

编写这样的代码实际上是 Python 的最大好处之一。我的经验法则是仅在内部使用特定于实例的字段(即在一个函数中,并且仅在必要时使用),而不是期望它们被外部模​​块使用。

如果我的对象预计会被另一个人使用,我希望他们查看类定义并找到他们需要了解的关于其结构的所有信息,并在一个地方清楚地描绘出来。记住,explicit is better than implicit

于 2013-04-10T21:35:57.803 回答
1

这样做通常很方便,但我看到您担心与其他开发人员合作时可能会导致混乱。您可以通过定义此处__slots__讨论的变量来阻止人们向类添加任意值。这将迫使人们明确他们想要的对象属性,这有助于避免混淆。

于 2013-04-10T21:26:45.143 回答
0

我相信这方面的 Python 术语是“猴子补丁”;以下是一些相关信息:

Stack Overflow - 猴子补丁

这样做的一个好处是你的 python 代码是非常动态的;您可以添加字段来自定义类、模块或任何具有属性的 Python 构造。

一个非常聪明的用法是“Bunch”python 配方,它定义了一个可以使用关键字参数构造的通用容器:

一堆食谱

成本在于潜在的维护问题;很难跟踪一个类或模块上次“猴子补丁”的位置。

当然,如果您真的喜欢鸭子类型,那么您的代码使用some_field并且other_field应该try/except确保属性确实存在,并处理这两种情况。

在您的具体示例中,似乎Visitor.most_recent_visits可以初始化为None,或其他一些哨兵。恕我直言,如果您可以将属性初始化为在构造函数中有意义的值,您应该这样做,并且只为极端情况保留“猴子补丁”。此外,库类和函数的猴子补丁似乎很麻烦。

于 2013-04-10T21:28:41.690 回答
0

我有一种冲动在这里引用“在你发出的东西上要保守,在你发出的东西上要自由”的原则。我会考虑将“我自己的”对象(在模块中使用)的设计设置为“一成不变”,但要轻松地对跨越模块边界的对象进行类型检查。使用适配器或其他一些显式模式来扩展内部类的行为总是可行的,而不是临时附加它。

于 2013-04-10T21:29:50.783 回答