簡體   English   中英

Python裝飾器和類繼承

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM