首页 > 解决方案 > 装饰器导致所有函数返回 True

问题描述

为了调试我遇到问题的在线权限系统,我创建了几个类和函数,旨在离线复制问题。不幸的是,我遇到了一个问题,将一个函数变成一个装饰器会完全改变一个装饰函数的结果。

我的代码如下,我尽可能保持基本,以说明我的观点。

  1. 设置用户类:

    class User(object):
    
        def __init__(self, forename=None, surname=None, logged_in=True, exists=True, poop=False, admin=False):
            self.forename = forename
            self.surname = surname
            self.logged_in = logged_in
            self.exists = exists
            self.poop = poop
            self.admin = admin
    
        def __repr__(self):
            return f'User: {self.forename} {self.surname}.'
    
    user1 = User('Paddy', 'McHugh', True, True, False, True)
    user2 = User('Rodney', 'Donger', False, False, True, False)
    user3 = User('Bob', 'Dangler', True, True, True, True)
    
  2. 创建函数以针对用户进行测试:

    def user_just_is(user):
        return user.exists
    
    
    def user_is_poop(user):
        return user.poop
    
    
    def user_is_admin(user):
        return user.admin
    
  3. 使用常规功能针对所选用户测试这些功能:

    class Permissions2(object):
    
        def __init__(self):
            pass
    
        def requires(self, *args):
            user = user2
    
            if not user.logged_in:
                print('You\'re not logged in, please log in.')
                return
    
            if not all(i(user) for i in args):
                print('Not all of the conditions were met.')
            else:
                print('All of the conditions were met.')
    
    
    Permissions2().requires(user_just_is, user_is_poop, user_is_admin)
    
  4. 使用装饰器功能针对所选用户测试这些功能:

    class Permissions(object):
    
        def __init__(self):
            pass
    
        def requires(self, *args):
            user = user2
    
            def decorator(func):
                @wraps(func)
                def allower(*args, **kwargs):
                    if not user.logged_in:
                        print('You\'re not logged in, please log in.')
                        return
    
    
                    if not all(i(user) for i in args):
                        print('Not all of the conditions were met.')
                        return
    
                    return func(*args, **kwargs)
    
                return allower
    
            return decorator
    
    
    @Permissions.requires(user_just_is, user_is_poop, user_is_admin)
    def print_stuff():
        print('All of the conditions were met.')
    
    print_stuff()
    

我希望常规函数和装饰器函数的结果是相同的。如果 User.logged_in == False,那么它将打印:'You're not logged in, please log in.'。如果所有布尔变量都是 True,它将打印:'All of the conditions were met.'。如果任何条件为 False,它将打印:'Not all of the conditions were met.'

装饰器函数仍然返回'You're not logged in, please log in',但如果 User.logged_if == True ,那么其他布尔值无关紧要,它总是返回 True 给all()函数和 print 'All of the conditions were met.'

将它放在装饰器中意味着all()似乎对所有测试的函数都返回 True 是什么意思?

标签: python

解决方案


args您的allower函数的参数会隐藏 的参数argsrequires因此当您在此处迭代时args

if not all(i(user) for i in args):

您不再遍历传递给requiresas的函数列表args,而是args传递给装饰函数。您应该重命名参数以避免命名冲突。

此外,您将定义Permissions.requires为实例方法,因此它的第一个参数是self,该方法绑定到的对象,因此当您调用时:

@Permissions.requires(user_just_is, user_is_poop, user_is_admin)

user_just_is作为 传递self,而不是成为 的一部分args。由于requires实际上并未使用self,因此应将其定义为静态方法。

因此,修复了上述问题后,您的Permissions课程应如下所示:

class Permissions(object):
    @staticmethod
    def requires(*conditions):
        user = user2

        def decorator(func):
            @wraps(func)
            def allower(*args, **kwargs):
                if not user.logged_in:
                    return print('You\'re not logged in, please log in.')

                if not all(i(user) for i in conditions):
                    return print('Not all of the conditions were met.')

                return func(*args, **kwargs)

            return allower

        return decorator

推荐阅读