using decorator arguments for switching - python

At the flaks library, we can use decorator like switch case. (Did I understand well?)
app.route('')
So...I would like to make some switch statement with decorators and arguments,
like:
#color('pink')
def _pink_power(self):
print("wow")
#color('blue')
#color('red')
def _powerpower(self):
print("god!!!!")
def input(color):
I don't know what to do in here..
if color is pink, print wow!
I was struggling to figure out this quite a long time, but I couldn't make it. Is it impossible do you think?

Here's a relatively simple way to do it (although I recommend that you change the name of the input function at the end because it conflicts with a built-in of the same name):
class color:
_func_map = {}
def __init__(self, case):
self.case = case
def __call__(self, f):
self._func_map[self.case] = f
return f
#classmethod
def switch(cls, case):
cls._func_map[case]()
#color('pink')
def _pink_power():
print("wow")
#color('blue')
#color('red')
def _powerpower():
print("god!!!!")
def input(colorname):
color.switch(colorname)
input('pink') # -> wow
input('blue') # -> god!!!!
input('red') # -> god!!!!
Enhancement
You could support having a default case like C/C++'s switch statements support that will be used when there's no matching colorname:
class color:
DEFAULT = '_DEFAULT'
_func_map = {}
def __init__(self, case):
self.case = case
def __call__(self, f):
self._func_map[self.case] = f
return f
#classmethod
def _default(cls):
raise ValueError('Unknown color!')
#classmethod
def switch(cls, case):
cls._func_map.get(case, cls._default)()
The _default() method added to the class raises an exception when it's invoked:
input('lavender') # -> ValueError: Unknown color!
However you can override that by defining your own error-handler function:
#color(color.DEFAULT) # Override default function.
def my_default():
print("loser!")
input('lavender') # -> loser!

You...could do this, but I'm not sure it's a great idea to.
import contextlib
class Colors(object):
def __init__(self):
self.__colors = dict()
def register(self, colorname):
def wrapper(f):
#contextlib.wraps(f)
def wrapper(*args, **kwargs):
return f(*args, **kwargs)
self.__colors[colorname] = wrapper
return wrapper
def __getitem__(self, item):
return self.__colors[item]
colors = Colors()
#colors.register("pink")
def _pink_power():
print("wow")
#colors.register("blue")
#colors.register("red")
def _powerpurple():
print("god!!!!!")
def input(colorname):
colors[colorname]()

Here's another way to do it, which I think is an improvement over than my original answer because it's more generic in the sense doesn't require defining a new class for every switch context which makes it more reusable.
The decorator is implemented as a class named lowercase switch and the function it's applied to becomes the default function called when a value doesn't match those of any of the associated "case" functions defined.
class switch:
""" Value dispatch function decorator.
Transforms a function into a value dispatch function, which can have
different behaviors based on the value of its first argument.
"""
def __init__(self, func):
self.func = func
self.registry = {}
def case(self, value, func=None):
if func is None:
return lambda f: self.case(value, f)
self.registry[value] = func
return func
def __call__(self, *args, **kwargs):
return self.registry.get(args[0], self.func)(*args, **kwargs)
Sample usage:
#switch
def color(name): # Default when no match.
print('loser!')
#color.case('pink')
def _pink_power(name):
print("wow")
#color.case('blue')
#color.case('red')
def _powerpower(name):
print("god!!!!")
def input(name):
color(name)
input('pink') # -> wow
input('blue') # -> god!!!!
input('red') # -> god!!!!
input('lavender') # -> loser!

Related

How to wrap a decorator around another classes method?

