tags:

views:

560

answers:

4

Hello

I want to create an object in python that have a few attributes and i want to protect myself from accidently using wrong attribute name. The code is following:

class MyClass( object ) :
  m = None # my attribute
  __slots__ = ( "m" ) # ensure that object has no _m etc

a = test() # create one
a.m = "?"  # here is a PROBLEM

But after running this simple code i got 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

Any wise programmer who can spare a bit of his time and enlighten me about "read-only" error?

+2  A: 

__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
RichieHindle
+11  A: 

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

Ayman Hourieh
Do you know why if i wrote:__slots__ = [ "m" ]m = 5Python will overwrite descriptor object with variable and will not use descriptor objects' set() method to set value instead?
Eye of Hell
Because m in this context is a class variable, not an instance variable. In order to utilize the descriptor's set() method, you should assign to the m of an object, not a class.If you want to initialize an instance variable of an object, do it from within the __init__ method as I did in my code snippet.
Ayman Hourieh
+2  A: 

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.

S.Lott
+1 for not abusing __slots__
Ravi
But this is more lines of code compared to __slots__? Why manually do check what can be automatically performed by language.
Eye of Hell
To avoid "very strange error" messages and to avoid having to spend a lot of time debugging inner mysteries of __slots__. I prefer obvious and simple solutions where secret knowledge is not required to debug.
S.Lott
Not being able to add new instance vars is a side-effect of __slots__ not the purpose. (which is a performance optimization).
Ravi
@Ravi — And that, I think, is the heart of the problem.
Ben Blank
+5  A: 

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.

nikow
+1 for completeness. As mentioned all over the place, __slots__ is not a bad idea, but requires careful usage. It is more than a safety check and adds much complexity to the implementation that will require to pay attention to how you use the object.
David Berger