簡體   English   中英

Python 基於類的裝飾器,帶有可以裝飾方法或函數的參數

[英]Python Class Based Decorator with parameters that can decorate a method or a function

我見過很多 Python 裝飾器的例子,它們是:

  • 函數風格裝飾器(包裝函數)
  • 類樣式裝飾器(實現__init____get____call__
  • 不帶參數的裝飾器
  • 帶參數的裝飾器
  • “方法友好”的裝飾器(即可以裝飾類中的方法)
  • “函數友好”的裝飾器(可以裝飾一個普通的函數
  • 可以裝飾方法和函數的裝飾器

但是我從未見過可以完成上述所有操作的單個示例,而且我無法從特定問題的各種答案(例如this onethis onethis one (具有最佳答案之一)進行綜合)我曾經在 SO) ) 上看到過如何將上述所有內容結合起來。

我想要的是一個基於類的裝飾器,它可以裝飾一個方法或一個函數,並且至少需要一個額外的參數 即,以便以下工作:

class MyDecorator(object):
    def __init__(self, fn, argument):
        self.fn = fn
        self.arg = argument

    def __get__(self, ....):
        # voodoo magic for handling distinction between method and function here

    def __call__(self, *args, *kwargs):
        print "In my decorator before call, with arg %s" % self.arg
        self.fn(*args, **kwargs)
        print "In my decorator after call, with arg %s" % self.arg


class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"


@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"

some_other_function()
Foo().bar()

我希望看到:

In my decorator before call, with arg some other func!
in some other function!
In my decorator after call, with arg some other func!
In my decorator before call, with arg foo baby!
in bar!
In my decorator after call, with arg foo baby!

編輯:如果重要的話,我使用的是 Python 2.7。

你不需要弄亂描述符。 __call__()方法中創建一個包裝函數並返回它就足夠了。 標准 Python 函數始終可以充當方法或函數,具體取決於上下文:

class MyDecorator(object):
    def __init__(self, argument):
        self.arg = argument

    def __call__(self, fn):
        @functools.wraps(fn)
        def decorated(*args, **kwargs):
            print "In my decorator before call, with arg %s" % self.arg
            result = fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % self.arg
            return result
        return decorated

關於當這個裝飾器這樣使用時發生了什么的一些解釋:

@MyDecorator("some other func!")
def some_other_function():
    print "in some other function!"

第一行創建了一個MyDecorator的實例並傳遞了"some other func!" 作為__init__()的參數。 讓我們稱這個實例為my_decorator 接下來,未裝飾的函數對象——讓我們稱之為bare_func被創建並傳遞給裝飾器實例,因此my_decorator(bare_func)被執行。 這將調用MyDecorator.__call__() ,它將創建並返回一個包裝函數。 最后這個包裝函數被分配給名稱some_other_function

你缺少一個級別。

考慮代碼

class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"

它與此代碼相同

class Foo(object):
    def bar(self):
        print "in bar!"
    bar = MyDecorator("foo baby!")(bar)

所以MyDecorator.__init__被調用為"foo baby!" 然后使用函數bar調用MyDecorator對象。

也許你的意思是實現更像

import functools

def MyDecorator(argument):
    class _MyDecorator(object):
        def __init__(self, fn):
            self.fn = fn

        def __get__(self, obj, type=None):
            return functools.partial(self, obj)

        def __call__(self, *args, **kwargs):
            print "In my decorator before call, with arg %s" % argument
            self.fn(*args, **kwargs)
            print "In my decorator after call, with arg %s" % argument

    return _MyDecorator

在您的裝飾器類型列表中,您錯過了可能帶參數也可能不帶參數的裝飾器。 我認為這個例子涵蓋了除“函數風格裝飾器(包裝函數)”之外的所有類型

class MyDecorator(object):

    def __init__(self, argument):
        if hasattr('argument', '__call__'):
            self.fn = argument
            self.argument = 'default foo baby'
        else:
            self.argument = argument

    def __get__(self, obj, type=None):
        return functools.partial(self, obj)

    def __call__(self, *args, **kwargs):
        if not hasattr(self, 'fn'):
            self.fn = args[0]
            return self
        print "In my decorator before call, with arg %s" % self.argument
        self.fn(*args, **kwargs)
        print "In my decorator after call, with arg %s" % self.argument


class Foo(object):
    @MyDecorator("foo baby!")
    def bar(self):
        print "in bar!"

class Bar(object):
    @MyDecorator
    def bar(self):
        print "in bar!"

@MyDecorator
def add(a, b):
    print a + b

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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