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

时间:2022-09-11 20:59:10

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?

是否有任何明智的程序员可以节省一些时间并让我了解“只读”错误?

5 个解决方案

#1


When you declare instance variables using __slots__, Python creates a descriptor object as a class variable with the same name. In your case, this descriptor is overwritten by the class variable m that 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 m in the __init__ method.

以下是您需要做的事情:不要定义名为m的类变量,并在__init__方法中初始化实例变量m。

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,)或1,而不是(1)。

#2


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.

为了完整起见,这里是插槽的文档链接。

#3


__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

#4


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.

更好的方法是使用单元测试进行此类检查。这是一个相当大的运行时间开销。

#5


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

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

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

#1


When you declare instance variables using __slots__, Python creates a descriptor object as a class variable with the same name. In your case, this descriptor is overwritten by the class variable m that 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 m in the __init__ method.

以下是您需要做的事情:不要定义名为m的类变量,并在__init__方法中初始化实例变量m。

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,)或1,而不是(1)。

#2


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.

为了完整起见,这里是插槽的文档链接。

#3


__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

#4


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.

更好的方法是使用单元测试进行此类检查。这是一个相当大的运行时间开销。

#5


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

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

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