python、__slots__ 和“属性是只读的”

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/820671/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-11-03 20:54:54  来源:igfitidea点击:

python, __slots__, and "attribute is read-only"

pythonexceptiondescriptorreadonly-attribute

提问by grigoryvp

I want to create an object in python that has a few attributes and I want to protect myself from accidentally using the wrong attribute name. The code is as follows:

我想在 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

But after running this simple code, I get a very strange error:

但是在运行这个简单的代码之后,我得到了一个非常奇怪的错误:

Traceback (most recent call last):
  File "test.py", line 8, in <module>
    a.m = "?"
AttributeError: 'test' object attribute 'm' is read-only

Is there any wise programmer who can spare a bit of their time and enlighten me about "read-only" errors?

有没有聪明的程序员可以抽出一点时间来启发我有关“只读”错误的信息?

回答by Ayman Hourieh

When you declare instance variables using __slots__, Python creates a descriptor objectas a class variable with the same name. In your case, this descriptor is overwritten by the class variable mthat you are defining at the following line:

当您使用 声明实例变量时__slots__,Python 会创建一个描述符对象作为同名的类变量。在您的情况下,此描述符被m您在以下行定义的类变量覆盖:

  m = None # my attribute

Here is what you need to do: Do not define a class variable called m, and initialize the instance variable min the __init__method.

这里是你需要做的:不要定义一个名为 的类变量m,并m__init__方法中初始化实例变量。

class MyClass(object):
  __slots__ = ("m",)
  def __init__(self):
    self.m = None

a = MyClass()
a.m = "?"

As a side note, tuples with single elements need a comma after the element. Both work in your code because __slots__accepts a single string or an iterable/sequence of strings. In general, to define a tuple containing the element 1, use (1,)or 1,and not (1).

作为旁注,具有单个元素的元组在元素后需要一个逗号。两者都在您的代码中工作,因为__slots__接受单个字符串或可迭代/字符串序列。通常,要定义包含元素的元组1,请使用(1,)or1,和 not (1)

回答by nikow

You are completely misusing __slots__. It prevents the creation of __dict__for the instances. This only makes sense if you run into memory problems with many small objects, because getting rid of __dict__can reduce the footprint. This is a hardcore optimization that is not needed in 99.9% of all cases.

你完全在滥用__slots__. 它可以防止__dict__为实例创建。这仅在您遇到许多小对象的内存问题时才有意义,因为摆脱__dict__可以减少占用空间。这是 99.9% 的情况下不需要的核心优化。

If you need the kind of safety you described then Python really is the wrong language. Better use something strict like Java (instead of trying to write Java in Python).

如果您需要您描述的那种安全性,那么 Python 确实是错误的语言。最好使用像 Java 这样严格的东西(而不是尝试用 Python 编写 Java)。

If you couldn't figure out yourself why the class attributes caused these problems in your code then maybe you should think twice about introducing language hacks like this. It would probably be wiser to become more familiar with the language first.

如果您无法弄清楚为什么类属性会在您的代码中导致这些问题,那么也许您应该在引入这样的语言技巧时三思而后行。首先更熟悉该语言可能会更明智。

Just for completeness, here is the documentation link for slots.

为了完整起见,这里是slot文档链接

回答by RichieHindle

__slots__works with instance variables, whereas what you have there is a class variable. This is how you should be doing it:

__slots__使用实例变量,而您拥有的是一个类变量。这就是你应该这样做的方式:

class MyClass( object ) :
  __slots__ = ( "m", )
  def __init__(self):
    self.m = None

a = MyClass()
a.m = "?"       # No error

回答by S.Lott

Consider this.

考虑一下。

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 )

A better approach is to use unit tests for this kind of checking. This is a fair amount of run-time overhead.

更好的方法是使用单元测试进行这种检查。这是相当数量的运行时开销。

回答by pinseng

class MyClass( object ) :
    m = None # my attribute

The mhere is the class attributes, rather than the instance attribute. You need to connect it with your instance by self in __init__.

m这里是类属性,而不是实例属性。您需要通过 self in 将其与您的实例连接__init__