【问题标题】:Python property vs method when no access to attribute is needed?不需要访问属性时的Python属性与方法?
【发布时间】:2013-12-28 16:28:49
【问题描述】:

我正在阅读“绝对初学者的 Python 编程”,其中有一段代码:

@property
def mood(self):
    unhappiness = self.hunger + self.boredom
    if unhappiness < 5:
        m = "happy"
    elif 5 <= unhappiness <= 10:
        m = "okay"
    elif 11 <= unhappiness <= 15:
        m = "frustrated"
    else:
        m = "mad"
    return m

它所做的只是即时计算并返回计算结果。它不提供对私有属性或类的任何属性的访问。这是作为属性而不是方法更好吗?

【问题讨论】:

    标签: python properties


    【解决方案1】:

    属性并非旨在提供对私有属性的访问。属性旨在为您提供使零参数方法可以像属性一样访问的选项,以便给定的“属性”可以实现为计算或实际属性,而无需更改类的接口。

    因此,这通常不是“更好”的问题,而是取决于上下文的优缺点的设计决策。

    在这种情况下,无论这个对象是什么都支持x.hungerx.boredomx.mood。为什么不x.mood()?您可以,尽管这会在界面中永久公开它一个计算并且没有被存储。

    如果该类的先前版本具有“真实”mood 属性,并且对象的必需不变量意味着任何更新 boredomhunger 的内部方法还必须小心地将 mood 设置为一致,那么引入该属性将是一个很好的重构;接口保持不变,但现在保证不变量始终保持不变,而不必仔细维护。整个领域的错误都无法发生。

    另一方面,如果mood 的计算成本很高,或者有副作用,那么作为普通方法可能会好得多。让它看起来像一个属性访问意味着客户端代码程序员可能会认为它是一个属性访问,这是廉价且非破坏性的;这将是错误或性能问题的丰富来源。

    【讨论】:

    • 是的,我确实得出了相同的结论,即这是设计决策,而 mood() 更好,因为它显示了计算。这样人们就知道它没有被存储。
    • @h3dkandi 实际上我的结论(在这种情况下)是最好公开它是一个计算;毕竟抽象接口和封装实现细节是OO的重点!但这只是表明这是一个没有“正确”答案的主观决定。 :)
    • 嗯,是的,我明白你的意思。隐藏起来可能会更好。
    • “便宜又无损”完美
    【解决方案2】:

    这只是口味问题。

    属性用于访问相当便宜的地方,例如仅查询“私有”属性或简单计算。

    一种方法用于发生相当“复杂”的过程并且这个过程是主要的。

    人们习惯使用getter和setter方法,但越来越倾向于使用属性。

    pyserialSerial 类为例。它具有 - 作为传统 - 方法,例如 getBaudRate()setBaudRate(),但建议使用 baudrate 作为读写属性来查询和设置波特率。

    【讨论】:

      【解决方案3】:

      我不认为有任何真正的区别。

      它只允许你使用obj.mood 而不是obj.mood()

      【讨论】:

        【解决方案4】:

        本质上是一种偏好。它允许您输入object.property 而不是object.property()

        那么什么时候应该使用哪个?您必须使用上下文来决定。如果您拥有的方法根据对象的属性返回一个值,它可以节省您创建变量并将其设置为某个生成器方法的时间(例如:property = object.generateProperty()。跳过这个不是更有意义吗?一步,让generateProperty()成为它自己的属性?

        这是我理解的一般概念。

        【讨论】:

        • 我同意。如果 getter 很昂贵并且您经常使用它,我更喜欢该方法并编写变量。
        【解决方案5】:

        实际上有一个很好的理由使用它(我刚刚遇到过)。在你的情况下,它可能看起来做的不多。但是想象一下有两个类:

        class Person(object):
            hunger = 3
            boredom = 5
        
            @property
            def mood(self):
                unhappiness = self.hunger + self.boredom
                if unhappiness < 5:
                    m = "happy"
                elif 5 <= unhappiness <= 10:
                    m = "okay"
                elif 11 <= unhappiness <= 15:
                    m = "frustrated"
                else:
                   m = "mad"
                return m
        
        class Werewolf(object):
            mood = "mad"
        

        在这种情况下,“狼人”总是“疯狂”,但“人”有条件地“疯狂”。

        现在,如果你有一个泛型方法:

        if time == "full moon"
            player = Werewolf
        else:
            player = Person
        

        那么你就可以“放心地”说

        mood_at_this_time = player.mood
        

        无需担心进行方法调用。

        【讨论】:

        • 我明白你想说什么,但不知道你的例子好不好。我会将狼人实现为 Person 的子类。取决于月相(人/狼)的物理形式,以及根据您的示例的情绪。我的意思是狼人在人类形态时仍然是狼人。我认为它是一个“病态”的人:)。我将您的示例视为 A 类和 B 子类,其中计算 A 属性且 B 属性为“常量”
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-09-19
        • 1970-01-01
        • 2021-11-16
        • 2011-08-11
        • 2012-09-25
        • 1970-01-01
        相关资源
        最近更新 更多