Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A Python "catch all" method for undefined/unimplemented attributes in classes

Tags:

python

I love Python's @property decorate system. I love that you can run custom code when you call aClassObect.attribute. Especially for validating data when you're setting an attribute. However, one thing that I want, but I can't find, is a way to run custom code when trying to set an attribute that doesn't exist. For example, say I've got the following class:

class C(object):
    def __init__(self):
        self._x = None

    @property
    def x(self):
        """I'm the 'x' property."""
        return self._x

    @x.setter
    def x(self, value):
        self._x = value

    @x.deleter
    def x(self):
        del self._x

Now, if I have myObj, which is an instance of class C, and I call myObject.x = 42, it will run the appropriate setter, which is great for validating data. But this doesn't stop someone from calling myOjbect.b = 47, and it will happily create a new attribute called b. Is there some way to run special code when setting a new attribute? I'd the ability to raise errors to say something like "error, this attribute doesn't exist".

like image 354
J-bob Avatar asked Apr 26 '13 02:04

J-bob


2 Answers

To elaborate a bit on Elazar's answer, you'll want to override the __setattr__ magic method and do a check in it to see if the attribute already exists. If not, raise an exception. Here's what that could look like for your class:

def __setattr__(self, name, value):
    if not hasattr(self, name): # would this create a new attribute?
        raise AttributeError("Creating new attributes is not allowed!")
    super(C, self).__setattr__(name, value)

It's also possible to override __getattr__ or __getattribute__ if you want to deal with requests for non-existent attributes, but for the specific issue you have described this is probably not necessary.

Note that the __setattr__ method I show above won't entirely play nicely with your current property, due to the deleter. If you delete myObj.x, afterwards getter will raise an exception if you try to access x later. This means that hasattr will return False when checking if x is an existing attribute, and so my __getattr__ won't let you recreate it. If you really need to be able to delete (and recreate) specific attributes, you'll need a more sophisticated __setattr__ implementation (it could check in the class for a property with the desired name, rather than just relying on hasattr).

like image 59
Blckknght Avatar answered Nov 20 '22 00:11

Blckknght


override __getattr__ and __setattr__.

like image 38
Elazar Avatar answered Nov 20 '22 01:11

Elazar