I have created a decorator which I am using to manage logging. I want logging to occur before and after the decorated function runs. The function works fine when interacting with very basic functions, however, when interacting with methods that are a part of other classes, things break. I suspect the issue is a result of there being 2 self arguments. Do you have any idea how to resolve it?
Simplified Decorator Class
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_type = logging_type
def __call__(self, decorated_function:callable):
self.func = decorated_function
return getattr(self, self.logging_type)
def debug(self, *args, **kwargs):
print("starting function")
output = self.func(*args, **kwargs)
print("Completing Function")
return output
We see that the decorator works on basic functions:
#Logger(logging_type="debug")
def simple_function(x):
return x**2
In [2]: simple_function(3)
starting function
Completing Function
Out[2]: 9
However, fails when work with other classes:
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
#Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
In [16]: test = BigClass()
...: test.cool_function(3)
starting function
It then hits a type error on the output line:
TypeError: cool_function() missing 1 required positional argument: 'input1'
Ideas?
By all means read juanpa.arrivillaga's informative answer. But here is a simpler approach. In writing a class decorator of this type, __call__ should return an ordinary function instead of a member function, like this:
class Logger:
def __init__(self, logging_type:str = 'debug'):
self.logging_function = getattr(self, logging_type)
def __call__(self, decorated_function: callable):
def f(*args, **kwargs):
return self.logging_function(decorated_function, *args, **kwargs)
return f
def debug(self, decorated_function, *args, **kwargs):
print("starting function")
output = decorated_function(*args, **kwargs)
print("Completing Function")
return output
#Logger(logging_type="debug")
def simple_function(x):
return x**2
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
#Logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
print(simple_function(12))
test = BigClass()
print(test.cool_function(3))
OUTPUT:
starting function
Completing Function
144
starting function
Completing Function
13
The problem is that you are decorating your function with a bound-method type, look at type(BigClass.cool_function), you'll see something like: <bound method Logger.debug of <__main__.Logger object at 0x11081f7c0>. Since bound-method objects aren't functions, they don't implement the descriptor protocol to bind the instance as the first argument, hence, the instance is never passed implicitly as the first argument.
The best solution is to avoid class-based decorators to begin with. Here's how you could implement what you are doing using function-based decorators, using the closures to maintain internal state:
from functools import wraps
def logger(*, logging_type): # I prefer keyword-only arugments for decorators, but that is your call...
def decorator(func):
#wraps(func)
def debug(*args, **kwargs):
print("starting function")
result = func(*args, **kwargs)
print("ending function")
return result
#wraps(func)
def another_option(*args, **kwargs):
print("another option")
return func(*args, **kwargs)
options = {"debug": debug, "another_option": another_option}
return options[logging_type]
return decorator
class BigClass:
def __init__(self, stuff = 10):
self.stuff = stuff
#logger(logging_type="debug")
def cool_function(self, input1: int):
return self.stuff + input1
#logger(logging_type="another_option")
def another_function(self):
return self.stuff*100

Is there a simpler syntax for member decorators for methods?

I changed a class which had a function that had to be ran prior to running a number of other functions. The "prior-to-others" function is now a decorator. But the syntax, which I came up with, seems very unintuitive.
It used to be something like this:
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
def request1(self):
self._wait_on_throttlers()
...
def request2(self):
self._wait_on_throttlers()
...
And now it's like this:
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
def _throttled(f):
def inner(self, *args, **kwargs):
self._wait_on_throttlers()
return f(self, *args, **kwargs)
return inner
#_throttled
def request1(self):
...
#_throttled
def request2(self):
...
And, while I think the use of this decorator made the code more clear, the implementation of this decorator took some doing. It's also very fragile and hard to read. For example, if the inner return line return f(self, *args, **kwargs) is changed to return self.f(*args, **kwargs), then it won't work anymore.
This seems to do with the order in which the elements of the class are compiled. I am also afraid that this would break in future versions of Python. I am using Python 3.6.8.
Is there an accepted and/or recommended way to make such class-member decorators of class methods which would be less counter-intuitive and less fragile?
For the sake of a minimal reproducible example, the ... can be considered to be a pass statement and the class TempThrottler can be defined as below (this isn't the actual implementation, but it's enough to satisfy the example above):
class TempoThrottler:
def __init__(self, t):
pass
def isallowed(self):
from random import randint
return (True, False)[randint(0,1)]
def mustwait(self):
return 1
def consume(self):
pass
Below is a runnable example that illustrates my suggestion of how it would be possible to move the decorator function completely out of the class:
from random import randint
import time
class TempoThrottler:
def __init__(self, t):
pass
def isallowed(self):
# return [True, False](randint(0,1))
return [True, False][randint(0,1)]
def mustwait(self):
return 1
def consume(self):
pass
# Decorator not in class.
def _throttled(f):
def inner(self, *args, **kwargs):
self._wait_on_throttlers()
return f(self, *args, **kwargs)
return inner
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
#_throttled
def request1(self):
print('in request1()')
...
#_throttled
def request2(self):
print('in request2()')
...
s = Session(range(3))
s.request1() # -> in request1()
s.request2() # -> in request2()
While this maybe more complicated, at the first glance, in order to address that
the decorator is not bound to an instance of the class
this decorator is coupled with the class
I am going to make the decorator parametrized and move it outside of the class. Then the instance of the decorator specialized for a specific pre-call method can be assigned to a class variable. And then this class variable can be used as the actual decorator.
def precall_decorator(precall_f):
def decor(f):
def inner(self, *args, **kwargs):
precall_f(self)
return f(self, *args, **kwargs)
return inner
return decor
class Session:
def __init__(self, ts):
self.tempo_throttlers = [TempoThrottler(t) for t in ts]
...
def _wait_on_throttlers(self):
for th in self.tempo_throttlers:
if not th.isallowed():
time.sleep(th.mustwait())
th.consume()
...
_throttled = precall_decorator(_wait_on_throttlers)
#_throttled
def request1(self):
...
#_throttled
def request2(self):
...

How to add a method into a dict using decorator?

