i meet this code today, and it seems confusing
class ClassOne:
def __init__(self,some_object):
self.not_important_attribute=some_object
class ClassTwo:
def __init__(self,some_object):
self.tmp=ClassOne(some_object)
def __getattr__(self,attr):
return getattr(self.tmp,attr)
a=ClassTwo('not_important_string')
print(getattr(a,'undefined_attribute',3))
When we use getattr at the last line we trigger the __getattr__ method in the SubClass , then delegate to the getattr(self.tmp,attr) function which will raise an exception if the attribute is undefined and no default value was given. But how the value 3 at the last line still go through all the process, and eventually return to getattr(a,'undefined_attribute',3) function ?. Because we didn't have a slot for the default value when we delegate getattr(self.tmp,attr), how is that possible ?
In your case
getattr(self.tmp,attr)
raises AttributeError and if getattr has third argument(default value) then it return default value instead of raising AttributeError
Step by step why 3 value is shown:
the super class has has an atribute not_important_attribute and it is set when the constructor is called
class ClassOne:
def __init__(self,some_object):
self.not_important_attribute=some_object
Here, in the ClassTwo contructor, you create an instance of ClassOne and save it into tmp variable. Meaning that when you overrides __getattr__ you will be asking for the value of attribute of ClassOne
print(getattr(a,'not_important_attribute',3))
not_important_string # founds the method
print(getattr(a,'any',3))
3 #doesn't found method, returns default
That is the same of directly do:
b = ClassOne("not_important_string")
print(getattr(b,'not_important_attribute',3))
not_important_string # founds the method
print(getattr(b,'any',3))
3 # doesn't found method, returns default
When calling getattr(a,'undefined_attribute',3) you are calling the standard python getattr function and pass it a default value. This actually wraps your custom getattr in ClassTwo. We can see that by modifying getattr al little bit.
def __getattr__(self, attr, *args, **kwargs):
print(args) # -> empty
print(kwargs) # -> empty
return getattr(self.tmp, attr)
You can actually go around that wrapping and call getattr from ClassTwo directly, by using print(a.__getattr__('undefined_attribute', 3)). This method would raise an Exception.
So essentially getattr(a,'undefined_attribute',3) is the standard python method that internally calls the custom getattr from ClassTwo.
class ClassTwo:
...
def __getattr__(self,attr):
return getattr(self.tmp,attr)
getattr(a,'undefined_attribute',3) method is a wrapper to call __getattribute__ and if __getattribute__ fails, it will call __getattr__. It is implemented with handling of exception AttributeError. In your case, getattr(a,'undefined_attribute',3) calls ClassTwo.__getattr__ above.
It seems you think when it reaches return getattr(self.tmp,attr), it will throw errors or return some kind of errors and break and stop at that point complete. However, in python, program will pass exception up the call stack. Along the upstream of the call stack, if that exception gets handle, it will exit/complete normally. In this case, AttributeError get passed back along the call stack to getattr(a,'undefined_attribute',3). this getattr has default value for AttributeError so it returns 3 and exits normally.
Related
What is static method and how can we explain the below code ?
#staticmethod
def get_model(**kwargs):
try:
return Model.objects.get(**kwargs)
except Model.DoesNotExist:
return
In short and maybe oversimplified: staticmethod doesn't require object of a class to run. This also means that you don't need self argument.
About the code:
This method is attempting to return single (.get()) instance of a Model that match with parameters specified in kwargs.
example:
kwargs = {"id":5, "is_alive": True}
Model.objects.get(**kwargs)
#is the same as
Model.objects.get(id=5, is_alive=True)
This can raise Model.DoesNotExists error if there is no instances of Model matching with paramaters so try/except is used.
If Model.DoesNotExists error is raised then method return None.
A staticmethod is a function not bound to an object, but is encapsulated within it ( typically to reduce outer namespace clutter, or eliminate any need to import it). It doesn't have the first self argument that a normal object method does.
Given the following code that emulates a staticmethod:
class StaticMethod(object):
"Emulate PyStaticMethod_Type() in Objects/funcobject.c"
def __init__(self, f):
self.f = f
def __get__(self, obj, objtype=None):
print('getting')
return self.f
class A:
def func2():
print('hello')
func2 = StaticMethod(func2)
When I call:
A.func2
i get what i expect:
getting
<function __main__.A.func2>
When I call: A.func2() I get:
getting
hello
Does this mean then that whenever you call a Descriptor Decorator method Python first retrieves it from the Descriptor's __get__ method?
If, yes, how then does the method actually get called? What exactly is happening under the hood?
Does this mean then that whenever you call a Descriptor Decorator method Python first retrieves it from the Descriptor's get method?
When you access an attribute on an object where its class has a class attribute that is a descriptor, the object calls __get__ and returns the result.
A "descriptor decorator" is not really a thing, the fact that a decorator is used to set the descriptor is irrelevant to its functionality.
If, yes, how then does the method actually get called? What exactly is happening under the hood?
If you mean the underlying function that was decorated with staticmethod, then the method gets called whenever you call it, the descriptor/staticmethod itself doesn't prescribe that, it just returns the function.
I have a piece of code that I am trying to understand, and even with the existing answers, I really couldn't understand the purpose of the following code, Can someone please help me in understanding the same?
I have already looked a various relevant questions ( __get__() ) here and I couldnt find specific answers. I understand that the class below is trying to create a method on the fly ( possibly we get to this class from a __getattr__() method which fails to find an attribute ) and return the method to the caller. I have commented right above the lines of code I need understanding with.
class MethodGen(object):
def __getattr__(self, name):
method = self.method_gen(name)
if method:
return self.method_gen(name)
def method_gen(self, name):
def method(*args, **kwargs):
print("Creating a method here")
# Below are the two lines of code I need help understanding with
method.__name__ = name
setattr(self, name, method.__get__(self))
return method
If I am not wrong, the method() function's attribute __name__ has been set, but in setattr() function, the attribute of the class MethodGen, name is set to what ?
This question really intrigued me. The two answers provided didn't seem to tell the whole story. What bothered me was the fact that in this line:
setattr(self, name, method.__get__(self))
the code is not setting things up so that method.__get__ Will be called at some point. Rather, method.__get__ is actually Being Called! But isn't the idea that this __get__ method will be called when a particular attribute of an object, an instance of MethodGen in this case, is actually referenced? If you read the docs, this is the impression you get...that an attribute is linked to a Descriptor that implements __get__, and that implementation determines what gets returned when that attribute is referenced. But again, that's not what's going on here. This is all happening before that point. So what IS really going on here?
The answer lies HERE. The key language is this:
To support method calls, functions include the __get__() method for
binding methods during attribute access. This means that all functions
are non-data descriptors which return bound methods when they are
invoked from an object.
method.__get__(self) is exactly what's being described here. So what method.__get__(self) is actually doing is returning a reference to the "method" function that is bound to self. Since in our case, self is an instance of MethodGen, this call is returning a reference to the "method" function that is bound to an instance of MethodGen. In this case, the __get__ method has nothing to do with the act of referencing an attribute. Rather, this call is turning a function reference into a method reference!
So now we have a reference to a method we've created on the fly. But how do we set it up so it gets called at the right time, when an attribute with the right name is referenced on the instance it is bound to? That's where the setattr(self, name, X) part comes in. This call takes our new method and binds it to the attribute with name name on our instance.
All of the above then is why:
setattr(self, name, method.__get__(self))
is adding a new method to self, the instance of the MethodGen class on which method_gen has been called.
The method.__name__ = name part is not all that important. Executing just the line of code discussed above gives you all the behavior you really want. This extra step just attaches a name to our new method so that code that asks for the name of the method, like code that uses introspection to write documentation, will get the right name. It is the instance attribute's name...the name passed to setattr...that really matters, and really "names" the method.
Interesting, never seen this done before, seems tough to maintain (probably will make some fellow developers want to hang you).
I changed some code so you can see a little more of what is happening.
class MethodGen(object):
def method_gen(self, name):
print("Creating a method here")
def method(*args, **kwargs):
print("Calling method")
print(args) # so we can see what is actually being outputted
# Below are the two lines of code I need help understanding with
method.__name__ = name # These the method name equal to name (i.e. we can call the method this way)
# The following is adding the new method to the current class.
setattr(self, name, method.__get__(self)) # Adds the method to this class
# I would do: setattr(self, name, method) though and remove the __get__
return method # Returns the emthod
m = MethodGen()
test = m.method_gen("my_method") # I created a method in MethodGen class called my_method
test("test") # It returned a pointer to the method that I can use
m.my_method("test") # Or I can now call that method in the class.
m.method_gen("method_2")
m.method_2("test2")
Consider the class below:
class Foo:
def bar(self):
print("hi")
f = Foo()
f.bar()
bar is a class attribute that has a function as its value. Because function implements the descriptor protocol, however, accessing it as Foo.bar or f.bar does not immediately return the function itself; it causes the function's __get__ method to be invoked, and that returns either the original function (as in Foo.bar) or a new value of type instancemethod (as in f.bar). f.bar() is evaluated as Foo.bar.__get__(f, Foo)().
method_gen takes the function named method, and attaches an actual method retrieved by calling the function's __get__ method to an object. The intent is so that something like this works:
>>> m = MethodGen()
>>> n = MethodGen()
>>> m.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MethodGen' object has no attribute 'foo'
>>> n.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MethodGen' object has no attribute 'foo'
>>> m.method_gen('foo')
<function foo at 0x10465c758>
>>> m.foo()
Creating a method here
>>> n.foo()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'MethodGen' object has no attribute 'foo'
Initially, MethodGen does not have any methods other than method_gen. You can see the exception raised when attempting to invoke a method named foo on either of two instances. Calling method_gen, however, attaches a new method to just that particular instance. After calling m.method_gen("foo"), m.foo() calls the method defined by method_gen. That call does not affect other instances of MethodGen like n.
I have been playing with my code and I ended up with this:
class TestProperty:
def __init__(self,func):
self.func = func
def __set__(self,instance,value):
setattr(instance,self.func.__name__,value)
def __get__(self,instance,cls):
if instance is None:
return self
else:
return getattr(instance,self.func.__name__)
class John(object):
def __init__(self):
pass
#TestProperty
def TestProp(self):
print('This line won\'t be printed')
p = John()
p.TestProp = 99
print(p.TestProp)
I'm trying to understand the behavior when creating a class descriptor and using them on methods instead of attributes. I am having a hard time understanding what's going on underneath and It would be really nice if someone can shed some light on me how did this end up as recursive error?
My initial guess is something like this:
Method that is decorated with the descriptor is called
Calls either __set__ or __get__ depending on how we accessed it.
Descriptors attempts to set the value of the instance which calls which ends up mapping it back to step 1(error).
Can anyone explain to me in great detail how did this happen and how do I resolve this?
The code provided serves no purpose other than understanding the behavior of class descriptor.
Don't use getattr() and setattr(); you are triggering the descriptor again there! The descriptor handles all access to the TestProp name, using setattr() and getattr() just goes through the same path as p.TestProp would.
Set the attribute value directly in the instance.__dict__:
def __get__(self,instance,cls):
if instance is None:
return self
try:
return instance.__dict__[self.func.__name__]
except KeyError:
raise AttributeError(self.func.__name__)
def __set__(self,instance,value):
instance.__dict__[self.func.__name__] = value
This works because you have a data descriptor; a data descriptor takes precedence over instance attributes. Access to p.TestProp continues to use the descriptor object on the class even though the name 'TestProp' exists in instance __dict__.
I need to decorate a object's method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied with argv), so a same object could be decorated 3 times, 2 times, or not be decorated at all.
Here is some context, the program is a puzzle solver, the main behavior is to find a solution for the puzzle automatically, by automatically I mean without user intervention. And here is where the decoration gets to play, one of the things I want to is draw a graph of what happened during the execution, but I want to do so only when the flag --draw-graph is used.
Here is what I've tried:
class GraphDecorator(object):
def __init__(self, wrappee):
self.wrappee = wrappee
def method(self):
# do my stuff here
self.wrappee.method()
# do more of stuff here
def __getattr__(self,attr):
return getattr(self.wrappee,attr)
And why it did NOT work:
It did not work because of the way I built the application, when a method that did not exist in my Decorator class was called it felt back to the implementation of the decorated class, the problem is that the application always started invoking the method run that did not need to be decorated, so the undecorated fall back was used and from inside the undecorated form it always called undecorated methods, what I needed was to replace the method from the object, not to proxy it:
# method responsible to replace the undecorated form by the decorated one
def graphDecorator(obj):
old_method = obj.method
def method(self):
# do my stuff here
old_method()
# do more of my stuff
setattr(obj,'method',method) # replace with the decorated form
And here is my problem, the decorated form does not receive self when it is called resulting on a TypeError because of the wrong number of arguments.
The problem was that I couldn't use func(self) as a method. The reason is that setattr() method does not bound the function, and the function acts like it a static method - not a class method -, thanks to the introspective nature of python I've able to come up with this solution:
def decorator(obj):
old_func = obj.func # can't call 'by name' because of recursion
def decorated_func(self):
# do my stuff here
old_func() # does not need pass obj
# do some othere stuff here
# here is the magic, this get the type of a 'normal method' of a class
method = type(obj.func)
# this bounds the method to the object, so self is passed by default
obj.func = method(decorated_func, obj)
I think this is the best way to decorate a object's method at runtime, though it would be nice to find a way to call method() directly, without the line method = type(obj.func)
You might want to use __getattribute__ instead of __getattr__ (the latter being only called if "standard" lookup fails):
class GraphDecorator(object):
def __init__(self, wrappee):
self.__wrappee = wrappee
def method(self):
# do my stuff here
self.wrappe.method()
# do more of stuff here
def __getattribute__(self, name):
try:
wrappee = object.__getattribute__(self, "_GraphDecorator__wrappee")
return getattr(wrappee, name)
except AttributeError:
return object.__getattribute__(self, name)
I need to decorate a object's method. It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program (arguments supplied with argv), so a same object could be decorated 3 times, 2 times, or not be decorated at all.
The above is unfortunately incorrect, and what you are trying to do is unnecessary.
You can do this at runtime like so. Example:
import sys
args = sys.argv[1:]
class MyClass(object):
pass
if args[0]=='--decorateWithFoo':
MyClass = decoratorFoo(MyClass)
if args[1]=='--decorateWithBar'
MyClass = decoratorBar(MyClass)
The syntax:
#deco
define something
Is the same thing as:
define something
something = deco(something)
You could also make a decorator factory #makeDecorator(command_line_arguments)
"It needs to be at runtime because the decorators applied on the object depends on the arguments that the user gave when calling the program"
The don't use decorators. Decorators are only syntactical support for wrappers, you can just as well use normal function/method calls instead.