I want a decorator that would add the decorated function to list, like this :
class My_Class(object):
def __init__(self):
self.list=[]
#decorator
def my_function(self)
print 'Hi'
I expect my_function to be added to self.list, but I just can't write this decorator. If I try to write it inside My_Class, then I would have to use #self.decorator, and self does not exist since we're outside any function. And if I try to write it out of My_Class, then I can't retrieve self from my_function.
I know quite similar questions exist, but they are overly complicated, and I'm just learning python and decorators.
You can't access self from the decorator, because the decorator is run at the time the function is defined, and at that time there are no instances of My_Class yet.
It's better to put the function list as a class attribute instead of an instance attribute. Then you can pass this list as a parameter to the decorator:
def addToList(funcList):
'''Decorator that adds the function to a given list'''
def actual_decorator(f):
funcList.append(f)
return f
return actual_decorator
class MyClass(object):
funcList = []
#addToList(funcList)
def some_function(self, name):
print 'Hello,', name
Now you can access MyClass.funcList to get the list of decorated functions.
There's nothing really special about writing decorators for bound functions (instance methods). For example, this simple example works fine:
def decorator(fn):
print "I'm decorating!"
return fn
class MyClass(object):
def __init__(self):
self.list = []
#decorator
def my_function(self):
print "Hi"
If you want to use self in your decorator, you'll treat your decorator the same as you'd treat any decorator that uses the function's args:
def decorator(fn):
def _decorator(self):
print "I'm decorating, and here's my list: %s!" % self.list
return fn(self)
return _decorator
Your list attribute should be a class attribute (and it should be renamed, because list is a builtin type). Then you can do something like this:
my_methods = []
def my_method(method):
my_methods.append(method)
return method
class MyClass(object):
my_methods = my_methods
#my_method
def my_function(self):
print 'Hi'
Related
I have to decorate a inherited method, but it decorates all inherited methods. Basically I have to create a decorator that will decorate just one method from the class.
The test looks like this
#my_decorator
class TestClass(Subclass):
pass
t = TestClass()
t.say_hi
Let's say my SubClass looks like this
class SubClass():
def __init__(self):
pass
def say_hi():
print("Hi")
def say_wow():
print("wow")
Now I have to make my_decorator, that has to decorate inherited function say_hi() to print("*****") before it prints "Hi"
I tried doing it like this, but than the decorator applies to all methods from SubClass
def my_decorator(cls)
def say_hi():
print("*********")
cls.say_hi()
return say_hi()
Naturally It applies to every function of the subclass, but how do I make it to apply to only a say_hi() function? -It also returns an TypeError "NoneType" object is not callable
First let us fix SubClass, because instance methods require an explicit instance parameter at definition time:
class SubClass():
def __init__(self):
pass
def say_hi(self):
print("Hi")
def say_wow(self):
print("wow")
Now you want the decorator to replace the say_hi method with a method that prints '****' before calling the original method. Le us write a decorator that just does that(*):
def my_decorator(cls):
orig = cls.say_hi # save the original method
def say_hi(self): # define a new one
print('****')
return orig(self) # ... calling the original method
cls.say_hi = say_hi # replace the method in the class
return cls
You can then use:
#my_decorator
class TestClass(SubClass):
pass
t = TestClass()
t.say_hi()
and get as expected:
****
Hi
(*) this is a very simple decorator that can only replace a say_hi(self) method: neither a different name, nor additional parameters, but decorators can be much smarter...
If you want to decorate a method, then decorate the method, not the class that contains it. If you want a new class, then the decorator applied to the class needs to return a class.
def print_banner(f):
def _(*args, **kwargs):
print("****")
f(*args, **kwargs)
return _
class SubClass():
def __init__(self):
pass
#print_banner
def say_hi(self, ):
print("Hi")
def say_wow(self):
print("wow")
For better encapsulation, I want to decorate instance methods with methods inside the same class.
class SomeClass(object):
#staticmethod
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
x = SomeClass()
x.do()
However, this piece of code raises TypeError: 'staticmethod' object is not callable
Now I make a workaround by defining a class and overload its new method to simulate a function, but it's eventually a class, not a function.
So can I access my functions inside the class scope?
Just get rid of that #staticmethod line. You want some_decorator to behave like a plain function, not like some kind of method.
The decorator is called when the class definition is being executed, before the class object itself exists. The normal method definitions inside a class are actually just plain old functions, they become methods dynamically each time they are called as attributes of the class instance (which turns them into bound methods). But while the class object itself is being built you can treat them as plain functions.
class SomeClass(object):
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
x = SomeClass()
x.do()
output
hello
world
BTW, you have an error in your decorator: it's returning wrapped() instead of wrapped.
As chepner mentions in the comments we can delete some_decorator so that it doesn't take up space in the class object after we've finished using it in the class definition. (If we accidentally try to call it we'll get an error). We could do del SomeClass.some_decorator after the class definition, but it's also perfectly valid to put a del statement inside the class definition:
class SomeClass(object):
def some_decorator(func):
def wrapped(self):
print 'hello'
return func(self)
return wrapped
#some_decorator
def do(self):
print 'world'
del some_decorator
I would like to create an object in python where a call to any method under it would be routed to a single method implementation. Also the code within this single method should use the name of the method called for processing or for its returned value.
For example, an object where calling any method name under it would print the method name.
>>> the_object.a_made_up_method()
You have called method "a_made_up_method"
(btw, if Mock could help, I'm all for it)
Maybe something like
class My():
def __getattr__(self, name):
def method():
print 'You have called method "{}"'.format(name)
return method
>>> a = My()
>>> a.a_made_up_method()
You have called method "a_made_up_method"
Here's another one, seems to work with *args and **kwargs:
class My():
def __getattr__(self, name):
return self.method(name)
def method(self, name):
def dostuff(*args, **kwargs):
print "I'm called as {}!".format(name)
print args, kwargs
return dostuff
how does one go about accessing a decorator from a base class in a child?
I assumed (wrongly) that the ffg. would work:
class baseclass(object):
def __init__(self):
print 'hey this is the base'
def _deco(func):
def wrapper(*arg):
res = func(*arg)
print 'I\'m a decorator. This is fabulous, but that colour, so last season sweetiedarling'
return res
return wrapper
#_deco
def basefunc(self):
print 'I\'m a base function'
This class works fine, but then I create a child class inheriting from this:
class otherclass(baseclass):
def __init__(self):
super(otherclass, self).__init__()
print 'other class'
#_deco
def meh(self):
print 'I\'m a function'
This won't even import properly, let alone run. #_deco is undefined. Trying baseclass._deco throws an unbound method _deco() error, which isn't really surprising.
Any idea how to do this, I'd really like to encapsulate the decorator in the class, but I'm not married to the idea and I'd need to call it in the base & the child class.
class baseclass(object):
def __init__(self):
print 'hey this is the base'
def _deco(func):
def wrapper(*arg):
res = func(*arg)
print 'I\'m a decorator. This is fabulous, but that colour, so last season sweetiedarling'
return res
return wrapper
#_deco
def basefunc(self):
print 'I\'m a base function'
#_deco
def basefunc2(self):
print "I'm another base function"
#no more uses of _deco in this class
_deco = staticmethod(_deco)
# this is the key. it must be executed after all of the uses of _deco in
# the base class. this way _deco is some sort weird internal function that
# can be called from within the class namespace while said namespace is being
# created and a proper static method for subclasses or external callers.
class otherclass(baseclass):
def __init__(self):
super(otherclass, self).__init__()
print 'other class'
#baseclass._deco
def meh(self):
print 'I\'m a function'
There is also python3-specific way to use that decorator in child class without mentioning parent, exactly as OP suggested. It requires decorator to be implemented in parent's metaclass (nice explanation of metaclases can be found here), using its __prepare__() method.
aaronasterling's answer is valid and preferred way how to solve that, I am posting this only as an interesting example to help others understand the basics of language. Use metaclasses only when there is no other way to achive what you need!
class metaclass(type):
#classmethod
def __prepare__(metacls, name, bases):
def _deco(func):
def wrapper(*arg):
res = func(*arg)
print('I\'m a decorator. This is fabulous, but that colour, so last season sweetiedarling')
return res
return wrapper
return {"_deco": _deco}
class baseclass(metaclass=metaclass):
def __init__(self):
print('hey this is the base')
#_deco
def basefunc(self):
print('I\'m a base function')
class otherclass(baseclass):
def __init__(self):
super(otherclass, self).__init__()
print('other class')
#_deco
def meh(self):
print('I\'m a function')
The sample code works well in python3:
>>> obj = otherclass()
hey this is the base
other class
>>> obj.meh()
I'm a function
I'm a decorator. This is fabulous, but that colour, so last season sweetiedarling
Important notes about __prepare__() method:
If present, it runs before the object body is executed
Its return value is used as local namespace for the class body at the begining of its evaluation (this way, decorator can be availabe from child's body without using parent's namespace)
It should be implemented as classmethod() and should return mapping object (i.e. dict)
If not present, empty mapping is used as initial local namespace.
How can I use a class instance variable as an argument for a method decorator in Python?
The following is a minimal example shows what I'm trying to do. It obviously fails as the decorator function does not have access to the reference to the instance and I have no idea how to get access to the reference from the decorator.
def decorator1(arg1):
def wrapper(function):
print "decorator argument: %s" % arg1
return function
return wrapper
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
#decorator1(self.var1)
def method1(self):
print "method1"
foo = Foo("abc")
foo.method1()
It's not going to work; the decorator is called during class creation time, which is long before an instance is created (if that ever happens). So if your "decorator" needs the instance, you have to do the "decorating" at instantiation time:
def get_decorator(arg1):
def my_decorator(function):
print "get_decorator argument: %s" % arg1
return function
return my_decorator
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
self.method1 = get_decorator(self.var1)(self.method1)
def method1(self):
print "method1"
foo = Foo("abc")
foo.method1()
Note that I changed the function names according to their meanings; the actual "decorator", i.e. the function that (potentially) modifies the method, is wrapper in your case, not decorator1.
Your “warper” function is actually a decorator, rather than a warper. Your “decorator1” function is a decorator constructor. If you want to have access to self.var1 in runtime you have to make a warper not decorator:
def decorator(function):
def wrapper(self,*args,**kwargs):
print "Doing something with self.var1==%s" % self.var1
return function(self,*args,**kwargs)
return wrapper
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
#decorator
def method1(self):
print "method1"
foo = Foo("abc")
foo.method1()
If you want to have more generic decorator, it's better idea to declare a callable class:
class decorator:
def __init__(self,varname):
self.varname = varname
def __call__(self,function):
varname=self.varname
def wrapper(self,*args,**kwargs):
print "Doing something with self.%s==%s" % (varname,getattr(self,varname))
return function(self,*args,**kwargs)
return wrapper
Using:
#decorator("var1")
Here's how we used to do this in the olden days.
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
def method1(self):
self.lock()
try:
self.do_method1()
except Exception:
pass # Might want to log this
finally:
self.unlock()
def do_method1(self):
print "method1"
def lock(self):
print "locking: %s" % self.arg1
def unlock(self):
print "unlocking: %s" % self.arg1
Now, a subclass only needs to o override do_method1 to get the benefits of the "wrapping". Done the old way, without any with statement.
Yes, it's long-winded. However, it doesn't involve any magic, either.
Do not try to juggle with decorators for this problem, it is going to make your code very complex and unpleasant to read.
You are attempting to construct a wrapper at class creation time with information that is only going to be available from instantiation time on. You can build the decorator dynamically at instantiation time, but still the outer class level at which you will need to apply the decorator to the methods will not have access to instance variables.
For avoiding the mess that implies to solve this problem with decorators, Python incorporates (from 2.7 on) a dedicated data model for solving this specific type of problem. It is called context manager and you can implement it by just using a generator.
from contextlib import contextmanager
def lock_file(file):
print('File %s locked' % file)
def unlock_file(file):
print('File %s unlocked' % file)
#contextmanager
def file_locked(arg1):
lock_file(arg1)
yield
unlock_file(arg1)
class Foo(object):
def __init__(self, arg1):
self.var1 = arg1
def method1(self):
with file_locked(self.var1) as f:
print "method1"
foo = Foo("abc")
foo.method1()
The decorator is executed when the class is defined, so you can't pass an instance variable to it.
Nested Methods made the trick, I was having a problem to passing self again while using it. Later I wrapped the function assignment with lambda to resolve the issue.
https://gist.github.com/kirankotari/e6959e74316352bfd06afa4d5ab8bd52