我倾向于这样定义我的Python类:
class MyClass(object):
"""my docstring"""
msg = None
a_variable = None
some_dict = {}
def __init__(self, msg):
self.msg = msg
在类似Java的语言中,将对象变量(msg、a_variable等)放在顶部声明好还是坏还是无所谓?我知道这是不必要的,但仍然很有诱惑力。
class MyClass(object):
msg = "ABC"
print MyClass.msg #prints ABC
a = MyClass()
print a.msg #prints ABC
a.msg = "abc"
print a.msg #prints abc
print MyClass.msg #prints ABC
print a.__class__.msg #prints ABC
从上面的代码可以看出,它与定义在类作用域的变量不太相同。虽然可以通过self.msg
访问变量,但当它被赋值时,它并没有被赋值给在类作用域中定义的变量。
通过您所使用的方法进行操作的一个缺点是,它会向类添加隐藏状态,从而可能导致错误。比如说,有人在构造函数中遗漏了self.msg = "ABC"
(或更现实地说,代码被重构,只有一个定义被改变)。
a = MyClass()
print a.msg #prints ABC
#somewhere else in the program
MyClass.msg = "XYZ"
#now the same bit of code leads to a different result, despite the expectation that it
#leads to the same result.
a = MyClass()
print a.msg #prints XYZ
class MyClass(object):
pass
print MyClass.msg #AttributeError: type object 'MyClass' has no attribute 'msg'
MyClass.a_variable
这样使用。但是它们也可以像 self.a_variable
一样使用,这是一个问题,因为天真的程序员可能会将它们视为实例变量。例如,您的 "some_dict" 变量将被 MyClass
的每个实例共享,因此如果您向其中添加键 "k",那么任何实例都可以看到。MyClass
中的初始定义将保留。但无论如何,这都不是好的实践,因为当不重新分配这些变量时,可能会遇到麻烦!class MyClass(object):
"""
Some class
"""
def __init__(self, msg):
self.__msg = msg
self.__a_variable = None
self.__some_dict = {}
使用双下划线表示“私有”变量(伪私有!)是可选的。如果变量应该是公共的,只需保留它们的名称而不带有__
前缀。
msg
属性仍然占用字典中的空间。因此它未被使用,但仍然占用一些内存。class MyClass(object):
msg = 'FeeFiFoFum'
def __init__(self, msg):
self.msg = msg
m=MyClass('Hi Lucy')
'Hi Lucy'
作为值。print(m.__dict__)
# {'msg': 'Hi Lucy'}
m.__class__
访问)仍然具有 FeeFiFoFum
。print(m.__class__.__dict__)
# {'__dict__': <attribute '__dict__' of 'MyClass' objects>, '__module__': '__main__', '__init__': <function __init__ at 0xb76ea1ec>, 'msg': 'FeeFiFoFum', 'some_dict': {}, '__weakref__': <attribute '__weakref__' of 'MyClass' objects>, '__doc__': 'my docstring', 'a_variable': None}
print(m.msg)
# Hi Lucy
print(MyClass.msg)
# FeeFiFoFum
当你声明一个类时,Python会解析它的代码并将所有内容放入该类的命名空间中;然后,该类将用作所有派生对象的一种模板——但任何对象都将拥有其自己的引用副本。
请注意,您总是有一个引用;因此,如果您能够更改所引用的对象,则更改将反映在其被使用的所有位置。但是,成员数据的插槽对于每个实例都是唯一的,因此将其分配给新对象不会反映到任何其他位置。
注意:Michael Foord在他的博客文章中详细介绍了类实例化的工作原理;如果您对此主题感兴趣,我建议您阅读短篇文章。
无论如何,就所有实际用途而言,你的两种方法之间有两个主要区别:
1. 名称已经在类级别可用,您可以在不实例化新对象的情况下使用它;这在命名空间中声明常量时听起来很整洁,但在许多情况下,模块名称可能已经是一个好名称。 2. 名称添加在类级别 - 这意味着您可能无法在单元测试期间轻松地模拟它,并且如果您有任何昂贵的操作,则会在导入的那一刻立即执行。msg
、a_variable
和 some_dict
这样设置类成员肯定会使它们在该类的所有对象之间共享,除非对象的构造函数复制并替换了这些成员。你见过什么不同的例子吗? - Jarret Hardie
None
只是一个站在那里的对象,除非您正在使用它,否则不应该给它打标签。 - cregox