Python装饰器示例

Max*_*Max 5 python decorator python-decorators

我正在从编码器的一个很棒的教程中学习一些关于装饰器的知识,但是我发现自己对一个例子感到困惑.

首先给出一个简单的例子,然后解释装饰器是什么.

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

my_get_text = p_decorate(get_text)

print my_get_text("John") 
Run Code Online (Sandbox Code Playgroud)

现在这对我来说很有意义.装饰器只是函数的包装器.在这个家伙的解释中,他说装饰器是一个函数,它将另一个函数作为参数,生成一个新函数,并返回生成的函数以便在任何地方使用.

现在相当于上面的代码是:

def p_decorate(func):
   def func_wrapper(name):
       return "<p>{0}</p>".format(func(name))
   return func_wrapper

@p_decorate
def get_text(name):
   return "lorem ipsum, {0} dolor sit amet".format(name)

print get_text("John")
Run Code Online (Sandbox Code Playgroud)

我相信我理解装饰器在没有参数时被初始化的方式.如果我错了,请纠正我.

  • 默认情况下,装饰器传入函数get_text,因为p_decorate返回一个函数func_wrapper,我们最终得到了true语句get_text = func_wrapper.

对我来说重要的是第一个等效的代码块,因为我看到并理解了装饰器的行为方式.

令我困惑的是以下代码:

def tags(tag_name):
    def tags_decorator(func):
        def func_wrapper(name):
            return "<{0}>{1}</{0}>".format(tag_name, func(name))
        return func_wrapper
    return tags_decorator

@tags("p")
def get_text(name):
    return "Hello "+name

print get_text("John")
Run Code Online (Sandbox Code Playgroud)

如果我错了,请再次纠正我,但这是我的理解.

  • 装饰器接受标记字符串"p"而不是默认函数名称.反过来,该函数tags_decorator 假定将传入的参数是要装饰的函数get_text.

看到"非装饰器"形式的等效代码块可能对我有所帮助,但我似乎无法理解那些看起来像什么的东西.我也不理解为什么tags_decoratorfunc_wrapper都归还了.什么是返回两种不同的功能,如果一个装饰只需要返回1个功能包的目的get_text.

作为旁注,它实际上归结为以下几点.

  • 这个块可以简化为少于一组3个函数吗?
  • 装饰器可以接受多于1个参数来简化代码吗?

Mar*_*ers 7

在限制范围内,@执行后的所有内容都会生成装饰器.在您的第一个示例中,后面的@内容只是一个名称:

@p_decorate
Run Code Online (Sandbox Code Playgroud)

所以Python查找p_decorate并使用装饰函数作为参数调用它:

get_text = p_decorate(get_text)
Run Code Online (Sandbox Code Playgroud)

(过度简化了一点,get_text最初并没有绑定到原始函数,但你已经得到了要点).

在你的第二个例子中,装饰器表达式涉及更多,它包括一个调用:

@tags("p")
Run Code Online (Sandbox Code Playgroud)

所以Python tags("p")用来找到装饰器.最后这是执行的内容:

get_text = tags("p")(get_text)
Run Code Online (Sandbox Code Playgroud)

输出tags("p")是这里的装饰!我将tags函数本身称为装饰器工厂,它在调用时生成装饰器.当你打电话时tags(),它会返回tags_decorator().那是真正的装饰者.

您可以改为删除装饰器功能并对"p"值进行硬编码并直接使用:

def tags_decorator_p(func):
    def func_wrapper(name):
        return "<{0}>{1}</{0}>".format("p", func(name))
    return func_wrapper

@tags_decorator_p
def get_text(name):
    # ...
Run Code Online (Sandbox Code Playgroud)

但是你必须为参数的每个可能值创建单独的装饰器tags().这是装饰工厂的价值,你可以向装饰器添加参数并改变你的功能装饰方式.

装饰工厂可以使用任意数量的参数; 它只是你调用生成装饰器的函数.装饰器本身只能接受一个参数,即装饰功能.

我说,我的回答开始的限度内有一个原因; 表达式的语法@仅允许带点名称(foo.bar.baz如此属性访问)和call((arg1, arg2, keyword=arg3)).请参阅参考文档.最初的PEP 318规定:

装饰器语句受限于它可以接受的内容 - 任意表达式都不起作用.Guido更喜欢这种,因为有一种直觉[17].