Python decorators in classes Error - python

class A(object):
def wrapped(self, func):
func.string = self.get_something()
return func
def get_something(self):
return "something"
#wrapped
def main(self):
print self.main.string
a = A()
a.main()
TypeError: wrapped() takes exactly 2 arguments (1 given)

wrapped() is called at the definition of main(). When it is called, it is not given self, because there is no instance of A that can be given to wrapped(). To do what you want, don't use decorators. Instead, define an __init__() method that assigns the right attributes. I don't know why you want it to be an attribute of self.main(), however. Generally it is not a good idea to add attributes to functions. You already have a class; use it. Just define self.string instead of self.main.string.

Related

Method is considering "self" as an argument variable

I have two classes, in the second class I defined a method that instantiates an object from the first class, assigns a value to one of its attribute, and then prints it.
The problem is when I call the function using:
test.Pprint(5)
I get the error.
TypeError: Pprint() missing 1 required positional argument: 'x'
My code:
class node():
def __init__(self, test):
self.test = test
class test():
def Pprint(self, x):
rootnode = node(x)
print(rootnode.test)
When I deleted the keyword self everything worked as intended. As far as I know self shouldn't be considered an argument. What's the problem?
This is python magic. When called from the class object test.Pprint(x) its a function requiring 2 arguments. When called from an instance, test().Pprint(x) python converts it to a method and automatically adds a reference to the instance as the first parameter. In your case, Pprint doesn't use any of the functionality of the class, so it can be a static method.
class test:
#staticmethod
def Pprint(x):
rootnode = node(x)
print(rootnode.test)
Now it works either from the class or an instance
test.Pprint(x)
t = test()
t.Pprint(x)

Proper form to call a method in Python?

I was trying some examples on class inheritance and stumbled, on how to access the methods. In tutorials, the proper form is always Class().method(), however apparently, that's not the only solution.
class A():
def open(self):
print("class A method open")
class B():
def open(self):
print("class B method open")
def close(self):
print("class B method close")
class D(A, B):
def end_reached(self):
print("end reached")
## Why do both of the following lines work & produce the same output?
D().open()
D.open(1)
I expected the last line to give an error, however, the output is the same as from the line above. It did give an error on missing parameter, if the method gets called like D.open(), but giving it just any parameter works.
Is there any difference between the two lines?
The difference lays in the descriptor protocol, specifically in how descriptors provide Python's object-oriented features.
open is a class attribute of various classes; in each case, though, it has the type function. That type implements the descriptor protocol, via the definition of function.__get__, by returning either a function object or a method object, depending on whether open is accessed via the class itself or an instance of the class.
Instance lookup
Given
d = D()
the attribute lookup d.open is equivalent to
type(d).__dict__['open'].__get__(d, D)
which returns a method which wraps the original function. When called, the method passes a reference to d and any of its own arguments to the wrapped function.
f = d.open # The return value of __get__
f() # open(d)
Class lookup
The attribute lookup D.open is equivalent to
D.__dict__['open'].__get__(None, D)
which returns the function open itself.
f = D.open # The return value of __get__
f(1) # open(1)
In order to learn about this, I have to go in depth about the properties of functions and methods.
Accessing a class field of a class via its instances checks if there are __set__ and/or __get__ methods. If they are, the call happens via __get__.
Functions work this way: if a function is a class field of a class (in short, methods), they are called via function.__get__(object)(*args, **kwargs).
And here, we have a difference between Python 2 and Python 3:
Python 2 unconditionally prepends the given object to the argumends and calls the underlying function with function(object, *args, **kwargs). object gets assigned to self then, which by convention is the name of the first parameter of a method.
Python 3 checks if the given object is an instance of the class as it should be. If it isn't, we get an exception. If it is, the call gets processed as above.
Example:
def outer_f(self):
return "f called with", self
class C(object):
f = outer_f
def g(self):
return "g called with", self
c = C()
print(outer_f(c))
print(C.f(c))
print(C.f.__get__(c)())
print(c.f())
print(C.g(c))
print(C.g.__get__(c)())
print(c.g())
class A():
def open(self):
print(self)
print("class A method open")
class B():
def open(self):
print("class B method open")
def close(self):
print("class B method close")
class D(A, B):
def end_reached(self):
print("end reached")
## Why do both of the following lines work & produce the same output?
D().open() # Okay
D.open(1) # BAD
output
<__main__.D object at 0x0045FE90>
class A method open
1
class A method ope
You might want to look at a static method
class A():
#staticmethod
def open():
print("open ran")
class D(A):
def end_reached(self):
print("end reached")
D.open() # Good
output
open ran
That's not an inheritance problem.
As you can see in your method declaration, first argument is self.
D() just creates an object of class D. If you do anything on the object, that object is automatically passed as first argument.
However, when doing D.open(1) you don't invoke the method on the object, so the first argument in the method is first argument passed to it == your code assumes self=1. Because your methods behave like static methods (you don't use self inside them), you don't access self's inside, so the code doesn't break.
But if your open method invoked anything from self (another method or a field),
then you would get an attribute error - because int object probably doesn't have a method like that.
Examples from build-in classes:
"aaa".split()
str.split("aaa")
They both behave the same.
"aaa" is of type str, so interpreted looks into class str for the method and automatically passes "aaa" as self. (Just like your D() is of type D. And d=D(), then d.open() will still know the type.)
The other one shows which class to ask for the method (str) but still needs an argument self. The way we define methods shows that "self" is really an argument as well, so we can just pass it.

