Here is the code:
class Animal:
def __init__(self, animal_type):
self.animal_type = animal_type
class Cat(Animal):
def __init__(self, animal_type, favorite_food):
super().__init__(animal_type)
self.favorite_food = favorite_food
def cat_print(self):
print("{}'s favorite food is {}.".format(self.animal_type, self.favorite_food))
def __getattribute__(self, item):
print('__getattribute__() be called. Item is: ', item)
def __getattr__(self, item):
print('__getattr__() be called. Item is: ', item)
def __setattr__(self, key, value):
print('__setattr__() be called. key and Value is: ', key, value)
cat = Cat('cat', 'fish')
print(cat.animal_type)
print(cat.favorite_food)
When I print the cat.animal_type
, it print None. I guess that because of I rewrite method: __setattr__()
and __getattribute__()
, The value can't pass into the attribute.
I want to know what is the process of assigning attribute and get the attribute in a class in python?
Thanks.
The reason all of your attributes are None
, even non-existent attributes like favorite
, is this:
def __getattribute__(self, item):
print('__getattribute__() be called. Item is: ', item)
You're overriding the normal object.__getattribute__
code with a method that always returns None
. If you want it to print something and also do the normal thing, you have to do it explicitly, using super
—the same way you already did in your initializer:
def __getattribute__(self, item):
print('__getattribute__() be called. Item is: ', item)
return super().__getattribute__(item)
If you fix that, lookup for attributes that exist will now work, but non-existent attributes are still going to return None
instead of raising an AttributeError
. Why?
def __getattr__(self, item):
print('__getattr__() be called. Item is: ', item)
The normal object.__getattribute__
(that you're now properly calling) looks for an attribute in the object's __dict__
, and in its class and all of its class's ancestors (with a bit of extra complexity related to descriptors that I'm going to ignore here), and then, if nothing is found (or something is found but fetching it raises AttributeError
), it calls the class's __getattr__
. And you provide a __getattr__
that returns None
.
Here, you again want to delegate to the superclasses:
def __getattr__(self, item):
print('__getattr__() be called. Item is: ', item)
return super().__getattr__(item)
Except that there is no default implementation of __getattr__
. So, you'll get an AttributeError
, but it'll be about a super
not having a __getattr__
, not about a Cat
not having a favorite
.
If you know one of your base classes wants to provide a __getattr__
for you to delegate to, super
to it. But if you know nobody does:
def __getattr__(self, item):
print('__getattr__() be called. Item is: ', item)
raise AttributeError(item)
And if you have no idea (because, say, your class was designed to be used as a mixin with someone else's class hierarchy), you probably want something like this:
def __getattr__(self, item):
print('__getattr__() be called. Item is: ', item)
try:
ga = super().__getattr__
except AttributeError:
pass
else:
return ga(item)
raise AttributeError(item)
If you fix both of those, now you'll get an AttributeError
on the attributes you did set. Why?
def __setattr__(self, key, value):
print('__setattr__() be called. key and Value is: ', key, value)
Same problem. Here, you might want to set the attribute explicitly, like this:
def __setattr__(self, key, value):
print('__setattr__() be called. key and Value is: ', key, value)
super().__getattribute__('__dict__')[key] = value
… or you might just want to delegate again:
def __setattr__(self, key, value):
print('__setattr__() be called. key and Value is: ', key, value)
super().__setattr__(key, value)