6

Please consider the following python example:

In [3]: class test(object):
   ...:     attribute='3'
   ...:     def __init__(self):
   ...:         self.other='4'
   ...:         

In [4]: b=test()

In [5]: b.attribute
Out[5]: '3'

In [6]: b.__dict__
Out[6]: {'other': '4'}

Why is it that __dict__ only shows the "other" attribute and not "atribute"?

And how do I get a dictionary with all the classe's attributes and values? That is, how do I get this?

{'other': '4', 'attribute': '3'}

And I mean by using __dict__ or by some other simple means.

PS: related to this question, but couldn't quite get a dict from there.

PS2: I'm not look for test.__dict__ or b.__class__.__dict__, I'm looking for something that can be used as

In [3]: class test(object):
   ...:     attribute='3'
   ...:     def __init__(self):
   ...:         self.other='4'
   ...:     def _print_atr(self):
   ...:         # This should print exactly {'other': '4', 'attribute': '3'}
   ...:         print(self.__all_atr__)

In [4]: b=test()

In [5]: b.attribute
Out[5]: '3'

In [6]: b.__dict__
Out[6]: {'other': '4'}

Cheers

Community
  • 1
  • 1
TomCho
  • 3,204
  • 6
  • 32
  • 83
  • Can you elaborate on what you mean by PS2? Why does your hypothetical `__all_atr__` exclude `__init__` and `_print_atr` but it _does_ include `attribute`? – mgilson Jul 13 '16 at 22:34
  • @mgilson yes, sorry if that wasn't clear, that's exactly what it would do. It would print out my excepted example: `{'other': '4', 'attribute': '3'}`. – TomCho Jul 13 '16 at 22:42
  • But _why_? What are the rules for excluding some things and not others? – mgilson Jul 13 '16 at 23:26
  • @mgilson The idea is to consider both `b`'s attributes and `b`'s class attributes as attributes of `b`, while still maintaining python's default of "excluding" attributes that start with an underscore. But maybe this is a stupid way to do it to begin with. Do you believe I should delete the question? – TomCho Jul 14 '16 at 13:53
  • 1
    No, I don't think you should necessarily delete the question. If you just want to filter "dunder" attributes/methods, you could easily filter the dictionary from my answer: `d = {k: v for k, v in d.items() if not (k.startswith('__') and k.endswith('__')}`. But I suppose it's also worth asking _why_ you want to do this. What is the end goal? – mgilson Jul 14 '16 at 16:48

3 Answers3

6

attribute is not an instance attribute but a class attribute (can be seen in the mappingproxy test.__dict__).

You can get attribute in the instance __dict__ if you update the value of attribute from the instance:

>>> b = test()
>>> b.__dict__
{'other': '4'}
>>> b.attribute
'3'
>>> b.attribute = 5
>>> b.__dict__
{'attribute': 5, 'other': '4'}

Or keep the original value with

>>> b.attribute  = b.__class__.attribute # may not be necessary

Or you could change the definition of the class and move attribute into one of the class methods and bind it to the instance via self.

Moses Koledoye
  • 77,341
  • 8
  • 133
  • 139
  • 1
    +1 for this special mention: "You can get attribute in the instance __dict__ if you update the value of attribute from the instance" – Taranjeet Singh Aug 04 '17 at 12:49
3

b.__dict__ is only a mapping of attributes on b, not on b's class (notice that __init__ isn't there either). The attributes on b's class are on the class's __dict__.

>>> class test(object):
...   attribute = 1
...   def __init__(self):
...     self.other = 2
... 
>>> b = test()
>>> b.__dict__
{'other': 2}
>>> test.__dict__
dict_proxy({'__module__': '__main__', 'attribute': 1, '__dict__': <attribute '__dict__' of 'test' objects>, '__weakref__': <attribute '__weakref__' of 'test' objects>, '__doc__': None, '__init__': <function __init__ at 0x1030f72a8>})

If you want both, you can do something like:

d = dict(vars(type(b)))
d.update(vars(b))

(note that some prefer vars(b) to b.__dict__) Of course, this doesn't get subclasses ...

If you want subclasses, you'll need to walk the method resolution order...

d = {}
for cls in type(b).__mro__:
    d.update(vars(cls))
d.update(vars(b))
mgilson
  • 300,191
  • 65
  • 633
  • 696
  • 1
    I get `AttributeError: 'dictproxy' object has no attribute 'update'` when running update on `d`. Also, please see my edit, specifically "PS2". – TomCho Jul 13 '16 at 22:31
1

Try typing:

test.__dict__

And it shows a key with 'attribute'. This happens exactly because attribute is a class variable not an instance variable.

  • 1
    Thats the same as `b.__class__.__dict__`, but that doesn't really help as much. Please check my edit in about 5 minutes – TomCho Jul 13 '16 at 22:25