Why setting attributes of class object instances results in AttributeError? - python-3.x

Why the following code produces an error
>>> object().foo = 'bar'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'object' object has no attribute 'foo'
while the next one works fine?
class A():
pass
A().foo = 'bar'
What is the exact difference between A and object? I believe o().foo = 'bar' leads to setattr('o', 'foo', 'bar') and this in turn results in o.__setattr__('foo', 'bar'). One would expect them to have identic __setattr__ methods since no overriding happens. Yet the outputs are different. Please explain why. What happens behind the scenes?
A similar pattern can be noticed for built-in functions and user-defined ones. I can't set my own attributes for let's say dict but it's perfectly ok to write (lambda:None).foo = 'bar'. What's going on here?

Related

Why calling method for two times result in TypeError: object is not callable

Define two class based on BaseHandler as below:
class BaseHandler:
def successor(self, successor):
self.successor = successor
class ScoreHandler1(BaseHandler):
pass
class ScoreHandler2(BaseHandler):
pass
Initialize two instances:
h1 = ScoreHandler1()
h2 = ScoreHandler2()
Call successor method first time :
h1.successor(h2)
Now call it second time:
h1.successor(h2)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: 'ScoreHandler2' object is not callable
Why can't call the method more times?
callable(ScoreHandler2)
True
The first time you call h1.successor(h2), you're calling the method named "successor". Inside this method, you set the attribute named "successor" to the object h2.
The second time you call h1.successor(h2), you're calling the attribute named "successor", which you defined previously to h2. Since ScoreHandler2 does not implement __call__, it'll raise an error.
To fix this, avoid naming attributes with the same name as methods.

How to get parameter names in python format function?

func = "Hello {name}. how are you doing {time}!".format
For example, let's assume func is defined as above.
we don't have a definition of func at hand but we have an instance of it.
how can I get all arguments to this function?
apparently inspect.getargspec(func) does not work here!
if I just run this with empty parameters it returns an error with one missing parameter at a time, but I don't know how to get them directly:
a()
-------
KeyError Traceback (most recent call last)
<ipython-input-228-8d7b4527e81d> in <module>
----> 1 a()
KeyError: 'name'
what exactly are you trying to do? what is your expected output? as far as i know, there is no possibility to insert name and time, after you define func. because "string".format("call variables") <---- it cant call variables that are not defined. As far as i know the calling process happens first, independent of the formatting type. (f string, %, .format().. whatever you use)

NameError: name 'XYZ' is not defined [ WARN:1] terminating async callback

I have a function 'draw_humans' in class 'TfPoseEstimator' in 'estimator.py' which is defined as:
def draw_humans:
global cocoDict
cocoDict = {}
cocoDict = dict(zip(a,zip(b,c)))
'''
'''
return (npimg, cocoDict, dist_dict)
I call this function in the main.py module and assign the returned values to variables like this:
image, cocoDict_clone, dist_dict_clone = TfPoseEstimator.draw_humans(image, humans, imgcopy=False)
But I get the error mentioned above.
Traceback (most recent call last):
File "run_webcam.py", line 306, in <module>
image, cocoDict_clone, dist_dict_clone = TfPoseEstimator.draw_humans(image, humans, imgcopy=False)
File "C:\Python\Python37\summer\PoseEstimation\tf_pose\estimator.py", line 772, in draw_humans
return (npimg, cocoDict, dist_dict)
NameError: name 'cocoDict' is not defined
[ WARN:1] terminating async callback
I have even tried to make it global but it did not work. Usually, it does work, can someone figure it out?
Actually, the problem was related to the scope of the variables (cocoDict in my case). This dictionary was initialized within the for loop but was being returned outside it. So, I declared it before the for loop and then after manipulating it within the for loop, returned it with no issues.
def draw_humans(npimg, humans, imgcopy=False):
global cocoDict
cocoDict = {}
for human in humans:
'''
'''
return (npimg, cocoDict, dist_dict)
I guess scope in Python is causing me a lot of efforts as I am from a C++ background.

what are the use cases of type() function in python? specifically when i passed 3 arguments to it

