I knew that python's __getattr__
is triggered when visiting a non-existing attribute.
But in my example below, inside c1's __init__
, I created a self attribute called name. When visiting it, both access ways triggered __getattr__
and thus printed "None".
This is weird to me. I suppose either my understanding or my code has some issue?
$ cat n.py
class c1(object):
def __init__(s):
print 'init c1'
s.name='abc'
def __getattr__(s,name):
print "__getattr__:"+name
return None
def __setattr__(s,name,value):
print "__setattr__:"+value
def __get__(s,inst,owner):
print "__get__"
class d:
def __init__(s):
s.c=c1()
c=c1()
print c.name
o=d()
print o.c.name
$ python n.py
init c1
__setattr__:abc
__getattr__:name
None
init c1
__setattr__:abc
__getattr__:name
None
You can see I've defined s.name='abc'
inside __init__
, but it is not recognized when calling it.
You also implemented __setattr__
, and it is always called when trying to set an attribute. Your version only prints the attribute:
def __setattr__(s,name,value):
print "__setattr__:"+value
and nothing else, which means the attribute is not actually set. The above is called for the s.name='abc'
expression, the name
attribute is never set, so any future access to the name
attribute is sent to __getattr__
again.
Have __setattr__
set the value in __dict__
directly:
def __setattr__(self, name, value):
print "__setattr__:" + value
self.__dict__[name] = value
or make your class a new-style class (inherit from object
), and you can re-use the base implementation with super(c1, self).__setattr__(name, value)
.
As a side note: you implemented c1.__get__
, presumably in an attempt to make the class a descriptor object. However, the descriptor protocol only applies to class attributes, not to instance attributes, and then only on new-style classes. Your class d
is not a new-style class, and you used an instance attribute c
to store the c1
instance.