class Test():
def __init__(self,age):
self.age=age
def __getattribute__(self,attribute):
print("Initializing getattribute")
return 6
def __setattr__(self,attribute,value):
print("Initializing setattr")
return object.__setattr__(self,attribute,value)
test=Test(4)
test.age
print(test.age)
From the code above the result is :
Initializing setattr
Initializing getattribute
Initializing getattribute
6
I understand where each dunder method is called, but what do they really do?In the previous example getattribute dictate the attribute value and if I delete the line :
return object.__setattr__(self,attribute,value)
Nothing changes.
So what does __setattr__ do?
__getattribute__ is called before any other attempt is made to access an attribute. No matter what __setattr__ does, test.age is handled by test.__getattribute__("age"), which returns 6 whether or not there is an attribute named age.
If you get rid of __getattribute__:
class Test():
def __init__(self,age):
self.age=age
def __setattr__(self,attribute,value):
print("Initializing setattr")
return object.__setattr__(self,attribute,value)
test=Test(4)
test.age
print(test.age)
The class behaves normally, setting test.age to 4. If you further get rid of the call to object.__setattr__, then you'll get an AttributeError because self.age = age will never actually create or set the age attribute; it just prints the initialization message and returns:
class Test():
def __init__(self,age):
self.age=age
def __setattr__(self,attribute,value):
print("Initializing setattr")
test=Test(4)
test.age
print(test.age)
results in
Initializing setattr
Traceback (most recent call last):
File "/Users/chepner/tmp.py", line 11, in <module>
test.age
AttributeError: 'Test' object has no attribute 'age'
Related
I am trying to invoke classmethod over classname .AttributeError problem occurs
When I use #singleton ,I can't run with classname.functionname .It's must be classname().functionname
Why does this happen?
def singleton(cls):
'''
单例
:param cls:
:return:
'''
_instance = {}
def _singleton(*args, **kargs):
if cls not in _instance:
_instance[cls] = cls(*args, **kargs)
# print(type(_instance[cls])) <class '__main__.Coco'>
return _instance[cls]
return _singleton
#singleton
class Coco():
# def __new__(cls, *args, **kwargs):
# if not hasattr(Coco, "_instance"):
# if not hasattr(Coco, "_instance"):
# Coco._instance = object.__new__(cls)
# print(type(Coco._instance))
# return Coco._instance
def __init__(self):
print('coco')
#classmethod
def get_info(cls):
print('coco is 18 ages old')
# print(Coco().get_info())
print(Coco.get_info())
Exception
Traceback (most recent call last):
File "/Users/coco/Automation/AutoTestRes/scripts/python/coco.py", line 36, in <module>
print(Coco.get_info())
AttributeError: 'function' object has no attribute 'get_info'
When you use a decorator in Python, like this:
#decorator_name
class class_name:
...
..., this is equivalent to doing this:
class class_name:
...
class_name = decorator_name(class_name)
This means that the value of the variable class_name is no longer necessarily a class, but instead it is whatever the return value of decorator_name is.
In your case, the class decorator singleton returns the function _singleton, not the actual class. So when you say:
print(Coco.get_info())
..., this is the same as saying:
print(_singleton.get_info())
...within the function.
Therefore, you get an AttributeError, because the function, which now has the name Coco, does not have that attribute.
To access the attribute of the class, you need to run the function because this will return an instance of the class, which will have the attribute.
It is no longer possible to access the class itself from the global scope.
For the class Thing, when I call an undefined method such as .doamethod()...
class Thing:
def __init__(self):
pass
def __getattr__(self, method):
print('Run method: '+method)
t = Thing()
t.doamethod()
...I get this output:
Run method: doamethod
Traceback (most recent call last):
File "C:\Users\person\classtest.py", line 9, in <module>
t.doamethod()
TypeError: 'NoneType' object is not callable
Since the text Run method: doamethod was printed I know the contents of __getattr__ ran (which is good, I want this) but it also raised TypeError: 'NoneType' object is not callable. Why?
__getattr__ returns the attribute. Your implementation of __getattr__ returns None -- so when you say t.doamethod, the value of that is None, and when you try to call it with (), you get the not callable error.
If you want your attribute to be a callable no-op, you could do:
class Thing:
# note: no need to add an empty __init__ method here
def __getattr__(self, method):
def impl(*args, **kwargs):
return None
print(f'Run method: {method}')
return impl
t = Thing()
t.doamethod # prints "Run method: doamethod"
t.doamethod() # prints "Run method: doamethod"
If you want the attribute to be a callable that prints "Run method" when it's called (rather than when the method is accessed), then put that code inside the function that __getattr__ returns:
class Thing:
def __getattr__(self, attr):
def impl(*args, **kwargs):
print(f'Run method: {attr}({args}, {kwargs})')
print(f'Get attribute: {attr}')
return impl
t = Thing()
func = t.foo # prints "Get attribute: foo"
func() # prints "Run method: foo((), {})"
func(42, arg2="bar") # prints "Run method: foo((42,), {'arg2': 'bar'})"
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 a difficulty implementing properties and __getattr__ so that
when an error happens, it is reported correctly. This is my MWE (python 3.6):
class A:
#property
def F(self):
return self.moo # here should be an error
#property
def G(self):
return self.F
def __getattr__(self, name):
print('call of __getattr__ with name =', name)
if name == 'foo':
return 0
raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, name))
a = A()
print(a.G)
The output is as follows:
call of __getattr__ with name = moo
call of __getattr__ with name = F
call of __getattr__ with name = G
Traceback (most recent call last):
line 18 in <module>
print(a.G)
line 15, in __getattr__
raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, name))
AttributeError: 'A' object has no attribute 'G'
But the error that should be raised is:
AttributeError: 'A' object has no attribute 'moo'
I know that properties and attributes in the __dict__ are attempted before __getattr__ is called in an error-free scenario.
It seems incorrect to me that when a property exists but fails, __getattr__ is still attempted instead of letting the error from the property to go through. How can this be avoided?
The initial error message that was generated about failing to get attribute 'foo' has been lost. The final error message 'A' object has no attribute 'G' is particularly misleading and annoying. How to implement __getattr__ in order to see the initial error?
(EDIT) A related problem is simultaneously to achieve that
hasattr(a, 'moo') returns False while hasattr(a, 'G') returns True or raises an exception of the missing 'moo' attribute. Does that make sense?
What is happening?
First, a little heads up as to why this happens. From the doc on __getattr__:
Called when the default attribute access fails with an AttributeError [...] or __get__() of a name property raises AttributeError.
In this case, since you are using #property, we are looking at an AttributeError raised from the __get__ method of the property F when trying to recover self.moo. This is what your call stack looks like at that moment.
__main__
a.G.__get__
a.F.__get__
a.__getattr__ # called with 'moo' <-- this is where the error is raised
The attribute getter protocol sees an error being raised from inside a.F.__get__, it thus fallback on calling a.__getattr__('F') and that despite the fact the error had been raised because of 'moo'. The same then happens for a.G.__get__
This behaviour is considered normal in Python, since the top-most property that failed to return a value is indeed a.G.
Solution
Now what you want is for an AttributeError raised by a __get__ method to bubble up instead of being caught. To do that you need not to have a __getattr__ method.
Thus, in this particular case, what you want to use is __getattribute__ instead.
Of course, with this solution you have to make sure yourself not to override an existing attribute.
class A:
#property
def F(self):
return self.moo # here should be an error
#property
def G(self):
return self.F
def __getattribute__(self, name):
print('call of __getattribute__ with name =', name)
if name == 'foo':
return 0
else:
return super().__getattribute__(name)
Example
A().G
Output
call of __getattribute__ with name = G
call of __getattribute__ with name = F
call of __getattribute__ with name = moo
Traceback (most recent call last):
...
AttributeError: 'A' object has no attribute 'moo'
Here's a hacky solution, replacing the AttributeError with another exception type:
from functools import wraps
def no_AttributeError(f):
#wraps(f)
def wrapped(self):
try:
return f(self)
except AttributeError as e:
raise Exception('AttributeError inside a property getter') from e
return wrapped
class A:
#property
#no_AttributeError
def F(self):
return self.moo # here should be an error
#property
#no_AttributeError
def G(self):
return self.F
def __getattr__(self, name):
print('call of __getattr__ with name =', name)
if name == 'foo':
return 0
raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, name))
a = A()
print(a.G)
This results in the following output:
call of __getattr__ with name = moo
Traceback (most recent call last):
File ".\test_getattr_redir.py", line 7, in wrapped
return f(self)
File ".\test_getattr_redir.py", line 17, in F
return self.moo # here should be an error
File ".\test_getattr_redir.py", line 28, in __getattr__
raise AttributeError("'{}' object has no attribute '{}'".format(type(self).__name__, name))
AttributeError: 'A' object has no attribute 'moo'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File ".\test_getattr_redir.py", line 31, in <module>
print(a.G)
File ".\test_getattr_redir.py", line 7, in wrapped
return f(self)
File ".\test_getattr_redir.py", line 22, in G
return self.F
File ".\test_getattr_redir.py", line 9, in wrapped
raise Exception('AttributeError inside a property getter') from e
Exception: AttributeError inside a property getter
As an addendum, to make it explicit why Python does what it does, here's an excerpt from the documentation:
[__getattr__ is called] when the default attribute access fails with an AttributeError (either __getattribute__() raises an AttributeError because name is not an instance attribute or an attribute in the class tree for self; or __get__() of a name property raises AttributeError). This method should either return the (computed) attribute value or raise an AttributeError exception.
(It looks like you know this but I think it's good to have it written out for other people running into the same issue.)
So that means when self.moo raises an AttributeError, it results in A.__getattr__(a, 'F') being called, which results into another AttributeError
Given the answers above, I have tried the following solution for the case when __getattr__ is already defined by the base class P that we cannot change.
class P:
def __getattr__(self, name):
print('call of __getattr__ with name =', name)
if name == 'foo':
return 0
raise AttributeError("Cannot recover attribute '{}'".format(name))
class A(P):
e = None
#property
def F(self):
return self.moo
#property
def G(self):
return self.F
def __getattr__(self, name):
raise A.e
def __getattribute__(self, name):
try:
return object.__getattribute__(self, name)
except AttributeError as e1:
try:
return P.__getattr__(self, name)
except AttributeError as e2:
A.e = AttributeError(str(e1) + ' -> ' + str(e2))
raise AttributeError
a = A()
print(a.G)
It replicates what python does when looking for attributes: the order of calls and semantics are kept. It only changes the final error message to
AttributeError: 'A' object has no attribute 'moo' -> Cannot recover attribute 'moo' -> Cannot recover attribute 'F' -> Cannot recover attribute 'G'
However, it might be causing more problems in the derived code than it is solving, so I don't know.
I'm work with python and I need a function in class like
class asas(object):
def b(self):
self.name = "Berkhan"
a = asas()
a.b().name
and I check this module
Traceback (most recent call last):
File "C:\Users\Berkhan Berkdemir\Desktop\new 1.py", line 5, in <module>
a.b().name
AttributeError: 'NoneType' object has no attribute 'name'
What should I do?
NoneType means that instead of an instance of whatever Class or Object you think you're working with, you've actually got None. That usually means that an assignment or function call up above failed or returned an unexpected result. See reference.
So, you can do something like this.
class asas(object):
def b(self):
self.name = "Berkhan"
return self.name
a = asas()
print(a.b()) # prints 'Berkhan'
or
class asas(object):
def b(self):
self.name = "Berkhan"
return self
a = asas()
print(a.b().name) # prints 'Berkhan'
b() returns nothing. Therefore it returns None.
You probably want something like this:
class asas(object):
def b(self):
self.name = "Berkhan"
return self
a = asas()
a.b().name