[英]Python decorators and class inheritance
我正在尝试使用装饰器来管理用户可能访问或不访问Web应用程序(在Google App Engine上运行)中的资源的方式。 请注意,我不允许用户使用他们的Google帐户登录,因此无法在app.yaml中设置特定路径的特定访问权限。
我使用了以下资源:
- 布鲁斯·埃克尔的装饰指南
- SO:get-class-in-python-decorator2
- SO:python-decorators-and-inheritance
- SO:get-class-in-python-decorator
不过我还是有点困惑......
这是我的代码! 在以下示例中,current_user是属于RequestHandler类的@property方法。 它返回存储在数据存储区中的User(db.model)对象,其级别为IntProperty()。
class FoobarController(RequestHandler):
# Access decorator
def requiredLevel(required_level):
def wrap(func):
def f(self, *args):
if self.current_user.level >= required_level:
func(self, *args)
else:
raise Exception('Insufficient level to access this resource')
return f
return wrap
@requiredLevel(100)
def get(self, someparameters):
#do stuff here...
@requiredLevel(200)
def post(self):
#do something else here...
但是,我的应用程序对不同类型的资源使用不同的控制器。 为了在所有子类中使用@requiredLevel装饰器,我需要将它移动到父类(RequestHandler):
class RequestHandler(webapp.RequestHandler):
#Access decorator
def requiredLevel(required_level):
#See code above
我的想法是使用以下代码访问所有控制器子类中的装饰器:
class FoobarController(RequestHandler):
@RequestHandler.requiredLevel(100)
def get(self):
#do stuff here...
我想我刚刚达到了关于装饰器和类继承的知识限制:)。 有什么想法吗 ?
你的原始代码,有两个小调整,也应该工作。 对于这样一个简单的装饰器来说,基于类的方法看起来相当重要:
class RequestHandler(webapp.RequestHandler):
# The decorator is now a class method.
@classmethod # Note the 'klass' argument, similar to 'self' on an instance method
def requiredLevel(klass, required_level):
def wrap(func):
def f(self, *args):
if self.current_user.level >= required_level:
func(self, *args)
else:
raise Exception('Insufficient level to access this resource')
return f
return wrap
class FoobarController(RequestHandler):
@RequestHandler.requiredLevel(100)
def get(self, someparameters):
#do stuff here...
@RequestHandler.requiredLevel(200)
def post(self):
#do something else here...
或者,您可以使用@staticmethod
代替:
class RequestHandler(webapp.RequestHandler):
# The decorator is now a static method.
@staticmethod # No default argument required...
def requiredLevel(required_level):
原始代码不起作用的原因是假定requiredLevel是一个实例方法,它不会在类声明时(当你装饰其他方法时)可用,也不会从class对象(将装饰器放在RequestHandler基类上是一个很好的主意,最终的装饰器调用很好地自我记录)。
您可能有兴趣阅读有关@classmethod
和@staticmethod
的文档。
另外,我想在装饰器中添加一些样板:
@staticmethod
def requiredLevel(required_level):
def wrap(func):
def f(self, *args):
if self.current_user.level >= required_level:
func(self, *args)
else:
raise Exception('Insufficient level to access this resource')
# This will maintain the function name and documentation of the wrapped function.
# Very helpful when debugging or checking the docs from the python shell:
wrap.__doc__ = f.__doc__
wrap.__name__ = f.__name__
return f
return wrap
在深入了解StackOverflow并仔细阅读Bruce Eckel的装饰指南之后 ,我想我找到了一个可能的解决方案。
它涉及将装饰器实现为Parent类中的类:
class RequestHandler(webapp.RequestHandler):
# Decorator class :
class requiredLevel(object):
def __init__(self, required_level):
self.required_level = required_level
def __call__(self, f):
def wrapped_f(*f_args):
if f_args[0].current_user.level >= self.required_level:
return f(*f_args)
else:
raise Exception('User has insufficient level to access this resource')
return wrapped_f
这样做了! 使用f_args [0]对我来说似乎有点脏,如果我找到更漂亮的东西,我会编辑这个答案。
然后,您可以通过以下方式修饰子类中的方法:
FooController(RequestHandler):
@RequestHandler.requiredLevel(100)
def get(self, id):
# Do something here
@RequestHandler.requiredLevel(250)
def post(self)
# Do some stuff here
BarController(RequestHandler):
@RequestHandler.requiredLevel(500)
def get(self, id):
# Do something here
随意评论或提出增强功能。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.