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:
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?
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)
.
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__
.
来源:https://stackoverflow.com/questions/820671/python-slots-and-attribute-is-read-only