Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python objects - avoiding creation of attribute with unknown name

Wishing to avoid a situation like this:

>>> class Point:
x = 0
y = 0
>>> a = Point()
>>> a.X = 4 #whoops, typo creates new attribute capital x

I created the following object to be used as a superclass:

class StrictObject(object):
    def __setattr__(self, item, value):
        if item in dir(self):
            object.__setattr__(self, item, value)
        else:
            raise AttributeError("Attribute " + item + " does not exist.")

While this seems to work, the python documentation says of dir():

Note: Because dir() is supplied primarily as a convenience for use at an interactive prompt, it tries to supply an interesting set of names more than it tries to supply a rigorously or consistently defined set of names, and its detailed behavior may change across releases. For example, metaclass attributes are not in the result list when the argument is a class.

Is there a better way to check if an object has an attribute?

like image 265
Peter Gillespie Avatar asked Mar 10 '12 11:03

Peter Gillespie


3 Answers

Much better ways.

The most common way is "we're all consenting adults". That means, you don't do any checking, and you leave it up to the user. Any checking you do makes the code less flexible in it's use.

But if you really want to do this, there is __slots__ by default in Python 3.x, and for new-style classes in Python 2.x:

By default, instances of both old and new-style classes have a dictionary for attribute storage. This wastes space for objects having very few instance variables. The space consumption can become acute when creating large numbers of instances.

The default can be overridden by defining __slots__ in a new-style class definition. The __slots__ declaration takes a sequence of instance variables and reserves just enough space in each instance to hold a value for each variable. Space is saved because __dict__ is not created for each instance.

Without a __dict__ variable, instances cannot be assigned new variables not listed in the __slots__ definition. Attempts to assign to an unlisted variable name raises AttributeError. If dynamic assignment of new variables is desired, then add '__dict__' to the sequence of strings in the __slots__ declaration.

For example:

class Point(object):
    __slots__ = ("x", "y")

point = Point()
point.x = 5 # OK
point.y = 1 # OK
point.X = 4 # AttributeError is raised

And finally, the proper way to check if an object has a certain attribute is not to use dir, but to use the built-in function hasattr(object, name).

like image 166
orlp Avatar answered Nov 04 '22 06:11

orlp


I don't think it's a good idea to write code to prevent such errors. These "static" checks should be the job of your IDE. Pylint will warn you about assigning attributes outside of __init__ thus preventing typo errors. It also shows many other problems and potential problems and it can easily be used from PyDev.

like image 39
Jochen Ritzel Avatar answered Nov 04 '22 07:11

Jochen Ritzel


In such situation you should look what the python standard library may offer you. Did you consider the namedtuple?

from collections import namedtuple

Point = namedtuple("Point", "x, y")

a = Point(1,3)

print a.x, a.y

Because Point is now immutable your problem just can't happen, but the draw-back is naturally you can't e.g. just add +1 to a, but have to create a complete new Instance.

x,y = a
b = Point(x+1,y)
like image 24
Don Question Avatar answered Nov 04 '22 07:11

Don Question