使用定义为实例变量的装饰器函数

Posted

技术标签:

【中文标题】使用定义为实例变量的装饰器函数【英文标题】:Using a decorator function defined as an instance variable 【发布时间】:2017-05-07 16:01:51 【问题描述】:

(虽然这个问题是专门针对Flask的,但可以根据题目概括一下)

我正在尝试在类中使用 Flask 的 app.route() 装饰器。但是,Flask 应用程序被初始化为实例变量,即 self.server 设置为应用程序。这意味着我不能使用装饰器,因为 self 在装饰方法之外是未定义的。我希望能够做到以下几点:

class MyClass:

    def __init__(self):
        self.server = Flask(__name__)

    @self.server.route('/')
    def home():
        return '<h1>Success</h1>'

是否有解决此问题的方法?非常感谢任何帮助!

【问题讨论】:

【参考方案1】:

您可以在__init__ 方法的上下文中定义函数。然后,为了使函数能够正常调用,将home成员设置为等于它。

class MyClass:
    def __init__(self):
        self.server = Flask(__name__)

        # This is indented at __init__'s level, so a new instance of the function
        # is defined every time __init__ runs. That means a new instance
        # is defined for each instance of the class, and so it can be wrapped with
        # the instance's "self" value.
        @self.server.route('/')
        def home_func():
            return '<h1>Success</h1>'

        # Then, you make it an object member manually:
        self.home = home_func

【讨论】:

我认为这不是一个前瞻性的解决方案,因为您最终会得到一个包含所有路由定义及其各自功能的大量 __init__() 方法。最好使用add_url_rule() function,正如我自己(和@rumdrums)对这个问题的回答中所解释的那样。 对于 Flask,这可能会更好。然而,即使对于不使用 Flask 的人来说,这也是一个很好的问题。此外,以这种方式定义函数只需要比add_url_rule 多一行,所以我认为“大量的__init__() 方法”并不特别重要。 很公平!纯粹作为一项学术练习,也可以使用route 装饰器对__init__ 方法中的MyClass 类的方法进行猴子补丁,方法是执行self.home = self.server.route('/')(self.home) 之类的操作。这是在一些可能遇到的棘手场景中装饰函数/方法的便捷方式,而不仅仅是 Flask 和路由。【参考方案2】:

我不确定您的整体用例是什么,但最好不要将应用程序对象嵌入到类中,而是使用 Flask 的 Pluggable Views 模块。这将允许您将视图清晰地定义为从 flask.views.View 继承的类。示例:

import flask
import flask.views

class MyClass(flask.views.View):
  def dispatch_request(self):
    return '<h1>Success</h1>'

app.add_url_rule('/test', view_func=MyClass.as_view('home'))

对于这个小例子来说,这肯定是更多的代码,但这让您可以更灵活地使用自己的路由定义其他类或函数,并可能考虑使用 MethodViews,它为定义多个 HTTP 方法并将它们关联起来提供了一个良好的结构一个班级。

【讨论】:

【参考方案3】:

您应该使用self.server 对象的add_url_rule method,而不是使用route() 装饰器,如下所示:

class MyClass:
    def __init__(self):
        self.server = Flask(__name__)
        self.server.add_url_rule('/', 'home', self.home)
        self.server.add_url_rule('/route-1', 'route-1', self.route_1)
        self.server.add_url_rule('/route-2', 'route-2', self.route_2)

    def home():
        return '<h1>Success</h1>'

    def route_1():
        ...

    def route_2():
        ...

此模式允许您将路由处理程序定义为类上的方法,并且更易于阅读,因为您可以在一个块中查看所有 URL 规则。

【讨论】:

每个方法的self参数在哪里?即homeroute_1route_2

以上是关于使用定义为实例变量的装饰器函数的主要内容,如果未能解决你的问题,请参考以下文章

初识python: 装饰器

如何将实例变量传递给打字稿装饰器参数?

将实例变量传递给装饰器

题型:python

装饰器基本原理

类的静态方法@staticmethod