what is the difference of self.function vs function?

Assume I have a class CLASS and I create a methodfnc in it
class CLASS():
def __init__(self,arg):
#initizalize
self.arg = arg
def fnc(self):
print(self.arg)
if I (in my class) wants to call fnc in a method prnt I can do it in two different ways, one using self.fnc and one just using fnc
class CLASS():
def __init__(self,arg):
#initizalize
self.arg = arg
def fnc(self):
print(self.arg)
def prnt(self):
self.fnc() #Method one
fnc() #Method two
which both seems to be working. I do know the self argument and how it works, but I do not understand the difference of the two method/function calls.
This should not work. It works because CLASS in your code is not actually a class since you used keyword def instead of class to define it.
What you actually did is define a function CLASS, which when executed defines some other functions.
To correct this declare your class like this:
class CLASS:
And your second call will raise a NameError probably because fnc does not exist in the scope of your method.

How to distinguish between function and a method in decorator

I am trying to write decorator that will do different job depending on that whether it will be decorating method or a function.
I tried something like this:
def dec(f):
def wrapper(*v, **kv):
if '__class__' in dir(f):
text = "Its a method"
else:
text = "Its a function"
print(text)
return f(*v, **kv)
return wrapper
class A:
#dec
def f(self):
pass
#dec
def f():
pass
a = A()
a.f()
f()
It returns:
Its a method
Its a method
But this won't work since all functions also have also __class__ attribute :) I tried also to do inspect.ismethod on A.f but A.f is treaten just like function but in A namespace and it return of course false. It would return True if we have done inspect.ismethod(a.f) where a is instance of A but I can't wait to have instance.
As Martijn already explained in his comments, being defined in a class statement's body doesn't make a function a method - the method object is created at lookup time, as explained here - so what you are decorating is and will always (well almost cf below) be a function. FWIW, Python's "methods" are just thin wrappers around the function, class and (eventually) instance.
The only case where you might get something else than a function are classmethods and staticmethods (in which case you'll get resp. a classmethod or staticmethod object instead), and even then it depends on the decorators order.
If you want to get at the method's current instance (or class) in your decorator, you know that it will always be the first positional argument - but this might not be of great help... To make a long story short, you have to either explicitely tell your decorator if it should treat the function as plain function or method, or decorate it with a custom descriptor in which case you'll know if the function has - or not - been looked up as a method... A naïve implementation migh look like this:
class MyDecorator(object):
def __init__(self, func, instance=None):
self.func = func
self.instance = instance
def __call__(self, *args, **kw):
if self.instance:
print "is method - instance : %s" % self.instance
args = (self.instance,) + args
else:
print "is_function"
return self.func(*args, **kw)
def __get__(self, instance, cls):
return type(self)(self.func, instance)

Python decorator, self is mixed up [duplicate]

This question already has answers here:
Decorating class methods - how to pass the instance to the decorator?
(3 answers)
Closed 3 years ago.
I am new to Python decorators (wow, great feature!), and I have trouble getting the following to work because the self argument gets sort of mixed up.
#this is the decorator
class cacher(object):
def __init__(self, f):
self.f = f
self.cache = {}
def __call__(self, *args):
fname = self.f.__name__
if (fname not in self.cache):
self.cache[fname] = self.f(self,*args)
else:
print "using cache"
return self.cache[fname]
class Session(p.Session):
def __init__(self, user, passw):
self.pl = p.Session(user, passw)
#cacher
def get_something(self):
print "get_something called with self = %s "% self
return self.pl.get_something()
s = Session(u,p)
s.get_something()
When I run this, I get:
get_something called with self = <__main__.cacher object at 0x020870F0>
Traceback:
...
AttributeError: 'cacher' object has no attribute 'pl'
for the line where I do self.cache[fname] = self.f(self,*args)
The problem - Obviously, the problem is that self is the cacher object instead of a Session instance, which indeed doesn't have a pl attribute. However I can't find how to solve this.
Solutions I've considered, but can't use - I thought of making the decorator class return a function instead of a value (like in section 2.1 of this article) so that self is evaluated in the right context, but that isn't possible since my decorator is implemented as a class and uses the build-in __call__ method. Then I thought to not use a class for my decorator, so that I don't need the __call__method, but I can't do that because I need to keep state between decorator calls (i.e. for keeping track of what is in the self.cache attribute).
Question - So, apart from using a global cache dictionary variable (which I didn't try, but assume will work), is there any other way to make this decorator work?
Edit: this SO question seems similar Decorating python class methods, how do I pass the instance to the decorator?
Use the descriptor protocol like this:
import functools
class cacher(object):
def __init__(self, f):
self.f = f
self.cache = {}
def __call__(self, *args):
fname = self.f.__name__
if (fname not in self.cache):
self.cache[fname] = self.f(self,*args)
else:
print "using cache"
return self.cache[fname]
def __get__(self, instance, instancetype):
"""Implement the descriptor protocol to make decorating instance
method possible.
"""
# Return a partial function with the first argument is the instance
# of the class decorated.
return functools.partial(self.__call__, instance)
Edit :
How it's work ?
Using the descriptor protocol in the decorator will allow us to access the method decorated with the correct instance as self, maybe some code can help better:
Now when we will do:
class Session(p.Session):
...
#cacher
def get_something(self):
print "get_something called with self = %s "% self
return self.pl.get_something()
equivalent to:
class Session(p.Session):
...
def get_something(self):
print "get_something called with self = %s "% self
return self.pl.get_something()
get_something = cacher(get_something)
So now get_something is an instance of cacher . so when we will call the method get_something it will be translated to this (because of the descriptor protocol):
session = Session()
session.get_something
# <==>
session.get_something.__get__(get_something, session, <type ..>)
# N.B: get_something is an instance of cacher class.
and because :
session.get_something.__get__(get_something, session, <type ..>)
# return
get_something.__call__(session, ...) # the partial function.
so
session.get_something(*args)
# <==>
get_something.__call__(session, *args)
Hopefully this will explain how it work :)
Closures are often a better way to go, since you don't have to muck about with the descriptor protocol. Saving mutable state across calls is even easier than with a class, since you just stick the mutable object in the containing scope (references to immutable objects can be handled either via the nonlocal keyword, or by stashing them in a mutable object like a single-entry list).
#this is the decorator
from functools import wraps
def cacher(f):
# No point using a dict, since we only ever cache one value
# If you meant to create cache entries for different arguments
# check the memoise decorator linked in other answers
print("cacher called")
cache = []
#wraps(f)
def wrapped(*args, **kwds):
print ("wrapped called")
if not cache:
print("calculating and caching result")
cache.append(f(*args, **kwds))
return cache[0]
return wrapped
class C:
#cacher
def get_something(self):
print "get_something called with self = %s "% self
C().get_something()
C().get_something()
If you aren't completely familiar with the way closures work, adding more print statements (as I have above) can be illustrative. You will see that cacher is only called as the function is defined, but wrapped is called each time the method is called.
This does highlight how you need to be careful with memoisation techniques and instance methods though - if you aren't careful to account for changes in the value of self, you will end up sharing cached answers across instances, which may not be what you want.
First, you explicitly pass cacher object as first argument in the following line:
self.cache[fname] = self.f(self,*args)
Python automatically adds self to the list of arguments for methods only. It converts functions (but not other callables as your cacher object!) defined in class namespace to methods. To get such behavior I see two ways:
Change your decorator to return function by using closures.
Implement descriptor protocol to pass self argument yourself as it's done in memoize decorator recipe.

Categories