Python装饰器和类继承
我试图使用decorator来管理用户访问web应用程序(在Google App Engine上运行)中资源的方式。请注意,我不允许用户使用他们的Google帐户登录,因此在app.yaml中设置特定路线的特定访问权限不是一个选项 我使用了以下资源:Python装饰器和类继承,python,web-applications,decorator,subclassing,Python,Web Applications,Decorator,Subclassing,我试图使用decorator来管理用户访问web应用程序(在Google App Engine上运行)中资源的方式。请注意,我不允许用户使用他们的Google帐户登录,因此在app.yaml中设置特定路线的特定访问权限不是一个选项 我使用了以下资源: - - - - 但是我还是有点困惑 这是我的密码!在下面的示例中,当前_用户是属于RequestHandler类的@property方法。它返回存储在数据存储中的用户(db.model)对象,其级别为IntProperty() 但是,我的应用程序对
-
-
-
- 但是我还是有点困惑 这是我的密码!在下面的示例中,当前_用户是属于RequestHandler类的@property方法。它返回存储在数据存储中的用户(db.model)对象,其级别为IntProperty() 但是,我的应用程序对不同类型的资源使用不同的控制器。为了在所有子类中使用@requiredLevel装饰器,我需要将其移动到父类(RequestHandler): 我的想法是使用以下代码访问所有控制器子类中的装饰器:
class FoobarController(RequestHandler):
@RequestHandler.requiredLevel(100)
def get(self):
#do stuff here...
我想我对装饰器和类继承的知识已经达到了极限:)。有什么想法吗?在仔细阅读StackOverflow之后,我想我找到了一个可能的解决方案 它涉及将装饰器作为父类中的一个类来实现:
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
请随意评论或提出增强。您的原始代码,加上两个小改动,也应该可以工作。对于这样一个简单的装饰器,基于类的方法似乎相当重要:
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是一个实例方法,它在类声明时不可用(当您装饰其他方法时),在类对象中也不可用(将decorator放在RequestHandler基类上是一个很好的主意,并且生成的decorator调用可以很好地自文档化)
您可能有兴趣阅读和的文档
另外,我喜欢在我的装饰师身上放一点样板:
@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
为什么它是类上的一个方法?这只会导致问题的解决,而且它只能像它定义的类中的一个常规函数一样工作。除非你是在3.x上,在这种情况下,它可能会正常工作。decorator是类上的一个方法,因为我还没有弄清楚如何将decorator编码为类1/并接受参数2/这可以访问当前类本身的方法。这就是你的意思吗?由于大部分是自学的,我很难完全理解Bruce Eckell的指南、装饰器和继承。你只需将函数复制粘贴到类外,它就会正常工作。这足以回答你的问题吗?移动requiredLevel decorator从FoobarController到RequestHandler,并使用@staticmethod对其进行修饰似乎是解决方案,但在我的情况下,它不起作用。很可能是因为我的decorator接受参数?不,我的意思是将其从类中完全删除。使其成为常规函数。您可以使用
wrapped\u f(request_handler,*args,**kwargs)
作为函数签名。也没有必要将decorator类放入RequestHandler类中。我将把它放在模块级。谢谢你的评论!我想你刚刚让我了解了继承的工作方式不同(而且更好)。我将相应地更新我的代码。
class RequestHandler(webapp.RequestHandler):
# The decorator is now a static method.
@staticmethod # No default argument required...
def requiredLevel(required_level):
@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