Python2 - printing an object's default attribu

2019-07-30 16:20发布

问题:

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?

回答1:

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/


回答2:

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__']}


回答3:

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!



标签: python class