I'm new to OOP, but I'm trying to look at an object's vars. Other Stack-O answers have suggested using object.__dict__
or vars(object)
. So I went into the Python shell to try a quick example, but I noticed neither of these answers prints the object's default attributes, only newly-assigned attributes, e.g.:
>>> class Classy():
... inty = 3
... stringy = "whatevs"
...
>>> object = Classy()
>>> object.inty
3
>>> object.__dict__
{}
>>> vars(object)
{}
>>> object.inty = 27
>>> vars(object)
{'inty': 27}
>>> object.__dict__
{'inty': 27}
Why are the variables present in one sense but not another? Is it because I didn't explicitly initialize them or something?
It's important understanding that in Python everything is an object (including functions, and a class
declaration itself)
When you do this:
class Classy():
inty = 3
stringy = "whatevs"
You're assigning inty
and stringy
to the Class
, not to the instances. Check this:
class Classy():
inty = 3
stringy = "whatevs"
print(Classy.__dict__)
Wait... A class
with a __dict__
? Yeah, because Classy
is also an instance (of type classobj
, since you're using old style classes, which you shouldn't really do, by the way... You should inherit from object
, which gives you access to more goodies)
>>> print(type(Classy))
<type 'classobj'>
Now, if you created an instance of classy, and put an inty
value to it, you would have:
class Classy():
inty = 3
stringy = "whatevs"
def __init__(self):
self.inty = 5
classy = Classy()
print("__dict__ of instance: %s" % classy.__dict__)
print("__dict__ of Class: %s" % classy.__class__.__dict__)
Which outputs
__dict__ of instance: {'inty': 5}
__dict__ of Class: {'__module__': '__main__', 'inty': 3, '__doc__': None, '__init__': <function __init__ at 0x1080de410>, 'stringy': 'whatevs'}
See the inty
being 5 in the __dict__
of the instance but still being 3 in the __dict__
of the class? It's because now you have two inty
: One attached to classy
, an instance of the class Classy
and another one attached to the class Classy
itself (which is, in turn, an instance of classobj
)
If you did
classy = Classy()
print(classy.inty)
print(classy.stringy)
You'd see:
5
whatevs
Why? Because when you try to get inty
on the instance, Python will look for it in the __dict__
of the instance first. If it doesn't find it, it will go to the __dict__
of the class. That is what's happening on classy.stringy
. Is it in the classy
instance? Nopes. Is it in the Classy
class? Yep! Aight, return that one... And that's the one you see.
Also, I mentioned that the Classy class is an object, right? And as such, you can assign it to something else like this:
What = Classy # No parenthesis
foo = What()
print(foo.inty)
And you'll see the 5
that was "attached" in Classy.__init__
because when you did What = Classy
, you're assigning the class Classy
to a variable named What
, and when you do foo=What()
you're actually running the constructor of Classy
(remember: What
and Classy
are the same thing)
Another thing Python allows (and that I personally don't like because then it makes code very difficult to follow) is attaching attributes to instances "on-the-fly":
classy = Classy()
try:
print(classy.other_thing)
except AttributeError:
print("Oh, dang!! No 'other_thing' attribute!!")
classy.other_thing = "hello"
print(classy.other_thing)
Will output
Oh, dang!! No 'other_thing' attribute!!
hello
Oh, and did I say that functions are objects? Yeah, they are... and as such, you can also assign attributes to them (also, something that makes code really, really confusing) but you could do it...
def foo_function():
return None # Very dummy thing we're doing here
print("dict of foo_function=%s" % foo_function.__dict__)
foo_function.horrible_thing_to_do = "http://www.nooooooooooooooo.com/"
print("Horrible thing? %s" % foo_function.horrible_thing_to_do)
Outputs:
dict of foo_function={}
Horrible thing? http://www.nooooooooooooooo.com/
You can use vars
or __dict__
with the class name, not instance:
Option 1:
class Classy:
inty = 3
stringy = "whatevs"
final_vals = {a:b for a, b in vars(Classy).items() if a not in ['__doc__', '__module__']}
Output:
{'inty': 3, 'stringy': 'whatevs'}
Option 2:
final_vals = {a:b for a, b in Classy.__dict__.items() if a not in ['__doc__', '__module__']}
The reason that the .__dict__
and vars
methods aren't working as you expected is because you haven't defined a constructor for your class with python's self
reference. The following will do what you're looking for:
class Classy():
def __init__(self):
self.inty = 3
self.stringy = 'whatevs'
object = Classy()
object.__dict__
vars(object)
Outputs:
{'inty': 3, 'stringy': 'whatevs'}
{'inty': 3, 'stringy': 'whatevs'}
Cheers!