当前位置: 首页 > 面试题库 >

使用类与函数的Python装饰器最佳实践

崔宜修
2023-03-14
问题内容

据我了解,有两种方法可以做Python装饰器,既可以使用__call__类的,也可以定义并调用函数作为装饰器。这些方法的优点/缺点是什么?有一种首选的方法吗?

例子1

class dec1(object):
    def __init__(self, f):
        self.f = f
    def __call__(self):
        print "Decorating", self.f.__name__
        self.f()

@dec1
def func1():
    print "inside func1()"

func1()

# Decorating func1
# inside func1()

例子2

def dec2(f):
    def new_f():
        print "Decorating", f.__name__
        f()
    return new_f

@dec2
def func2():
    print "inside func2()"

func2()

# Decorating func2
# inside func2()

问题答案:

说每种方法是否都具有“优势”是相当主观的。

但是,如果对幕后的事物有一个很好的了解,那么自然就可以为每种场合选择最佳选择。

装饰器(谈论函数装饰器)只是一个以函数为输入参数的可调用对象。Python有其相当有趣的设计,它允许除功能以外还可以创建其他类型的可调用对象-
并且可以将其用于创建更多可维护的或更短的代码。

装饰器在Python 2.3中作为“语法快捷方式”添加回

def a(x):
   ...

a = my_decorator(a)

除此之外,当我们使用这种类型时,我们通常将装饰器称为“装饰器”,而不是“装饰器工厂”。

@my_decorator(param1, param2)
def my_func(...):
   ...

使用param1和param2调用“ my_decorator”-然后返回将再次调用的对象,这次以“
my_func”作为参数。因此,在这种情况下,从技术上讲,“ decorator”是“ my_decorator”返回的值,从而使其成为“
decorator factory”。

现在,如上所述的装饰器或“装饰器工厂”通常必须保留一些内部状态。在第一种情况下,它唯一保留的是对原始函数的引用(f在示例中称为变量)。“装饰器工厂”可能想要注册其他状态变量(在上面的示例中为“
param1”和“ param2”)。

在装饰器编写为函数的情况下,此额外状态保留在封闭函数内的变量中,并由实际包装函数作为“非局部”变量进行访问。如果编写了适当的类,则可以将它们作为实例变量保存在装饰器函数(将被视为“可调用对象”,而不是“函数”)中,并且对它们的访问更加明确并且更具可读性。

因此,在大多数情况下,是否选择一种方法还是一个可读性问题:对于简短的装饰器而言,功能性方法通常比一个类编写的方法更具可读性-有时更精细-
尤其是一种“装饰工厂”将充分利用Python编码之前的“扁平优于嵌套”建议。

考虑:

def my_dec_factory(param1, param2):
   ...
   ...
   def real_decorator(func):
       ...
       def wraper_func(*args, **kwargs):
           ...
           #use param1
           result = func(*args, **kwargs)
           #use param2
           return result
       return wraper_func
   return real_decorator

针对这种“混合”解决方案:

