Convert a bound method in python to a function (and reduce arg count)

烈酒焚心 提交于 2021-01-28 05:06:48

问题


I am adding a URL handler to a Flask application using add_url_rule. It requires a function as a handler (not a method). However, for proper encapsulation, my intended function is a bound method that uses the self variable. Is there any way I can somehow make Flask see my function: def action(self, param) as def action(param) ?

class A():
    def __init__(self):
        self._localvar = 5
        self._flask = Flask("testapp")

    def add_rules(self):
        self._flask.add_url_rule("/","root",????)

    def action(self, value):
        print("The value is {}".format(self._localvar))

    def run(self):
        self._flask.run()

app = A()
app.add_rules()
app.run()

what I want to do is replace ???? with app.action, a bound method. However, add_url_rule expects a function. I tried app.action.__func__ and app.action.im_func which are function pointers. However, the function signature is still going to require self and will give me a runtime error.

My current solution is to have some loose functions in my module and have app just use them directly, but it just seems that there has to be a cleaner way since the handlers are really tightly coupled to the app object and I don't want them floating around outside of it.


回答1:


What I want to know is whether there is a python way to somehow "cast" a def action(value) to the def action(self, value) [...]

And what if you do it the other way around? You define your actions as functions (or staticmethods if you want to keep them in the class A), and bind them to your instance only when you need them.

You could use the types module to bind a function to an instance.

import types

class A():

    def bind_action_func_to_instance(self, func, value):
        self.action_method = types.MethodType(func, self)

    @staticmethod
    def action_func(value):
        print('action with value: {}'.format(value))

    def add_rules():
        self._flask.add_url_rule("/","root", self.action_func)

You could also implement a default action method and assign it to action_method in the __init__. It will then be reassigned when you call bind_action_func_to_instance.

I think you could also find useful this answer about the Strategy pattern.




回答2:


This is an old question, but I thought it might be useful to know a possibly easier solution for someone (like me) visiting this page.

You can access the underlying function of a bound method as its __func__ attribute. Also, if you access it as an attribute of the class and not the instance, it will give you the function.

class C:
    def fn(self, param):
        return param

instance = C()
print(instance.fn("hello"))  # 'hello'
print(instance.fn.__func__(instance, "hello"))  # 'hello'
print(instance.fn.__func__(None, "hello"))  # 'hello'
print(C.fn(instance, "hello"))  # 'hello'
print(C.fn(None, "hello"))  # 'hello'



回答3:


I Think I finally found a way to do this (i.e. keep the action method bound inside my class and have a function-like reference to it when needed). I used a function inside a method:

class A():
    def __init__(self):
        self._localvar = 5
        self._flask = Flask("testapp")

    def add_rules(self):
        self._flask.add_url_rule("/","root",self.action_as_func())

    def action(self, value):
        print("The value is {}".format(self._localvar))

    def action_as_func(self):
        def inner_action(value):
            self.action(value)
        return inner_action

    def run(self):
        self._flask.run()

app = A()
app.add_rules()
app.run()

Any comments on whether there is something wrong with this ? The function can access self. Kinda sneaky :)



来源:https://stackoverflow.com/questions/46897417/convert-a-bound-method-in-python-to-a-function-and-reduce-arg-count

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!