Reputation: 997
Below is my test code:
import functools
class LazyLoader(object):
def __init__(self, klass, *args, **kwargs):
self.klass = klass
self.args = args
self.kwargs = kwargs
self.instance = None
def __getattr__(self, name):
return functools.partial(self.__run_method, name)
def __run_method(self, __name, *args, **kwargs):
if self.instance is None:
self.instance = self.klass(*self.args, **self.kwargs)
return getattr(self.instance, __name)(*args, **kwargs)
class SchedulerReportClient(object):
def method(self):
print 'method called'
def __getattr__(self, name):
print '__getattr__ called'
return functools.partial(self.__run_method, name)
def __run_method(self, __name, *args, **kwargs):
print '__run_method called'
return getattr(self, __name)(*args, **kwargs)
if __name__ == "__main__":
a = SchedulerReportClient()
entity = LazyLoader(a)
print hasattr(entity, 'obj_to_primitive')
print(callable(entity.obj_to_primitive))
print entity.__class__.__name__
if hasattr(entity, 'obj_to_primitive') and callable(entity.obj_to_primitive):
entity.obj_to_primitive()
And the outouts are:
True
True
Traceback (most recent call last):
LazyLoader
File "C:/Users/chen/PycharmProjects/pytest/main", line 42, in <module>
entity.obj_to_primitive()
File "C:/Users/chen/PycharmProjects/pytest/main", line 17, in __run_method
self.instance = self.klass(*self.args, **self.kwargs)
TypeError: 'SchedulerReportClient' object is not callable
The code is mostly in openstack nova, I copied some.
There isn't a obj_to_primitive
method in class SchedulerReportClient
, why hasattr and callable function return true?
CentOS7.4 with Python2.7.
Upvotes: 1
Views: 368
Reputation: 22294
The builtin function hasattr
is implemented by calling getattr
and checking if it raises an exception or not. This is stated in the doc.
The result is
True
if the string is the name of one of the object’s attributes,False
if not. (This is implemented by calling getattr(object, name) and seeing whether it raises an exception or not.)
In this case you defined the LazyLoader.__getattr__
method which returns a partially evaluated method LazyLoader.__run_method
regardless of the value of the argument name
.
def __getattr__(self, name):
return functools.partial(self.__run_method, name)
As a reminder, the __getattr__
method is called when an attribute was not found by the usual means.
So when you get entity.obj_to_primitive
, what is returned is functools.partial(entity.__run_method, 'obj_to_primitive')
.
Upvotes: 1