Python类与模块属性

时间:2009-08-09 06:04:50

标签: python attributes class-design module

我有兴趣听一些关于Python中类属性的讨论。例如,类属性的一个很好的用例是什么?在大多数情况下,我无法想出一个类属性比使用模块级属性更可取的情况。如果这是真的,那为什么要把它们放在一边?

我遇到的问题是,错误地破坏类属性值几乎太容易了,然后你的“全局”值变成了本地实例属性。

随时评论您将如何处理以下情况:

  1. 类和/或子类使用的常量值。这可能包括“幻数”字典键或列表索引,它们永远不会改变,但可能需要一次性初始化。
  2. 默认类属性,在极少数情况下为类的特殊实例更新。
  3. 全局数据结构,用于表示所有实例之间共享的类的内部状态。
  4. 初始化许多默认属性的类,不受构造函数参数的影响。
  5. 一些相关帖子:
    Difference Between Class and Instance Attributes

4 个答案:

答案 0 :(得分:7)

#4: 我从不使用类属性来初始化默认实例属性(通常放在__init__中的属性)。例如:

class Obj(object):
    def __init__(self):
        self.users = 0

永远不会:

class Obj(object):
    users = 0

为什么呢?因为它不一致:当你分配除了不变对象之外的任何东西时它不会做你想要的东西:

class Obj(object):
    users = []

导致用户列表在所有对象之间共享,在这种情况下不需要。根据它们的类型将它们分成__init__中的类属性和赋值是很困惑的,所以我总是将它们全部放在__init__中,无论如何我都会更清楚。


至于其余部分,我通常会在类中添加特定于类的值。这不是因为全局变量是“邪恶的” - 它们并不像某些语言那么大,因为它们仍然是模块化的,除非模块本身太大 - 但是如果是外部代码想要访问它们,将所有相关值放在一个地方是很方便的。例如,在module.py中:

class Obj(object):
    class Exception(Exception): pass
    ...

然后:

from module import Obj

try:
    o = Obj()
    o.go()
except o.Exception:
    print "error"

除了允许子类更改值(无论如何总是不需要),这意味着我不必费力地导入异常名称和使用Obj所需的一些其他东西。 “从模块导入Obj,ObjException,......”很快就会很烦人。

答案 1 :(得分:4)

  

什么是类属性的好用例

案例0。类方法只是类属性。这不仅仅是技术上的相似性 - 您可以通过为它们分配callables来在运行时访问和修改类方法。

案例1。模块可以轻松定义多个类。将关于class A的所有内容封装到A...以及将class B的所有内容封装到B...中是合理的。例如,

# module xxx
class X:
    MAX_THREADS = 100
    ...

# main program
from xxx import X

if nthreads < X.MAX_THREADS: ...

案例2。此类具有许多可在实例中修改的默认属性。这里将属性保留为“全局默认”的能力是一个特征,而不是bug。

class NiceDiff:
    """Formats time difference given in seconds into a form '15 minutes ago'."""

    magic = .249
    pattern = 'in {0}', 'right now', '{0} ago'

    divisions = 1

    # there are more default attributes

一个人创建NiceDiff的实例来使用现有的或稍微修改过的格式,但是一个不同语言的定位器将类子类化为以一种根本不同的方式实现某些功能重新定义常量:

class Разница(NiceDiff): # NiceDiff localized to Russian
    '''Из разницы во времени, типа -300, делает конкретно '5 минут назад'.'''

    pattern = 'через {0}', 'прям щас', '{0} назад'

您的案例

  • 常数 - 是的,我把它们放到了课堂上。说self.CONSTANT = ...很奇怪,所以我没有看到破坏它们的巨大风险。
  • 默认属性 - 混合,如上所述可能会转到类,但也可能会转到__init__,具体取决于语义。
  • 全局数据结构---如果在课堂上使用,则会上课,但也可以转到模块,在任何一种情况下都必须非常记录良好。

答案 2 :(得分:2)

类属性通常用于允许覆盖子类中的默认值。例如,BaseHTTPRequestHandler具有类常量sys_version和server_version,后者默认为"BaseHTTP/" + __version__。 SimpleHTTPRequestHandler将server_version覆盖为"SimpleHTTP/" + __version__

答案 3 :(得分:1)

封装是一个很好的原则:当一个属性在它所属的类中而不是在全局范围内时,这为阅读代码的人提供了额外的信息。

在你的情况1-4中,我会尽可能地避免使用全局变量,并且更喜欢使用类属性,这样就可以从封装中受益。