2009-08-09 161 views
12

我有兴趣听到关于Python中类属性的一些讨论。例如,什么是类属性的良好用例?大多数情况下,我不能想出一个类属性优于使用模块级属性的情况。如果这是真的,那么为什么他们呢?Python类与模块属性

我与他们的问题是,它几乎太容易错误地破坏类属性值,然后你的“全局”值已经变成本地实例属性。

欢迎你将如何处理以下情况发表意见:

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

几个相关文章:
Difference Between Class and Instance Attributes

+0

应该是社区维基 – SilentGhost 2009-08-09 09:18:32

回答

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,...”很快就厌倦了。

2

类属性经常被用来允许在子类中压倒一切的默认值。例如,BaseHTTPRequestHandler具有类常量sys_version和server_version,后者默认为"BaseHTTP/" + __version__。 SimpleHTTPRequestHandler覆盖server_version到"SimpleHTTP/" + __version__

+0

但是,如果这有效或无效,类属性的访问方法将是一个因素。 Class属性危险的另一个原因。 – cmcginty 2009-08-09 10:19:05

+0

什么“访问方法”? cls.version,self.version,getattr,都会给出正确的答案。 *在类中定义以允许覆盖*与数据一样适用于代码 - 所以,凯西,为什么你认为这对数据是危险的,但对代码来说很好? – 2009-08-09 15:27:45

+0

通过在访问属性的方法中使用Class.value而不是self.value将防止发生重写。有时这是所需的情况,其他时间则不是。 – cmcginty 2009-08-09 21:15:34

1

封装是一个很好的原则:当一个属性位于类内部时,它属于全局范围内的属性,这为读取代码的人提供了额外的信息。

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

3

什么是一个很好的用例类属性

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

案例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__取决于语义。
  • 全球数据结构---如果使用只有类,但也可以去模块,在任何情况下必须是非常良好的文档。