python、__slots__和“属性是只读的”
我想在Python中创建一个对象,这个对象有一些属性,我希望能保护自己,避免不小心用错属性名。我的代码是这样的:
class MyClass( object ) :
m = None # my attribute
__slots__ = ( "m" ) # ensure that object has no _m etc
a = MyClass() # create one
a.m = "?" # here is a PROBLEM
但是运行这段简单的代码后,我遇到了一个很奇怪的错误:
Traceback (most recent call last):
File "test.py", line 8, in <module>
a.m = "?"
AttributeError: 'test' object attribute 'm' is read-only
有没有哪位聪明的程序员能花点时间告诉我关于“只读”错误的事情呢?
5 个回答
7
__slots__
是用来管理实例变量的,而你提到的那个是类变量。你应该这样做:
class MyClass( object ) :
__slots__ = ( "m", )
def __init__(self):
self.m = None
a = MyClass()
a.m = "?" # No error
11
你完全误用了 __slots__
。它会阻止为实例创建 __dict__
。这样做只有在你遇到很多小对象导致内存问题时才有意义,因为去掉 __dict__
可以减少内存占用。这是一种极端的优化,在99.9%的情况下都不需要。
如果你需要你所描述的那种安全性,那么Python真的不是合适的语言。最好使用一些严格的语言,比如Java(而不是试图在Python中写Java的代码)。
如果你自己都搞不清楚为什么类属性在你的代码中造成了这些问题,那么也许你应该再考虑一下引入这样的语言技巧。可能更明智的做法是先更熟悉这门语言。
为了完整起见,这里有一个关于 slots
的 文档链接。
49
当你使用 __slots__
来声明实例变量时,Python 会创建一个和你声明的变量同名的描述符对象,作为类变量。在你的例子中,这个描述符被你在下一行定义的类变量 m
覆盖了:
m = None # my attribute
你需要做的是:不要定义一个叫 m
的类变量,而是在 __init__
方法中初始化实例变量 m
。
class MyClass(object):
__slots__ = ("m",)
def __init__(self):
self.m = None
a = MyClass()
a.m = "?"
顺便提一下,单元素的元组在元素后面需要加个逗号。你的代码中这两种写法都可以,因为 __slots__
可以接受一个字符串或者一个字符串的可迭代对象/序列。一般来说,要定义一个包含元素 1
的元组,应该用 (1,)
或者 1,
,而不是 (1)
。