Custom property decorator that behaves like #property? - python

I'd like to create a custom Python decorator that will 1) check the value of a class attribute my_attribute before running the function, and 2) turn the class method into a property. I can achieve this as follows:
def my_decorator(f):
def wrapper(self, *args, **kwargs):
if self.my_attribute is None:
raise ValueError('No location set to model.')
return f(self, *args, **kwargs)
return wrapper
class MyClass:
my_attribute = None
#property
#my_decorator
def my_method():
return self. my_attribute
I'd like to know how I can edit the definition of my_decorator so that it makes the wrapped method a property. Essentially I'd like to avoid the use of #property and #my_decorator for each method, letting me write the class definition as
class MyClass:
my_attribute = None
#my_new_decorator
def my_method():
return self. my_attribute
I've looked up the declaration of the builtin #property decorator, but it's defined as a class and wasn't much help.
Any suggestions?

What if you changed your decorator definition to look like this?
def my_decorator(f):
#property
def wrapper(self, *args, **kwargs):
if self.my_attribute is None:
raise ValueError('No location set to model.')
return f(self, *args, **kwargs)
return wrapper
That should make the returned function behave like a property, and keep your custom error handling.

If one wants to keep/use also the original my_decorator, one could create a my_decorator_property as following:
def my_decorator_property(f):
#property
def wrapper(self, *args, **kwargs):
return my_decorator(f)(self, *args, **kwargs)
return wrapper

Related

Repeat methods for object of same class

I would like to create two objects such that methods for some of this objects will be repeated for other. Then I try to do this:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
getattr(self.another_tester, method.__name__)(*args, **kwargs)
return wrapper
class Tester():
def __init__(self, name):
self.name = name
def select_backup(self, tester):
self.another_tester = tester
#backup
def foo1(self, stop=False):
print("I am tester {}, do method foo1".format(self.name))
#backup
def foo2(self, stop=False):
print("I am tester {}, do method foo2".format(self.name))
#backup
def foo3(self, stop=False):
print("I am tester {}, do method foo3".format(self.name))
tester1 = Tester("A")
tester2 = Tester("B")
tester1.select_backup(tester2)
tester2.select_backup(tester1)
tester1.foo1()
tester2.foo2()
tester1.foo3()
I get RuntimeError: maximum recursion depth exceeded while calling a Python object for such code. class Tester have a lot of different methods (foo1, foo2, foo3, ...), and I would like to backup (repeat) each method. So I use decorator.
I can change decorator:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
try:
kwargs["stop"]
except KeyError:
getattr(self.another_tester, method.__name__)(stop=True, *args, **kwargs)
return wrapper
It's work, but I think there is more pythonic way to do it. Can anyone offer such way?
If your pair of objects are always instances of the same class (or at least, never have different implementations of the decorated method), you can change the decorator so that it directly calls the original method on the other instance, rather than getting the decorated version via getattr:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
method(self.another_tester, *args, **kwargs)
return wrapper
If you want to be able to support pairs of objects of different classes (and different method implementations), things need to be a bit more complicated. You can save a reference to the original undecorated method as an attribute on the wrapper function, then look it up when you need it:
def backup(method):
def wrapper(self, *args, **kwargs):
method(self, *args, **kwargs)
other_method = getattr(self.another_tester, method.__name__).orig_method
other_method(self.another_tester, *args, **kwargs)
wrapper.orig_method = method
return wrapper

Call decorator with class variable in python

Probably I am approaching the problem in the wrong way and there is a simpler solution, but here is my problem.
I have a decorator defined like this:
def my_decorator(argument):
def wrap(f):
def wrapped_f(*args, **kwargs):
.... # does something with argument
return wrapped_f
return wrap
then I defined a class that looks like this:
class MyClass(object):
def __init__(argument):
self.argument = argument
#my_decorator(self.argument) # !this is an error!
def my_method(self):
.... # does something
Clearly what I wrote is wrong because at the decorator level I cannot access self, but what's the right approach to solve this problem?
You could have the decorator access self.argument inside wrapped_f:
def mydecorator(f):
def wrapped_f(self, *args, **kwargs):
argument = self.argument
.... # does something with argument
return f(self, *args, **kwargs)
return wrapped_f
return wrap
If the variable being passed as argument changes depending on the function being decorated, you can pass it to the decorator as a string and use getattr to get it from self:
def my_decorator(arg_name):
def wrap(f):
def wrapped_f(self, *args, **kwargs):
argument = getattr(self, arg_name)
.... # does something with argument
return wrapped_f
return wrap
class MyClass(object):
def __init__(self, argument):
self.argument = argument
#my_decorator("argument")
def my_method(self):
.... does something

