MST

星途 面试题库

面试题:Python装饰器:元类与装饰器的结合应用

假设你正在开发一个框架,需要使用元类和装饰器结合的方式来实现类方法的权限控制。例如,某些类方法只能由具有特定权限的用户调用。请设计并实现这个方案,包括元类的定义、装饰器的编写以及它们之间如何协同工作。详细解释每一步的设计思路和代码实现。
13.1万 热度难度
编程语言Python

知识考点

AI 面试

面试题答案

一键面试

1. 设计思路

  • 元类:元类用于创建类,在这里我们通过元类来扫描类的方法,并检查是否有特定的权限装饰器。如果有,将权限信息存储起来,以便后续在方法调用时进行权限验证。
  • 装饰器:装饰器用于标记类方法,并指定调用该方法所需的权限。

2. 代码实现

# 定义权限装饰器
def require_permission(permission):
    def decorator(func):
        func.required_permission = permission
        return func
    return decorator


# 定义元类
class PermissionMeta(type):
    def __new__(cls, name, bases, attrs):
        new_attrs = {}
        for attr_name, attr_value in attrs.items():
            if callable(attr_value) and hasattr(attr_value, 'required_permission'):
                def wrapper(self, *args, **kwargs):
                    if hasattr(self, 'has_permission') and self.has_permission(attr_value.required_permission):
                        return attr_value(self, *args, **kwargs)
                    else:
                        raise PermissionError(f"User does not have permission: {attr_value.required_permission}")
                new_attrs[attr_name] = wrapper
            else:
                new_attrs[attr_name] = attr_value
        return super().__new__(cls, name, bases, new_attrs)


# 使用元类和装饰器
class User:
    __metaclass__ = PermissionMeta

    def __init__(self, permissions):
        self.permissions = permissions

    def has_permission(self, permission):
        return permission in self.permissions

    @require_permission('admin')
    def admin_method(self):
        print("This is an admin - only method.")


# 测试代码
admin_user = User(['admin'])
admin_user.admin_method()

regular_user = User(['user'])
try:
    regular_user.admin_method()
except PermissionError as e:
    print(e)

3. 代码解释

  • 权限装饰器 require_permission
    • 这是一个高阶函数,接受一个 permission 参数。
    • 它返回一个内部装饰器 decorator,这个内部装饰器接受一个函数 func 作为参数。
    • decorator 函数内部,为 func 添加了一个 required_permission 属性,值为传入的 permission,然后返回 func。这样就标记了该方法所需的权限。
  • 元类 PermissionMeta
    • __new__ 方法中,遍历类的属性。
    • 如果属性是可调用的(即方法)且具有 required_permission 属性(说明该方法被权限装饰器标记了),则创建一个新的包装函数 wrapper
    • wrapper 函数中,首先检查实例是否有 has_permission 方法,并且当前用户是否具有调用该方法所需的权限。如果有则调用原始方法,否则抛出 PermissionError
    • 最后,使用 super().__new__ 创建并返回新的类。
  • User
    • 使用 __metaclass__ = PermissionMeta 指定使用 PermissionMeta 作为元类。
    • 构造函数 __init__ 接受用户的权限列表并存储。
    • has_permission 方法用于检查用户是否具有特定权限。
    • admin_method 方法被 @require_permission('admin') 装饰,标记该方法需要 admin 权限才能调用。

通过这种方式,元类和装饰器协同工作,实现了类方法的权限控制。