0
votes

The __dict__ attribute of class instances is very useful for mapping a given dict object to a class instance (the keys of the dict becoming in the process the class instance attributes).

Example:

>>> class SomeClass(object):
...     def __init__(self):
...         self.numbers = [1, 3, 5, 7, 9]
... 
>>> inst = SomeClass()
>>> inst.__dict__
{'numbers': [1, 3, 5, 7, 9]}
>>> inst.__dict__.update({
...     'letters': ['a', 'b', 'c', 'd'],
...     'numbers': [2, 4, 6, 8, 10]
...     })
>>> inst.letters, inst.numbers
(['a', 'b', 'c', 'd'], [2, 4, 6, 8, 10])

However, this doesn't work if class attributes are declared as follows:

>>> class SomeClass(object):
...     numbers = [1, 3, 5, 7, 9]
... 
>>> inst = SomeClass()
>>> inst.__dict__
{}

Is there a way to have access to them in this case ? I mean, is there a way to list them independently of the other special class attributes, such as methods, or defaults attributes ?

3

3 Answers

3
votes

Those are class attributes, not instance attributes. Class attributes are shared between instances. This is also how instances inherit the methods of their class. Instance attributes with the same name shadow class attributes, so you can set numbers on the instance to give the instance it's own copy.

You can use dir() on an instance to list a combined view of instance and class attribute names:

>>> inst = SomeClass()
>>> dir(inst)
['__class__', '__delattr__', '__dict__', '__doc__', '__format__', '__getattribute__', '__hash__', '__init__', '__module__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', '__weakref__', 'numbers']

You can use inspect.getmembers() to filter on the type of attribute.

Another option is to list the class attributes with __dict__:

>>> vars(type(inst))
dict_proxy({'__dict__': <attribute '__dict__' of 'SomeClass' objects>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'SomeClass' objects>, 'numbers': [1, 3, 5, 7, 9], '__doc__': None})
>>> {k: v for k, v in vars(type(inst)).iteritems() if not k.startswith('__') and not inspect.isfunction(v)}
{'numbers': [1, 3, 5, 7, 9]}

From the Class definitions documentation:

Variables defined in the class definition are class attributes; they are shared by instances. Instance attributes can be set in a method with self.name = value. Both class and instance attributes are accessible through the notation “self.name”, and an instance attribute hides a class attribute with the same name when accessed in this way.

1
votes

My advice is that you should avoid modifying __dict__, locals() and so on.

Why not explicitly store the attributes that you're expecting to be dynamic in a dictionary? That way you don't end up having a bunch of objects around with attributes you weren't expecting because they weren't defined in the class and were monkey-patched by code somewhere else.

I think this is a lot clearer and less scary:

class MyClass(object):
    classattrs = {'numbers': [1,2,3,4,5]}

MyClass.classattrs['letters'] = ['a','b','c']

It boils down to the fact that people expect the contents of a dict to change. They don't expect the attributes of a class to change.

In any case, inspect, dir and their ilk will have trouble filtering out all the other class attributes like special methods and so on.

This idea of keeping names and attributes (reasonably) static has been expressed by people a lot cleverer and more experienced than me: http://nedbatchelder.com/blog/201112/keep_data_out_of_your_variable_names.html

1
votes

If you define numbers as a class attribute,

class SomeClass(object):
    numbers = [1, 3, 5, 7, 9]

then it is put in SomeClass.__dict__:

In [5]: SomeClass.__dict__
Out[5]: dict_proxy(
{'__dict__': <attribute '__dict__' of 'SomeClass' objects>,
 '__module__': '__main__',
 '__weakref__': <attribute '__weakref__' of 'SomeClass' objects>,
 'numbers': [1, 3, 5, 7, 9],
 '__doc__': None})