Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python: Can a class forbid clients setting new attributes?

Tags:

python

oop

typing

I just spent too long on a bug like the following:

>>> class Odp():
    def __init__(self):
        self.foo = "bar"


>>> o = Odp()
>>> o.raw_foo = 3 # oops - meant o.foo

I have a class with an attribute. I was trying to set it, and wondering why it had no effect. Then, I went back to the original class definition, and saw that the attribute was named something slightly different. Thus, I was creating/setting a new attribute instead of the one meant to.

First off, isn't this exactly the type of error that statically-typed languages are supposed to prevent? In this case, what is the advantage of dynamic typing?

Secondly, is there a way I could have forbidden this when defining Odp, and thus saved myself the trouble?

like image 850
Nick Heiner Avatar asked Jun 29 '10 02:06

Nick Heiner


People also ask

Can classes have attributes Python?

Python class attributes are variables of a class that are shared between all of its instances. They differ from instance attributes in that instance attributes are owned by one specific instance of the class only, and are not shared between instances.

Can class attributes be changed in Python?

But be careful, if you want to change a class attribute, you have to do it with the notation ClassName. AttributeName. Otherwise, you will create a new instance variable.

How do I add an attribute to a class in Python?

Adding attributes to a Python class is very straight forward, you just use the '. ' operator after an instance of the class with whatever arbitrary name you want the attribute to be called, followed by its value.

What is the difference between class attributes and instance attributes?

Class attributes are the variables defined directly in the class that are shared by all objects of the class. Instance attributes are attributes or properties attached to an instance of a class. Instance attributes are defined in the constructor. Defined directly inside a class.


1 Answers

You can implement a __setattr__ method for the purpose -- that's much more robust than the __slots__ which is often misused for the purpose (for example, __slots__ is automatically "lost" when the class is inherited from, while __setattr__ survives unless explicitly overridden).

def __setattr__(self, name, value):
  if hasattr(self, name):
    object.__setattr__(self, name, value)
  else:
    raise TypeError('Cannot set name %r on object of type %s' % (
                    name, self.__class__.__name__))

You'll have to make sure the hasattr succeeds for the names you do want to be able to set, for example by setting the attributes at a class level or by using object.__setattr__ in your __init__ method rather than direct attribute assignment. (To forbid setting attributes on a class rather than its instances you'll have to define a custom metaclass with a similar special method).

like image 52
Alex Martelli Avatar answered Sep 30 '22 10:09

Alex Martelli