I was studying python's type() function.
Its first application is to return the type of any python's object as follows:-
a = 5
type(a)
But in documentation there is another way of calling type() function by passing it three arguments as follows :-
X = type('X', (object,), dict(a=1))
This second call returning "type" class's object.
What are some use cases of this "type" class object?
Pleas elaborate.
Here is the documentation link, which i have followed, but could not get any help regarding it's use cases
Programiz's link, I have explored that as well, but unable to find any relevant stuff there as well
We use three arguments to create a new class.
The first argument is the class name that we are inheriting from. The second argument is the bases attribute (tuple containing all the base class) and in the third argument, we provide all declarations made in the class.
Now with an example,
>>> class X:
a = 1
>>> Y = type('X', (object,), dict(a=10))
Here, 'Y' inherits from class 'X'.
The second argument only means that the object we are creating is of 'object' type.
The third argument is just declaration of definitions that were made in class 'X'. If you don't mention anything inside dict(), then there will be no new attribute in class 'Y'.
>>> Y = type('X', (object,), dict())
Now if you try,
>>> Y.a
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: type object 'X' has no attribute 'a'
Go through this to understand the predefined attributes in Python.

hasattr telling lies? (AttributeError: 'method' object has no attribute '__annotations__')

The following code
class Foo:
def bar(self) -> None:
pass
foo = Foo()
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__ = 'hi'
crashes with
AttributeError: 'method' object has no attribute '__annotations__'
How can this happen?
The attribute error here is raised because you can't set any attribute on a method object:
>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'
The exception here is perhaps confusing because method objects wrap a function object and proxy attribute read access to that underlying function object. So when attributes on the function exist, then hasattr() on the method will return True:
>>> hasattr(foo.bar, 'baz')
False
>>> foo.bar.__func__.baz = 42
>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz
42
However, you still can't set those attributes via the method, regardless:
>>> hasattr(foo.bar, 'baz')
True
>>> foo.bar.baz = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'method' object has no attribute 'baz'
So, just because the attribute can be read doesn't mean you can set it. hasattr() is speaking the truth, you just interpreted it to mean something different.
Now, if you tried to set the __annotations__ attribute directly on the underlying function object you'd get another error message:
>>> foo.bar.__func__.__annotations__ = 'hi'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __annotations__ must be set to a dict object
You would want to use a dictionary object here:
>>> foo.bar.__func__.__annotations__ = {'return': 'hi'}
>>> foo.bar.__annotations__
{'return': 'hi'}
However, because __annotations__ is a mutable dictionary, it is just easier to directly manipulate the keys and values to that object, which is perfectly feasible to do via the method wrapper:
>>> foo.bar.__annotations__['return'] = 'int'
>>> foo.bar.__annotations__
{'return': 'int'}
Now, if you were hoping to set per instance annotations, you can't get away with setting attributes on method objects, because method objects are ephemeral, they are created just for the call, then usually discarded right after.
You would have to use custom method descriptor objects via a metaclass and re-create the __annotations__ attribute for those each time, or you could instead pre-bind methods with a new function object that would be given their own attributes. You then have to pay a larger memory price:
import functools
foo.bar = lambda *args, **kwargs: Foo.bar(foo, *args, **kwargs)
functools.update_wrapper(foo.bar, Foo.bar) # copy everything over to the new wrapper
foo.bar.__annotations__['return'] = 'hi'
Either way you completely kill important speed optimisations made in Python 3.7 this way.
And tools that operate on the most important use case for __annatotions__, type hints, do not actually execute code, they read code statically and would completely miss these runtime alterations.
You're getting an error. because __annotations__ is a dictionary. If you want to change values you'll have to do it like this:
if hasattr(foo.bar, '__annotations__'):
foo.bar.__annotations__['return'] = 'hi'
This will make the return value of your foo.bar be hi instead of None. The only thing I'm not sure about is how the __annotations__ are protected, not allowing you to change them from a dict to string, but I suppose it's some internal check in the source.
UPDATE
For more control over the signature you can use the inspect module and get the Signature object of your class(or method) and edit it from there. For example
import inspect
sig = inspect.signature(foo.bar)
sig.return_annotation # prints None (before modifying)
sig.replace(return_annotation="anything you want")
More on that here

Resources