python, __slots__, and “attribute is read-only”

戏子无情 提交于 2019-12-02 20:36:03
Ayman Hourieh

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:

  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.

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).

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.

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).

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.

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

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

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

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.

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__.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!