I'm learning python design patterns from github repo faif/python-patterns and found the example chain_of_responsibility implements abstractmethod check_range as staticmethod.
My question is, is there any benefit other than less typing a self?
Simplify code is
from abc import ABC, abstractmethod
class A(ABC):
#abstractmethod
def foo(self, x):
pass
class B(A):
#staticmethod
def foo(x):
print("B.foo", x)
# both the two works
B.foo(1)
b = B()
b.foo(2)
There's no particular benefit. Sure, not all the check_range methods need an instance of the handler class that defines it, so they are declared as static methods.
But there's no reason any of the classes in your linked page need to exist in the first place, because in Python you can just store a function itself in a list, rather than storing some other dummy object that has an equivalent method.
Here's how you can implement "chain of responsibility" in Python idiomatically.
def check_range0(request):
return request in range(10)
# A closure can be used in place of a class
def make_check_range1():
start = 10
stop = 20
return lambda request: request in range(start, stop)
# Another way of using a closure in place of a class
def make_check_range2(start, stop):
return lambda request: request in range(start, stop)
def fallback(request):
print("No handler for {request}")
handlers = [check_range0, make_check_range1(), make_check_range2(20, 30)]
for request in requests:
if any((handler:=f)(request) for f in handlers):
print(f"{request} handled by {handler.__name__}")
else:
fallback(request)
A simple list takes the place of the linked list implied by Handler. Each subclass of Handler is replaced by a regular function (or a function that returns a closure, just to emphasize that a class is not necessary to provide or store state). The any function implements the iteration provided by Handler.handle.
If you really want a handler class, you can define it more simply than the example.
class Handler(ABC):
def __init__(self, handlers=None):
if handlers is None:
handlers = []
self.handlers = handlers
self.fallback = lambda request: pass
def add_handler(self, f):
self.handlers.append(f)
# Barely necessary; you can set the fallback
# attribute on a Handler instance yourself.
def set_fallback(self, f):
self.fallback = f
def handle(self, request):
# An alternative to any()
for h in self.handlers:
if h(request):
break
else:
self.fallback(request)
h = Handler([check_range0])
h.add_handler(make_check_range1())
h.add_handler(make_check_range2(20, 30))
def fallback(request):
print(f"No handler for {request}"
h.fallback = fallback
# h.set_fallback(fallback)
for request in requests:
h.handle(request)
Related
I have a python object that has various attrs, one of them is check=None.
class MenuItem:
check: bool = True
During the __init__() process, it parses it's own attrs and looks if they are callable. If so, it calls the function, and replaces it's instance variable with the result of the function:
def __init__(self):
self.request = ...
if callable(self.check):
self.check = self.check(self.request)
The purpose is to have subclasses that may replace class attrs by lambda functions:
class MainMenuItem(MenuItem):
check = lambda request: request.user.is_authenticated
So far, so good. But as the calling of instance methods implicitly adds self as first parameter, I would have to write every lambda (or external function) as lambda self, request: ... - external functions must be def check_superuser(self, request): ... - which IMHO looks bad.
Is there a way in Python to call a function from a method "as staticmethod"?
Like
if callable(self.check):
self.check = staticmethod(self.check)(self.request)
(this obviously doesn't work)
Any hint's welcome. Do I completely think wrong?
Is this what you are looking for?
class A:
check: bool = True
def __init__(self):
self.request = 'request'
if callable(self.check):
self.check = self.__class__.check(self.request)
class B(A):
check = lambda request: len(request)
b = B()
print(b.check)
outputs 7
I need a class router (for lack of a better word). The router needs to instantiate a class & call a function of that class instance based on variables passed to it.
How do I properly define the class function?
How do I properly call the class function?
Example code:
class ClassWorker1:
def function_1(self):
print('1a')
def function_2(self):
print('2a')
def function_3(self):
print('3a')
class ClassWorker2:
def function_1(self):
print('1b')
def function_2(self):
print('2b')
def function_3(self):
print('3b')
class ClassRouter(object):
def __init__(self, class_name, class_function):
self.class_instance = class_name()
self.class_function = class_function
self.main()
def main(self):
# how should I call the class function here?
self.class_instance.class_function()
return
a = 1
b = 1
if a == 1:
class_name = ClassWorker1
else:
class_name = ClassWorker1
if b == 1:
# Strings won't work as class function definition
# I won't know the class at this point. I will only know
# the shared function name at this point.
# how could this class function be defined directly?
class_function = 'function_1'
elif b == 2:
class_function = 'function_2'
else:
class_function = 'function_3'
ClassRouter(class_name, class_function)
I need a class router (for lack of a better word).
Are you sure you need a class for this ?
The router needs to instantiate a class & call a function of that class instance
When it belongs to a class or instance, a function is usually named a "method". Not really important but it makes things clearer. Also, an "instance" is obviously always, by definition, an instance of a class ;)
How do I properly define the class function?
How do I properly call the class function?
Does the router really have to be a class ? But anyway...
There are a couple distinct issues here (I of course assume you need something that's generic enough).
The first one is that your class (the one that will be instanciated by the "router") constructor may need some args - position or named or both. If it's the router's duty to instanciate the class (but should it be ?), you'll have to pass those args (both position and named) to the router. And since your router has to be generic (else it's useless) you cannot explicitely name these args in your router's constructor.
Hopefully, Python has a way to "unpack" tuples (for position args) and dicts (for named args) when calling a function, using respectively the * and ** operators at call time, ie:
def somefunc(arg1, arg2, arg3="foo", arg4=None):
print arg1, arg2, arg3, arg4
args = ("one", "two", "three")
kwargs = {"arg4": "four"}
somefunc(*args, **kwargs)
This let you pass arguments to a function in a generic way.
So if you want your router to be in charge of instanciating the "target" class, you'll have to support this:
class Router(object):
def __init__(self, cls, clsargs=None, clskwargs=None):
if clsargs is None:
clsargs = ()
if clskwargs is None:
clskwargs = {}
self._obj = cls(*clsargs, **clskwargs)
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker, clsargs=("foo",))
# or
r = Router(Worker, clskwargs={"name":"foo"})
Now note that at this point you really don't gain anything (except for more code) from having the router instanciating the Worker - since you need to have the Worker class and it's constructor's args to instanciate the router, you could as well just instanciate the Worker yourself and pass the Worker instance to the router:
Since you must have a reference to the class passed to the router (else you can't pass it ), you could as well
class Router(object):
def __init__(self, obj):
self._obj = obj
class Worker(object):
def __init__(self, name):
self.name = name
print self.name
r = Router(Worker("foo"))
# or
r = Router(Worker(name="foo"))
The cases where it would make sense to have the router instanciate the worker are:
1/ if the Worker's constructor arguments are not known when the router is instanciated and are to be passed later (which requires a distinct router method to pass those args)
2/ if the Worker's instanciation is very costly and you're not even sure you'll really need it, in which case you want to wait until the router's "main" method is called to instanciate the worker.
The second issue is "how do I get the worker's method by name". This one has already been answered by Lukas: you use getattr(obj, attrname).
The third issue is "if my worker method needs arguments, how do I pass them". This is the same problem as with the worker's constructor arguments, so the solution is obviously the same. Depending on the concrete use case, you'll have to pass those args either when instanciating the router or when calling it's "main" method.
wrt/ this "main" method, remember that you can define your own callable types by implementing the __call__ method, ie
class NotAFunc(object):
def __init__(self, wot):
self.wot = wot
def __call__(self, count):
print self.wot * count
notafunc = NotAFunc("wot ? ")
notafunc(42)
So it might make sense to use this as your router's "main" method
Now do you really need a router class at all ? Python functions are object on their own (so a function can take a function and/or return a function), and moreover act as closures (a closure is a function that "captures" part of the environment where it's been defined):
def route(instance, methodname, methargs=None, methkwargs=None):
method = getattr(instance, methodname)
if methargs is None:
methargs = ()
if methkwargs is None:
methkwargs = {}
def func():
return method(*methargs, **methkwargs)
return func
class Worker(object):
def __init__(self, name):
self.name = name
def work(self, count):
return [self.name for i in range(count)]
r = route(Worker("foo"), "work", (42,))
print r()
Note that while I kept your "router" term, most of what I described above are known patterns. You may want to search for "proxy", "proxy method", and (for the last exemple) "partial evaluation".
You are looking for dynamic attribute lookup.
class C:
def c1(self, x):
return 2*x
instance = C()
method_name = 'c1'
method = getattr(instance, method_name)
print(method(1)) # call method and print result
You'll need to override the __new__ method of your (new-style!) class.
class ClassRouter(object):
def __new__(self, class_name, *args):
if arg=="Class1":
new_instance = ClassWorker1(*args)
new_instance.method()
return new_instance
elif arg=="Class2":
return ClassWorker2(*args)
Is that possible to use python decorators to mark a method, and get it for the later use, if I don't know the name of the wrapped function?
Here is the example, and I don't know the name of method_with_custom_name:
#run_this_method
def method_with_custom_name(my_arg):
return "The args is: " + my_arg
def _init_and_run():
# Here, I want to get and call method_with_custom_name
# but I don't know it's name,
# so the next line isn't valid.
return run_this_method()(my_arg_value)
def run_this_method(m):
def w(my_arg):
_do_some_magic(my_arg, m)
return w
def _do_some_magic(callback_arg, callback):
if some_checks():
callback(callback_arg)
So how can I get a list of methods wrapped with #run_this_method
If you need to track all functions and methods decorated with your decorator you need to create global variable to register all such functions and methods. I've modified your code:
funcs_registry = [] #List of all functions decorated with #run_this_method
def run_this_method(m):
global functions_registry
funcs_registry.append(m) #Add function/method to the registry
def w(my_arg):
_do_some_magic(my_arg, m)
return w
def _do_some_magic(callback_arg, callback):
if some_checks():
callback(callback_arg)
#run_this_method
def method_with_custom_name(my_arg):
return "The args is: " + my_arg
def _init_and_run():
global functions_registry
# Here you can iterate over "functions_registry"
# and do something with each function/method in it
for m in functions_registry:
print(m.__name__)
Instead of using global variable functions_registry you can create class to be used as decorator and register functions in entity field. Something like this:
class FunDecorator:
def __init__(self):
self.registry = []
def __call__(self, m):
"This method is called when some method is decorated"
self.registry.append(m) #Add function/method to the registry
def w(my_arg):
_do_some_magic(my_arg, m)
return w
run_this_method = FunDecorator() #Create class instance to be used as decorator
#run_this_method
def method_with_custom_name(my_arg):
return "The args is: " + my_arg
#do some magic with each decorated method:
for m in run_this_method.registry:
print(m.__name__)
If I understand your question correctly (how to decorate a method with an unknown name?) then it is totally possible.
#decorator
def foo(bar):
pass
is syntastic sugar for
def foo(bar):
pass
foo = decorator(foo)
So in your case you should just do:
method_with_custom_name = run_this_method(method_with_custom_name)
The example you provided is confusing, though. Why don't you know the name of method_with_custom_name? It is right there. It is called method_with_custom_name. To use the decorated version later, you just call method_with_custom_name.
I have a class where I have multiple methods. I want to use one of the methods as a decorator for other methods. For this I am using following syntax:
#self.action
def execute(self,req):
where action is other method in my class. But it doesn't work and throws exception as
name 'self' is not defined
You cannot use a method of the class while defining it; there is no self within the class nor is the class 'baked' yet to even access any class.
You can treat methods as functions to use as a decorator:
class SomeClass():
def action(func):
# decorate
return wrapper
#action
def execute(self, req):
# something
If action is defined on a base class, then you'd have to refer to the name via the base class:
class Base():
#staticmethod
def action(func):
# decorate
return wrapper
class Derived(Base):
#Base.action
def execute(self, req):
# something
For Python 2, you'd have to make action a static method here, as otherwise you get an unbound method that'll complain you cannot call it without an instance as the first argument. In Python 3, you can leave off the #staticmethod decorator there, at least for the purposes of the decorator.
But note that action cannot then be used as a method directly; perhaps it should not be part of the class at all at that point. It is not part of the end-user API here, presumably the decorator is not used by consumers of the instances of these classes.
Just beware that both the decorator and the decorated function are unbound methods, so you can only access the self (or cls for classmethods) in the inner scope of the decorator, and must manually bind the decorated method to the instance bound in the inner decorator.
class A:
x = 5
y = 6
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
#decorate
def func(self):
return self.y
A().func() # 30!!
Still trying to wrap my head around how decorators could be inherited and overridden.
Beware that for the decorator to work it can't be bound to an instance. That is: there is no way to make this work
a = A()
#a.decorate
def func(*args):
return 1
Despite this pattern is much more common than the asked here.
At this point the question raises: is it a method at all or just code that you happen to hide in a class?
The only way to prevent the decorator being wrongfully bound is to declare it as a staticmethod, but then it must be in a previous super class because to be used it must be bound to the static class reference which would not be yet defined, just as the self.
class A:
x = 1
#staticmethod
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
class B(A):
#A.decorate
def func(self):
return 1
class C():
x = 2
#B.decorate
def func(self):
return 1
a = A()
class D():
x = 3
#a.decorate
def func(self):
return 1
B().func() # 1
C().func() # 2
D().func() # 3
But as you can see, there is no way for the decorator to use the state of its own class. class A from this last example just happens to be a mixin with a default x variable and an "unrelated" static decorator.
So, again, is it a method?
To overcome all of this, you can bind the staticmethod in your same class to an arbitrary type. Namely, the builtin type will do.
class A:
x = 1
#staticmethod
def decorate(unbound):
def _decorator(self):
bound = unbound.__get__(self)
return bound() * self.x
return _decorator
#decorate.__get__(type)
def func(self):
return 1
class B:
x = 2
#A.decorate
def func(self):
return 1
class C:
x = 3
#(A().decorate) # Only for Python 3.9+, see PEP-614
def func(self):
return 1
A().func() # 1
B().func() # 2
C().func() # 3
But this features too much magic for my taste. And still not a method for my gut.
In python "self" is passed to instance methods as an argument (the first), "self" is just a convention is possible to call it "foobarbaz" (of course it would be silly)… the point is that, from the outside "self" is not defined (because its scope is the method)… you can't decorate class methods with other class methods, instead you have to write a separate class!
I have a class which maintains a list of functions. These functions are just objects sitting in a queue and every so often the class pops one off and executes it. However, there are times when I would like to print out this list, and I'm imagining code as follows:
for function in self.control_queue:
print function.summarize()
if function.ready():
function()
In other words, I would like to call methods called summarize() and ready(), that I want to define somewhere, on these function objects. Also, I would like to be able to toss anonymous functions on this queue - i.e., generate everything dynamically.
you can make it a class and define __call__
class MyClass():
def summarize(self):
#summarize stuff
pass
def ready(self):
#ready stuff
pass
def _call__(self):
#put the code here, for when you call myClass()
pass
How you run it:
function = MyClass()
print function.summarize()
if function.ready():
function()
You have a couple possible approaches.
You could add the definitions to functions.
def foo():
pass
# later..
foo.summarize = lambda: "To pair with bar"
foo.ready = lambda: True
You could create class objects to wrap the function operation.
class Func():
def summarize(self):
return "Function!"
def ready(self):
return self.ready
def __call__(self):
# Act as a function
Or you can have a function which checks the function label for these capabilities.
def summarize_func(func):
return func.__name__ # Or branch here on specific names/attributes
def ready_func(func):
return True # Or branch on names/attributes
Finally to accommodate anonymous functions you can check for prescience of these attributes and return optimistically if the attributes are absent. Then you can combine above approaches with something that will work on any function.
def summarize_func(func):
if hasattr(func, summarize):
return func.summarize()
else:
# Note this will just be '<lambda>' for anonymous funcs
return func.__name__
def ready_func(func):
if hasattr(func, ready):
return func.ready()
else:
return True
One option is to implement function as a class instance:
class Function(object):
def summarize(self): pass # some relevant code here
def __call__(self): pass # and there
and use it later with
function = Function()
With __call__ magic method implemented, this function becomes a callable object.
For sure, you can assign attributes to functions, but it is rather obscure and conterintuitive:
>>> def summ(a): return sum(a)
...
>>> def function(a): return a
...
>>> function.sum=summ
>>> function.sum([1,2,3])
6