What I want is something like flask routing register:
class ClassName(object):
def __init__(self):
self.handle_dict = {}
def handle(data):
h = self.handle_dict.get(data)
h()
#handle_route("open")
def open_handle()
"""do something"""
pass
#handle_route("close")
def close_handle()
"""do something"""
pass
def handle_route()
"""How to implement this warpper?"""
I know I could write the a dict in the __init__ function:
self.handle_dict = {"open":self.open_handle, "close":self.colse_handle}
But the decorator way looks more clear, right?
I have tried
def handle_router(self, path):
def warpper(f):
self.handle_map[path] = f
return f
return warpper
but self is not allow in decorator args, #handle_router(self, "path") is invalid.
Effectively, the handle dict should be some sort of class attribute, as it is filled at class definition time and all instances share the same route-handler mapping. However, the class does not exist yet when the decorator is called and evaluated. You could do something like the following:
from functools import partial
class A:
router = {}
def handle(self, data):
m = self.router.get(data)
return m(self)
#partial(router.setdefault, 'open')
def open_handle(self):
return 5
>>> a = A()
>>> a.handle('open')
5
Or be more explicit and extract the routing functionailty to a reuasable class:
from functools import partial
class Router(dict):
__call__ = lambda self, route: partial(self.setdefault, route)
class A:
router = Router()
def handle(self, data):
m = self.router.get(data)
return m(self)
#router('open')
def open_handle(self):
return 5
Instead of decorating the functions, you could just use a consistent naming convention:
class ClassName(object):
def handle(self, type):
getattr(self, '{}_handle'.format(type))()
def open_handle(self):
print('open_handle')
def close_handle(self):
print('close_handle')
ClassName().handle('open')
>>> open_handle

DRYout a class-based chainable API

I have written a chainable API (in a similar style to jQuery) as follows:
class ChainableAPI:
def show(self):
self.visibility = 1
return self
def goBlue(self):
self.color = 'Blue'
return self
def die(self):
self.life = 0
return self
chainableAPI = ChainableAPI()
(chainableAPI
.goBlue()
.show()
.die()
.goBlue())
Notice how each method of ChainableAPI ends with return self. Is there a way to have all methods to return self automatically? That way I do not have to specify return self manually for each method.
You could use a decorator:
import functools
def chainable(func):
#functools.wraps(func)
def decorated(*args, **kwargs):
func(*args, **kwargs)
return args[0]
return decorated
Then the class would look like this:
class Foo(object):
#chainable
def show(self):
self.visibility = True
#chainable
def go_blue(self):
self.color = 'blue'
But is it better than just writing return self?
Also, a class decorator could be written to apply the chainable decorator to all methods not starting with _:
def all_chainable(klass):
for k, v in klass.__dict__.items():
if not k.startswith('_') and callable(v):
setattr(klass, k, chainable(v))
return klass
Example:
#all_chainable
class Foo(object):
def show(self):
self.visibility = True
Still, no matter how cool it may be I still think you should just use return self. But this shows how powerfull Python is.

Create decorator that can see current class method

Can you create a decorator inside a class that will see the classes methods and variables?
The decorator here doesnt see: self.longcondition()
class Foo:
def __init__(self, name):
self.name = name
# decorator that will see the self.longcondition ???
class canRun(object):
def __init__(self, f):
self.f = f
def __call__(self, *args):
if self.longcondition(): # <-------- ???
self.f(*args)
# this is supposed to be a very long condition :)
def longcondition(self):
return isinstance(self.name, str)
#canRun # <------
def run(self, times):
for i in xrange(times):
print "%s. run... %s" % (i, self.name)
There's no real need to implement this decorator as a class, and there's no need to implement it inside the definition of the Foo class. The following will suffice:
def canRun(meth):
def decorated_meth(self, *args, **kwargs):
if self.longcondition():
print 'Can run'
return meth(self, *args, **kwargs)
else:
print 'Cannot run'
return None
return decorated_meth
Using that decorator seems to work:
>>> Foo('hello').run(5)
Can run
0. run... hello
1. run... hello
2. run... hello
3. run... hello
4. run... hello
>>> Foo(123).run(5)
Cannot run
You can have it be a class but you need to use the descriptor protocol
import types
class canRun(object):
def __init__(self, f):
self.f = f
self.o = object # <-- What the hell is this about?
def __call__(self, *args):
if self.longcondition():
self.f(*args)
def __get__(self, instance, owner):
return types.MethodType(self, instance)
You always need to use a descriptor when you want to decorate class methods with a class instance using the __call__ method. The reason for this is that there will only be one self passed in which refers to the instance of the decorating class and not the instance of the decorated method.
My previous answer was made in haste. If you're wanting to write a decorator you should really use wraps from the functools module. It takes care of the hard stuff for you.
A proper way to define the canRun decorator is:
from functools import wraps
def canRun(f):
#wraps(f)
def wrapper(instance, *args):
if instance.longcondition():
return f(instance, *args)
return wrapper
The canRun function should be defined outside of the class.

Categories

Resources