我正在try 对其他成员函数修饰器重新使用成员函数修饰器,但我收到以下错误:

'function' object has no attribute '_MyClass__check_for_valid_token'

基本上,我有一个有效的修饰器来判断用户是否登录(@LOGIN_REQUIRED),我想首先在@ADMIN_REQUIRED修饰器中调用它(所以我的 idea 是判断用户是否使用现有的@LOGIN_REQUIRED修饰器登录,然后添加一些特定的验证来判断登录的用户是否是@ADMIN_REQUIRED修饰器中的管理员).

我现在的代码是这样的:

class MyClass:
    def LOGIN_REQUIRED(func):
            @wraps(func)
            def decorated_function(self, *args, **kwargs):
                # username and token should be the first parameters
                # throws if not logged in
                self.__check_for_valid_token(args[0], args[1])
                return func(self, *args, **kwargs)
            return decorated_function
    
    @LOGIN_REQUIRED
    def ADMIN_REQUIRED(func):
        @wraps(func)
        def decorated_function(self, *args, **kwargs):
            is_admin = self.check_if_admin()

            if not is_admin:
                raise Exception()

            return func(self, *args, **kwargs)
        return decorated_function
        
    @ADMIN_REQUIRED
    def get_administration_data(self, username, token):
        # return important_data
        # currently throws 'function' object has no attribute '_MyClass__check_for_valid_token'

你知道我怎么才能让它起作用吗?


根据 comments 和答复澄清的约notes项:

  1. 可以将方法__check_for_valid_token的名称更改为不会遇到名称损坏问题.我只是使用了双下划线,因为它是一个应该只能由类本身访问的方法(私有).
  2. "MyClass"中没有继承.
  3. @LOGIN_REQUIRED代码必须在@ADMIN_REQUIRED代码之前运行(因为这是某些人所期望的,至少在我的例子中是这样).

推荐答案

我认为这是可能的,但有两个警告:第一,装饰者将不得不搬到班级之外,第二,将需要对名称进行一些调整.让我们先解决第一个问题.

移动一些函数

直接装饰装饰者看起来很直观,但可能不会得到你想要的结果.但是,您可以修饰内部函数--就像使用@wraps一样.由于Python解析代码的方式,外部修饰符必须在类外部(和类之前)定义,否则将出现NameError.代码应如下所示:

def _OUTER_LOGIN_REQUIRED(func):
    @wraps(func)
    def decorated_function(self, *args, **kwargs):
        self.__check_for_valid_token(args[0], args[1])
            return func(self, *args, **kwargs)
        return decorated_function
    [Notice no code-changes to this function (yet)]

class MyClass:
    # The following line will make the transition seamless for most methods
    LOGIN_REQUIRED = _OUTER_LOGIN_REQUIRED

    def ADMIN_REQUIRED(func):
        @wraps(func)
        @_OUTER_LOGIN_REQUIRED  # <-- Note that this is where it should be decorated
        def decorated_function(self, *args, **kwargs):
        ... [the rest of ADMIN_REQUIRED remains unchanged]

    @ADMIN_REQUIRED
    def get_administration_data(self, username, token):
    ... [this should now invoke LOGIN_REQUIRED -> ADMIN_REQUIRED -> the function]

    @LOGIN_REQUIRED
    def get_some_user_data(self, ...):
    ... [Such definitions should still work, as we added LOGIN_REQUIRED attribute to the class]

如果到目前为止这样的改变是可以接受的,那么让我们继续

名称损坏

由于__check_for_calid_token函数的名称是mangled(因为它的名称以DUnder开头),您必须决定如何处理它.有两个选项:

  1. 如果没有限制,只需将下划线缩短为一个下划线(或者随意重命名--只要它不以多个下划线开头).
  2. 如果名字Manging很重要,你必须将呼叫改为_OUTER_LOGIN_REQUIRED,如下所示:
def decorated_function(self, *args, **kwargs):
    self._MyClass__check_for_valid_token(args[0], args[1])

这可能会影响继承,应该进行彻底的测试.

摘要

我在python3.9上对此进行了一些测试,它似乎工作得很好.我注意到,登录错误出现在管理员错误之前,这是我所希望的.尽管如此,我只是以一种肤浅的方式进行了探索,虽然我想不出这会导致行为不正常的原因,但我强烈建议在提交此方法之前彻底测试它(特别是如果代码包含继承,而我甚至没有接触到它).

我希望这对你有效,如果它不起作用-让我们知道它在哪里崩溃,以及如何崩溃.

Python相关问答推荐

两极按组颠倒顺序

将词典写入Excel

"Discord机器人中缺少所需的位置参数ctx

用Python获取HTML Span类中的数据

如何将Matplotlib的fig.add_axes本地坐标与我的坐标关联起来?

如何终止带有队列的Python进程?+ 队列大小的错误?

使用plotnine和Python构建地块

点到面的Y距离

使用miniconda创建环境的问题

如何标记Spacy中不包含特定符号的单词?

有症状地 destruct 了Python中的regex?

' osmnx.shortest_track '返回有效源 node 和目标 node 的'无'

为什么sys.exit()不能与subproccess.run()或subprocess.call()一起使用

如果值发生变化,则列上的极性累积和

在Python中,从给定范围内的数组中提取索引组列表的更有效方法

用渐近模计算含符号的矩阵乘法

从Windows Python脚本在WSL上运行Linux应用程序

在Python中调用变量(特别是Tkinter)

如何找出Pandas 图中的连续空值(NaN)?

为什么'if x is None:pass'比'x is None'单独使用更快?