Conditionally disable caching decorator based on instance variable

I want to cache certain methods of a class - but only if a read_only flag is set on the instance of the class.
So, in the object below, I want the get() method to be cacheable, but only if self.readonly is True.
I can't conditionally use the decorator, because it's set when the class is defined, not when it's instantiated.
from beaker.cache import cache_regions, cache_region
cache_regions.update({
'long_term':{
'expire':86400,
'type':'dbm',
'data_dir':'/tmp',
'key_length': 32,
}
})
class Foo(object):
def __init__(self, read_only=True):
self.read_only = read_only
#cache_region('long_term')
def get(self, arg):
return arg + 1
You can use a decorator to call the right (cached or not) function checking the desired attribute:
def conditional(decorator):
def conditional_decorator(fn):
dec = decorator(fn)
def wrapper(self, *args, **kw):
if self.read_only:
return dec(self, *args, **kw)
return fn(self, *args, **kw)
return wrapper
return conditional_decorator
Use like this:
#conditional(cache_region('long_term'))
def get(self, arg):
return arg + 1

Methods decorated with a decorator class do not have the "self" argument frozen

I have a decorator declared as a class:
class predicated(object):
def __init__(self, fn):
self.fn = fn
self.fpred = lambda *args, **kwargs: True
def predicate(self, predicate):
self.fpred = predicate
return self
def validate(self, *args, **kwargs):
return self.fpred(*args, **kwargs)
def __call__(self, *args, **kwargs):
if not self.validate(*args, **kwargs):
raise PredicateNotMatchedError("predicate was not matched")
return self.fn(*args, **kwargs)
... and when I use it to wrap a method in a class, calling that method does not seem to set the instance of the object as the first argument. While this behavior is not exactly unexpected, how would I go about getting self to be frozen when the method becomes an instance method?
Simplified example:
class test_decorator(object):
def __init__(self, fn):
self.fn = fn
def __call__(self, *args, **kwargs):
return self.fn(*args, **kwargs)
class Foo(object):
#test_decorator
def some_method(self):
print(self)
Foo().some_method()
Expected instance of foo, instead get an error saying 0 arguments were passed.
Figured it out - needed to define a __get__ method in order to create a MethodType binding like so:
def __get__(self, obj, objtype=None):
return MethodType(self, obj, objtype)
which creates a MethodType object when invoking the method on an object that freezes the self argument.

Python: How do I access an decorated class's instance from inside a class decorator?

Here's an example of what I mean:
class MyDecorator(object):
def __call__(self, func):
# At which point would I be able to access the decorated method's parent class's instance?
# In the below example, I would want to access from here: myinstance
def wrapper(*args, **kwargs):
return func(*args, **kwargs)
return wrapper
class SomeClass(object):
##self.name = 'John' #error here
name="John"
#MyDecorator()
def nameprinter(self):
print(self.name)
myinstance = SomeClass()
myinstance.nameprinter()
Do I need to decorate the actual class?
class MyDecorator(object):
def __call__(self, func):
def wrapper(that, *args, **kwargs):
## you can access the "self" of func here through the "that" parameter
## and hence do whatever you want
return func(that, *args, **kwargs)
return wrapper
Please notice in this context that the use of "self" is just a convention, a method just uses the first argument as a reference to the instance object:
class Example:
def __init__(foo, a):
foo.a = a
def method(bar, b):
print bar.a, b
e = Example('hello')
e.method('world')
The self argument is passed as the first argument. Also your MyDecorator is a class emulating a function. Easier to make it an actual function.
def MyDecorator(method):
def wrapper(self, *args, **kwargs):
print 'Self is', self
return method(self, *args, **kwargs)
return wrapper
class SomeClass(object):
#MyDecorator
def f(self):
return 42
print SomeClass().f()

Categories

Resources