【问题标题】:Is adding attributes dynamically frowned upon in Python?在 Python 中动态添加属性是否令人不悦?
【发布时间】:2011-12-17 10:37:20
【问题描述】:

在 Python 中,您可以从定义类之外分配一个任意属性

class Profile(models.Model):
    user = models.OneToOneField(User)  
    name = models.CharField(max_length=140)

p = Profile()
p.age = 42

这里的底层机制是__dict__ 属性,它维护着一个包含所有属性的字典。

我们都被告知not to expose our inner workings to the client code,但附加数据根本与封装无关,对吧?这个成语在 Python 代码中很常见吗?


就是我的意思……

每个Tweet 都有标准字段,例如idtextowner
在为用户返回推文列表时,您希望显示推文是否被该用户“收藏”。

显然,要获取is_favorite,您需要查询此user 的多对多关系。
是否可以用当前用户对应的is_favorite 预填充Tweet 对象

当然我可以公开一个方法is_favorite_for(user),但我遇到了Django template language 限制,它不允许从模板内部调用带有参数的方法。另外,我认为模板根本不应该调用方法


我知道这会工作很好,但我想知道在开源项目中做这样的事情是否会让其他开发人员鄙视我。

旁注:

我来自 C#/.NET 背景,其中动态类型是最近才引入的,除了一些小众领域(互操作性、IoC 框架、REST 客户端框架等)外,并没有得到广泛应用。

【问题讨论】:

    标签: python oop encapsulation dynamic-languages design-principles


    【解决方案1】:

    我认为这是一种不好的做法。

    对象不知道你在弄乱它的属性。例如,考虑一下,如果 Profile 稍后扩展为具有一个名为 age 的属性,该属性与您的代码中的 p.age 无关。

    如果您想添加属性,为什么不将Profile 子类化,或者将Profiles 的外部映射到具有您的自定义属性的对象?

    【讨论】:

    • 虽然子类化也有同样的陷阱
    • @Owen:是的。 IMO 的主要区别在于,通过子类化,问题可能更容易定位,因为它位于基类和子类之间。在外部添加属性意味着问题代码可以在代码库中的任何位置
    • 我稍微解释了一下原因,我认为Tweetis_favorite 是比Personage 更好的例子。在这种情况下,子类化没有多大意义,对吧?
    • @DanAbramov:我没有资格评论 Django 的局限性,但在普通 Python 中,我可能会将用户的收藏夹放入 set,而不是将 is_favourite 设为 Tweet 的属性.
    • 这是有道理的。如果获取favorites 太冗长(在我的真实应用程序中,有两层多对多关系),我可以将它们提取到代码中的列表中并将它们传递给模板,而不是使@987654334 @ 一个属性。
    【解决方案2】:

    我认为答案是:视情况而定。首先,如果你真的想阻止它,你可以通过在类中定义__slots__。而且添加未在类中实际定义的属性通常不是一个好习惯,因为它可能会使阅读的人感到困惑 代码很少有用。

    但在某些时候,能够做到这一点是很有用的,Python 文档将其作为一种获得类似于 C 结构或 Pascal 记录的方法(请参阅第 9.7 节中的 http://docs.python.org/tutorial/classes.html。)

    【讨论】:

      【解决方案3】:

      如果该属性只是有时存在,您可能会冒着为一个对象获取AttributeError 的风险,而代码对于同一类的另一个对象工作正常(是的,确切的类型在鸭子打字时并不那么重要,但同一类的对象经常被认为是相同的“鸭子类型”)。即使它没有发生,您也无法仅通过查看部分代码来确定,并且无论如何都难以检查。因此,这样做只会降低您的代码的可靠性。

      然后可以选择提供默认属性作为类属性或属性,仅在与默认值不同时分配对象属性。但是对于预计会因对象而异的内容,__init__ 中列出的每个属性的清晰性通常超过延迟实例属性访问的任何潜在优势。

      这并不是说它不可接受,但您必须提出令人信服的论据才能将其视为一个好主意。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2012-12-12
        • 2012-10-03
        • 2017-06-30
        • 2020-01-18
        • 1970-01-01
        • 2018-04-11
        • 2012-05-18
        相关资源
        最近更新 更多