In the official doc, __getattr__ will be called when __getattribute__ raise an AttributeError.
But the code did not come into an infinite loop, why?
class A:
def __getattr__(self, item):
return self.__getattribute__(item)
a = A()
print(a.d)
It just throw a AttributeError, how to explain it?
This debugging print will help you to understand what is going on:
class A:
def __getattr__(self, item):
print('__getattr__ called for item {}'.format(item))
return self.__getattribute__(item)
def __getattribute__(self, item):
print('__getattribute__ called for item {}'.format(item))
return object.__getattribute__(self, item)
a = A()
print(a.d)
Output:
__getattribute__ called for item d
__getattr__ called for item d
__getattribute__ called for item __getattribute__
__getattribute__ called for item d
AttributeError: 'A' object has no attribute 'd'
__getattribute__ is called first.
After failure of __getattribute__ lookup algorithm calls __getattr__.
Inside it in self.__getattribute__ also __getattribute__ is called for item __getattribute__.
Now __getattribute__ is called for item d, but not in the flow of lookup algorithm because it was called directly, so __getattr__ will not be called.
object.__getattribute__(self, item) can't find d so error is raised.
Related
I have the following class that tries to define a custom __getattribute__:
class Item:
def __init__(self):
self.x = 1
def __dir__(self):
return ['x']
def __getattribute__(self, i):
if i not in dir(self):
return "NOT DEFINED!"
else:
return super().__getattribute__(a)
And then I can run it fine:
>>> i=Item()
>>> i.x
1
>>> i.a
'NOT DEFINED!'
However, if I change this line:
if i not in dir(self):
To:
if i not in self.__dir__():
I get a RecursionError as it seems to be calling itself. Why is this so, and why does dir() and differently than __dir__()?
The __getattribute__ method is called whenever the dot notation is used on an object, so self.__dir__() would call the __getattribute__ method of self with '__dir__' as the argument, which, with your code, would then call self.__dir__() again, which calls the __getattribute__ method again with the same argument, resulting in endless recursions.
I have a situation, where getattribute fallbacks to getattr and then again getattribute gets called.
How current getattribute gets called again? I am confused.
class Count(object):
def __init__(self,mymin,mymax):
self.mymin=mymin
self.mymax=mymax
self.current=None
def __getattr__(self, item):
print("akhjhd")
self.__dict__[item]=0
return 0
def __getattribute__(self, item):
print("this is called first")
if item.startswith('cur'):
print("this raised an error")
raise AttributeError
print("This will execute as well")
return object.__getattribute__(self,item)
obj1 = Count(1,10)
print(obj1.mymin)
print(obj1.mymax)
print(obj1.current)
Console Output:
this is called first
This will execute as well
1
this is called first
This will execute as well
10
this is called first
this raised an error
akhjhd
this is called first
This will execute as well
0
getattr is called because getattribute raises AttributeError
self.__dict__ invokes the "second" call to getattribute
Clean the code and add print(item) to make this clearer:
class Count(object):
def __init__(self):
self.current = None
def __getattr__(self, item):
print("in getattr")
self.__dict__[item] = 0
return 0
def __getattribute__(self, item):
print(item)
print("in __getattribute__ 1")
if item.startswith('cur'):
print("starts with 'cur'")
raise AttributeError
print("in __getattribute__ 2")
return object.__getattribute__(self, item)
obj1 = Count()
print(obj1.current)
Outputs
current
in __getattribute__ 1
starts with 'cur'
in getattr
__dict__
in __getattribute__ 1
in __getattribute__ 2
0
You need to consult with python Data model
Excerpts for __getattribute__:
Called unconditionally to implement attribute accesses for instances of the class. If the class also defines __getattr__(), the latter will not be called unless __getattribute__() either calls it explicitly or raises an AttributeError.
I see in your code:
if item.startswith('cur'):
print("this raised an error")
raise AttributeError
So I think you did it intentionally
I have this class:
class Meta:
def __getattr__(self, atr):
print('get', atr)
def __setattr__(self, atr, atr_value):
print('{0} with value {1} added'.format(atr, atr_value))
self.__dict__[atr] = atr_value
and when I set an attribute - print works:
>>> x.a = 1
a with value 1 added
but when I call it:
>>> x.a
1
Value is returned without the print output.
Help will be much appreciated.
__getattr__ is called only when the attribute does not exist. You want to override __gettattribute__.
def __getattribute__(self, name):
print("get", name)
return object.__getattribute__(self, name)
If the attribute is in the class or the instance dictionary, __getattr__ won't be called. This is an intentional asymmetry from __setattr__.
Implement __getattribute__ if you want full control.
In new-style classes __getattr__(true for old style classes as well) is called only if the attribute cannot be found on the instance or its class(well this includes the base classes as well).
In new-style classes you can achieve this by overriding __getattribute__.
As the title says. It seems no matter what I do, __getattr__ will not be called. I also tried it for instance (absurd, I know), with predictably no response. As if __getattr__ was banned in meta classes.
I'd appreciate any pointer to documentation about this.
The code:
class PreinsertMeta(type):
def resolvedField(self):
if isinstance(self.field, basestring):
tbl, fld = self.field.split(".")
self.field = (tbl, fld)
return self.field
Field = property(resolvedField)
def __getattr__(self, attrname):
if attrname == "field":
if isinstance(self.field, basestring):
tbl, fld = self.field.split(".")
self.field = (tbl, fld)
return self.field
else:
return super(PreinsertMeta, self).__getattr__(attrname)
def __setattr__(self, attrname, value):
super(PreinsertMeta, self).__setattr__(attrname, value)
class Test(object):
__metaclass__ = PreinsertMeta
field = "test.field"
print Test.field # Should already print the tuple
Test.field = "another.field" # __setattr__ gets called nicely
print Test.field # Again with the string?
print Test.Field # note the capital 'F', this actually calls resolvedField() and prints the tuple
Thanks to BrenBarn, here's the final working implementation:
class PreinsertMeta(type):
def __getattribute__(self, attrname):
if attrname == "field" and isinstance(object.__getattribute__(self, attrname), basestring):
tbl, fld = object.__getattribute__(self, attrname).split(".")
self.field = (tbl, fld)
return object.__getattribute__(self, attrname)
As documented, __getattr__ is only called if the attribute does not exist. Since your class has a field attribute, that blocks __getattr__. You can use __getattribute__ if you really want to intercept all attribute access, although it's not clear from your example why you need to do this. Note that this has nothing to do with metaclasses; you would see the same behavior if you created an instance of an ordinary class and gave it some attribute.
Even assuming you used __getattribute__, so it was called when the attribute exists, your implementation doesn't make much sense. Inside __getattr__ you try to get a value for self.field. But if __getattribute__ was called in the first place, it will be called again for this access, creating an infinite recursion: in order to get self.field, it has to call __getattribute__, which again tries to get self.field, which again calls __getattribute__, etc. See the documentation for __getattribute__ for how to get around this.
I want to use __setattr__ only when the attribute was not found in the object's attributes, like __getattr__.
Do I really have to use try-except?
def __setattr__(self, name, value):
try:
setattr(super(Clazz, self), name, value)
except AttributeError:
# implement *my* __setattr__
pass
You can use hasattr():
def __setattr__(self, name, value):
if hasattr(super(Clazz, self), name):
setattr(super(Clazz, self), name, value)
else:
# implement *my* __setattr__
pass
There are many times when calling hasattr won't work the way you expect (e.g., you've overridden __getattr__ to always return a value), so another way to set the right attribute in the right place would be something like this:
def __setattr__(self, k, v):
if k in self.__dict__ or k in self.__class__.__dict__:
super(Clazz, self).__setattr__(k, v)
else:
# implement *my* __setattr__
pass
__setattr__, if it exists, is called for every attribute set on the object.
Your example code, though, is rather confusing for me. What are you trying to do with the statement:
setattr(super(Clazz, self), name, value) ??
Set an attribute on self, with self viewed as an instance of its superclass? That makes no sense, because the object is still "self".
On the other hand trying to use "setattr" on the object returned by a call to "super" will always yield an attribute error, regardless if the attribute exists on the superclass or not. That is because super returns not the superclass itself, but a wrapper object that wil fetch attributes there when they are needed - so you can use "hasattr" in the object returned by super, but not setattr. I thought it would behave so, and just tried it on the console :
>>> class A(object):pass
...
>>> class B(A): pass
...
>>> b = B()
>>> super(B,b)
<super: <class 'B'>, <B object>>
>>> setattr(super(B,b), "a", 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'a'
>>> A.a = 1
>>> setattr(super(B,b), "a", 5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'super' object has no attribute 'a'
But then, you can just use "hasattr" in the object itself, and proceed like this:
def __setattr__(self, attr, value):
if hasattr(self, value):
#this works because retrieving "__setattr__" from the
# result of the supercall gives the correct "__setattr__" of the superclass.
super(Clazz, self).__setattr__(self, attr, value)
else:
# transform value /or attribute as desired in your code
super(Clazz, self).__setattr__(self, attr, value)