我想在 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
是否有聪明的程序员可以抽出一点时间来启发我有关“只读”错误的信息?
当您使用
__slots__
声明实例变量时,Python 将创建一个 描述符对象 作为同名的类变量。在您的情况下,此描述符将被您在以下行定义的类变量 m
覆盖:
m = None # my attribute
您需要做的是:不要定义名为
m
的类变量,并在 m
方法中初始化实例变量 __init__
。
class MyClass(object):
__slots__ = ("m",)
def __init__(self):
self.m = None
a = MyClass()
a.m = "?"
顺便说明一下,具有单个元素的元组需要在元素后面加一个逗号。两者都可以在您的代码中使用,因为
__slots__
接受单个字符串或可迭代/字符串序列。一般来说,要定义包含元素 1
的元组,请使用 (1,)
或 1,
而不是 (1)
。
你完全误用了
__slots__
。它阻止为实例创建 __dict__
。仅当您遇到许多小对象的内存问题时,这才有意义,因为摆脱 __dict__
可以减少占用空间。这是一项硬核优化,在 99.9% 的情况下都不需要。
如果您需要您所描述的那种安全性,那么 Python 确实是错误的语言。最好使用像 Java 这样严格的东西(而不是尝试用 Python 编写 Java)。
如果您自己无法弄清楚为什么类属性会在代码中导致这些问题,那么也许您应该三思而后行地引入这样的语言黑客。首先熟悉这门语言可能会更明智。
为了完整起见,这里是 插槽的文档链接。
__slots__
适用于实例变量,而您拥有的是类变量。 这就是你应该这样做的方式:
class MyClass( object ) :
__slots__ = ( "m", )
def __init__(self):
self.m = None
a = MyClass()
a.m = "?" # No error
考虑一下。
class SuperSafe( object ):
allowed= ( "this", "that" )
def __init__( self ):
self.this= None
self.that= None
def __setattr__( self, attr, value ):
if attr not in self.allowed:
raise Exception( "No such attribute: %s" % (attr,) )
super( SuperSafe, self ).__setattr__( attr, value )
更好的方法是使用单元测试进行此类检查。 这是相当大的运行时开销。
class MyClass( object ) :
m = None # my attribute
这里的
m
是类属性,而不是实例属性。您需要在__init__
中自行将其与您的实例连接。
这个问题早已得到解答,但我想我应该再添加一条注释,以防有人像我一样在搜索中遇到这个问题。自 2016 年提出此问题以来,Python 的错误报告已经不断发展,现在将失败并提供更多帮助:
ValueError: 'm' in __slots__ conflicts with class variable