class MyDecorator(html" target="_blank">object):
    """Decorator example mixing class and function definitions."""
    def __init__(self, func, param1, param2):
        self.func = func
        self.param1, self.param2 = param1, param2

    def __call__(self, *args, **kwargs):
        ...
        #use self.param1
        result = self.func(*args, **kwargs)
        #use self.param2
        return result

def my_dec_factory(param1, param2):
    def decorator(func):
         return MyDecorator(func, param1, param2)
    return decorator

更新 :缺少装饰器的“纯类”形式

现在,请注意“混合”方法采用“两全其美”的做法,以保持最短且可读性更高的代码。专门用类定义的完整“装饰器工厂”将需要两个类,或者需要一个“模式”属性来知道是否调用了它来注册修饰后的函数或实际调用了最终函数:

class MyDecorator(object):
   """Decorator example defined entirely as class."""
   def __init__(self, p1, p2):
        self.p1 = p1
        ...
        self.mode = "decorating"

   def __call__(self, *args, **kw):
        if self.mode == "decorating":
             self.func = args[0]
             self.mode = "calling"
             return self
         # code to run prior to function call
         result = self.func(*args, **kw)
         # code to run after function call
         return result

@MyDecorator(p1, ...)
def myfunc():
    ...

最后是一个纯净的“白领”装饰器,定义了两个类-也许可以使事物更加分离,但是将冗余性提高到一定程度,这不能说它更易于维护:

class Stage2Decorator(object):
    def __init__(self, func, p1, p2, ...):
         self.func = func
         self.p1 = p1
         ...
    def __call__(self, *args, **kw):
         # code to run prior to function call
         ...
         result = self.func(*args, **kw)
         # code to run after function call
         ...
         return result

class Stage1Decorator(object):
   """Decorator example defined as two classes.

   No "hacks" on the object model, most bureacratic.
   """
   def __init__(self, p1, p2):
        self.p1 = p1
        ...
        self.mode = "decorating"

   def __call__(self, func):
       return Stage2Decorator(func, self.p1, self.p2, ...)


@Stage1Decorator(p1, p2, ...)
def myfunc():
    ...

我几年前在上面写过文字。由于创建了“更扁平”的代码,最近我想到了一个更喜欢的模式。

基本思想是使用函数,但是partial如果在用作修饰符之前使用参数调用了该对象,则返回其自身的对象:

from functools import wraps, partial

def decorator(func=None, parameter1=None, parameter2=None, ...):

   if not func:
        # The only drawback is that for functions there is no thing
        # like "self" - we have to rely on the decorator 
        # function name on the module namespace
        return partial(decorator, parameter1=parameter1, parameter2=parameter2)
   @wraps(func)
   def wrapper(*args, **kwargs):
        # Decorator code-  parameter1, etc... can be used 
        # freely here
        return func(*args, **kwargs)
   return wrapper

就是这样-使用此模式编写的装饰器可以立即装饰函数,而无需先“调用”:

@decorator
def my_func():
    pass

或使用参数定制:

@decorator(parameter1="example.com", ...):
def my_func():
    pass

2019年 -使用Python 3.8和仅位置参数,最后一个模式将变得更好,因为func可以将参数声明为仅位置参数,并要求对参数进行命名;

def decorator(func=None, *, parameter1=None, parameter2=None, ...):


 类似资料:
  • 主要内容:带参数的函数装饰器,函数装饰器可以嵌套前面章节中,我们已经讲解了 Python 内置的 3 种函数装饰器,分别是 @staticmethod、@classmethod 和 @property,其中 staticmethod()、classmethod() 和 property() 都是 Python 的内置函数。 那么,函数装饰器的工作原理是怎样的呢?假设用 funA() 函数装饰器去装饰 funB() 函数,如下所示: 实际上,上面

  • 问题内容: 我想这就是它们的称呼,但我会举一些例子以防万一。 装饰类: 装饰器功能: 使用一个或另一个只是口味的问题吗?有实际区别吗? 问题答案: 如果您可以编写函数来实现装饰器,则应首选它。但是并非所有装饰器都可以轻松地编写为一个函数-例如,当您要存储一些内部状态时。 我见过人们(包括我自己)经过荒唐的努力,只用函数编写装饰器。我仍然不知道为什么,一个班级的开销通常可以忽略不计。

  • 本文向大家介绍详谈Python高阶函数与函数装饰器(推荐),包括了详谈Python高阶函数与函数装饰器(推荐)的使用技巧和注意事项,需要的朋友参考一下 一、上节回顾 Python2与Python3字符编码问题,不管你是初学者还是已经对Python的项目了如指掌了,都会犯一些编码上面的错误。我在这里简单归纳Python3和Python2各自的区别。 首先是Python3-->代码文件都是用utf-8

  • 本文向大家介绍使用python装饰器计算函数运行时间的实例,包括了使用python装饰器计算函数运行时间的实例的使用技巧和注意事项,需要的朋友参考一下 装饰器在python里面有很重要的作用, 如果能够熟练使用,将会大大的提高工作效率 今天就来见识一下 python 装饰器,到底是怎么工作的。 本文主要是利用python装饰器计算函数运行时间 一些需要精确的计算函数运行了多久的程序,都可以采用这种

  • 问题内容: 我想构造用作装饰器的类,并保留以下原则: 应该有可能在top 1函数上堆叠多个此类装饰器。 产生的函数名称指针应该与没有装饰器的相同函数没有区别,可能只是它是哪种类型/类。 除非装饰员实际要求,否则订购装饰员应该无关紧要。就是 独立的装饰器可以以任何顺序应用。 这是针对Django项目的,现在我正在处理的特定情况下,该方法需要2个装饰器,并显示为普通的python函数: @AutoTe

  • 本文向大家介绍基于Python 装饰器装饰类中的方法实例,包括了基于Python 装饰器装饰类中的方法实例的使用技巧和注意事项,需要的朋友参考一下 title: Python 装饰器装饰类中的方法 comments: true date: 2017-04-17 20:44:31 tags: ['Python', 'Decorate'] category: ['Python'] --- 目前在中文网