因此,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_visit
将引发AttributeError
的事实,除非有人之前曾调用summarize_visits
同一个对象。好像它会导致很多if hasattr(...)
块,没有?
答案 0 :(得分:2)
编写这样的代码实际上是Python的最大好处之一。我的经验法则是仅在内部使用特定于实例的字段(即,在一个函数内,并且仅在必要时),并且不期望它们被外部模块使用。
如果我的对象被另一个人所使用,我希望他们能够查看课程定义并找到他们需要知道的关于其结构在一个地方清楚描述的所有内容。请记住,explicit is better than implicit
。
答案 1 :(得分:1)
这通常很方便,但我发现你担心在与其他开发人员合作时会导致混淆。您可以通过定义__slots__
变量来阻止人们向类中添加任意值,如所讨论的here。这将迫使人们明确他们想要在对象中的属性,这可以帮助避免混淆。
答案 2 :(得分:0)
我认为Python这个术语是“猴子修补”;这里有一些相关的信息:
Stack Overflow - monkey patching
这样做的好处是你的python代码非常动态;您可以添加字段来自定义具有属性的类,模块或任何Python构造。
一个非常巧妙的用法是“Bunch”python配方,它定义了一个可以使用关键字参数构建的通用容器:
成本是潜在的维护问题;很难追踪班级或模块最后一次“猴子补丁”。
当然,如果您真的喜欢打字,那么使用some_field
和other_field
的代码应该try/except
来确保属性确实存在,并处理这两个属性例。
在您的具体示例中,似乎Visitor.most_recent_visits
可以初始化为None
或其他一些哨兵。恕我直言,如果你可以将一个属性初始化为一个在构造函数中有意义的值,你应该这样做,并且只为极端情况保留“monkey-patching”。另外,对类库和函数进行猴子修补似乎很麻烦。
答案 3 :(得分:0)
我迫切希望在这里引用“你所散发的东西保守,在你所散发的东西中保持自由”的原则。我会考虑将“我自己的”对象(在模块中使用)设计为“一成不变”,但要轻松检查跨越模块边界的对象。使用适配器或其他显式模式扩展内部类的行为总是可行的,而不是附加它.- / p>