大家好,我是剑南。近期有不少人咨询有关装饰器的知识,很多人对这一块了解甚少,因此我便打算出两期文章来专门写装饰器的知识,希望可以对大家带来帮助。
关于装饰器介绍
python中装饰器是指任何可以修改函数或类的可调用对象。它们允许一些类似于类似于其他语言的附加功能,例如将方法声明为类或者是静态方法。
类方法是在类是在类,而不是在特定实例调用的方法。静态方法类似于类方法,但将应用于类的所有实例,而不仅仅是特定的实例。在python中,实例方法是处理OOP的传统方法。
当一个函数被调用时,它被传递给装饰器,装饰器返回一个被修改过的函数/类,这些修改过的对象通常包含调用最初的调用对象。
注意:装饰器可以与函数或者方法一起使用,但通常都会简称它们为“函数”,这使得描述更加简洁。方法将在讨论类的时候时使用。
简单回顾函数
因为在处理装饰器时,理解函数是如何工作的是非常重要的,所以我们会快速浏览一下它们。首先,我们需要记住,python中一切都是对象,包括函数。
在python中使用def关键字并给出命名来创建函数。输入参数是可选的,以下是一些基本参考功能。
def func_foo():
pass
实现方法
(1)函数可以有多个名称,其实也可以这样理解,除了函数名本身,还可以将一个函数分配给其他变量名,每个变量名与基础函数具有相同的功能。
def first_func1(val):
print(val)
new_name = first_func1
first_func1("hello world")
print('*'*50)
new_name('hello world too')
运行结果,如下所示:
hello world
**************************************************
hello world too
(2)创建函数时,参数是可以是其他函数,可以是自定义函数,也可以是内置函数,例如map()和filter(),使用这个特性来完成工作。
具体代码,如下所示:
def mult(x, y):
return x * y
def div(x, y):
return x/y
def math(func, x ,y):
result = func(x, y)
return result
val1 = math(mult, 4, 2)
print(val1)
print("*" * 50)
val2 = math(div, 4, 2)
print(val2)
运行结果,如下所示:
8
**************************************************
2.0
(3)像循环一样,函数也可以做嵌套操作。
具体代码,如下所示:
def person(name):
def greeting():
return 'would you like some milk'
greet = greeting() + name + '?'
return greet
print(person('Sir Galahad'))
运行结果,如下所示:
would you like some milkSir Galahad?
(4)函数可以用作其他函数的参数。这是因为函数参数实际上是对对像的引用,由于函数是对象,因此函数(实际上是对函数对象的引用)可以用作参数。
具体代码,如下所示:
def greeting(name):
return "'allo " + name
def call_me(func):
nickname = 'mate'
return func(nickname)
print(call_me(greeting))
运行结果,如下所示:
'allo mate
同样,函数可以返回函数,这是因为函数的返回值是对对象的引用。
具体代码,如下所示:
def func_creator():
def return_saying():
return 'Blessed are the cheese makers'
return return_saying
statement = func_creator()
print(statement())
运行结果,如下所示:
Blessed are the cheese makers
(6)嵌套函数可以访问其父函数的作用域,这也称为闭包。我们必须认识到,这种访问是只读的,嵌套函数不能写出,或将变量赋值给外部作用域。
在实际情况下,这与哈函数变量分配参数没有什么不同,输入参数只是将其传递给另一个封闭函数,而不是变量。
具体代码,如下所示:
def func_creator2(name):
def greeting():
return 'welcome ' + name
return greeting
greet = func_creator2('Jack')
print(greet())
运行结果,如下所示:
welcome Jack
工作原理
函数及其面向对象的兄弟——方法,是许多编程语言中必不可少的元素,它们允许代码的复用,因为可以从代码中的不同位置多次调用函数。如果语言支持,甚至可以从不同的程序中调用它们,例如导入python。
函数还允许抽象工作,在某种程度上,函数类似于黑盒。开发人员只需要知道为函数提供什么数据、函数如何处理这些数据,以及是否返回值。只要结果一致,就不需要知道函数的实现过程,内部代码你是不知道的。
编写一个没有函数的程序是可行的,但是它要求对整个程序进行串行处理。所有需要重复的功能代码必须每次都需要复制粘贴,这也是为什么最早的高级语言也包含了子程序,子程序允许程序员跳出主程序的的逻辑来处理一些数据,然后返回到主程序。
在此之前,必须使用特殊的调用序列来实现子程序,以便将返回地址存储到主程序中。
装饰器简介
有了以上的基础,接下来就可以价绍装饰器了。装饰器是将一个函数包装在另一个函数中,该函数以某种方式修改原始函数,例如添加功能、修改参数或结果,等待。
实现方法
(1)首先要定义装饰器函数
# 定义装饰器函数
def fun_decorator(some_funct):
def wrapper():
print('这是一个装饰器')
for i in range(10):
print(i)
print('装饰器运行完毕,返回原始的调度方法')
print(some_funct())
return wrapper
(2)定义主函数
# 定义主函数
def main_fun():
text = '我是主程序'
return text
(3)将主函数作为一个变量传递给装饰器,并把这个装饰器赋值给主函数
main_fun = fun_decorator(main_fun)
(4)调用主函数
main_fun()
(5)将上面的代码进行整合,如下所示:
# 定义装饰器函数
def fun_decorator(some_funct):
def wrapper():
print('这是一个装饰器')
for i in range(10):
print(i)
print('装饰器运行完毕,返回原始的调度方法')
print(some_funct())
return wrapper
# 定义主函数
def main_fun():
text = '我是主程序'
return text
main_fun = fun_decorator(main_fun)
main_fun()
(6)运行代码,结果如下图所示:
(7)我们可以使用语法糖(@符号)来注释主函数是由fun_decorator修改的,这样可以消除main_fun = fun_decorator(main_fun),具体代码,如下所示:
# 定义装饰器函数
def fun_decorator(some_funct):
def wrapper():
print('这是一个装饰器')
for i in range(10):
print(i)
print('装饰器运行完毕,返回原始的调度方法')
print(some_funct())
return wrapper
@fun_decorator
def main_fun():
text = '我是主程序'
return text
main_fun()
(8)修改代码后,运行结果一致,如下图所示:
工作原理
当一个带有装饰器的函数被调用,装饰器函数会捕捉到这个调用,然后装饰器函数会执行它的工作。在它完成之后,移交给原始函数,从而完成任务。
语法糖是编程语言中的一种特殊语法,旨在通过使代码更易于读或写,令程序员的工作更轻松。语法糖表达式是通过查看糖丢失时代码功能是否会一起丢失来识别的。
使用函数装饰器
函数装饰器显然适用于函数。@fun_decorator装饰器行放在函数定义之前的行上。语法糖接收一个函数,并通过另一个函数自动运行其结果。在处理结束时,原始函数调用的名称应用于最终结果。对于系统来说,它看起来像直接提供结果的原始函数调用。(但,其实并不是)下面是装饰器的演示。
@fun_decorator
def my_function():
pass
当python解释器到达此代码块时,将处理my_function()并将结果传递给@fun_decorator所指向的函数。装饰器函数将被处理,结果替换为原来的my_function()结果。从本质上说,装饰器劫持了函数的调用,修改原始结果,并将修改结果替换为原始函数提供的结果。
修改装饰器代码,可以采用管理或增加原始调用的形式。一旦一个函数完成它的工作,装饰器就接管并对原始结果进行处理,返回修改后的代码。
实现方法
(1)我们必须要明确装饰器要做什么。对于下面的例子,装饰器函数将查看传递给函数的参数,并检查传递的值是否是整数。
(2)编写装饰器函数
def arg_check(func):
def wrapper(num):
if type(num) != int:
raise TypeError('参数不是一个整数')
elif num <= 0:
raise ValueError('参数不是一个正数')
else:
return func(num)
return wrapper
(3)编写需要修饰的函数
@arg_check
def circle_measures(radius):
circumference = 2 * pi *radius
area = pi * radius * radius
diameter = 2 * radius
return (circumference, area, diameter)
(4)调用并打印有关圆的值
circumference, area, diameter = circle_measures(6)
print('circumference is ', circumference, '\n', 'area is ', area, '\n', 'diameter is ', diameter)
注意:在实际情况下,这与为函数变量分配参数没有什么不同。输入参数只是将其传递给另一个封闭函数,而不是变量。
例如,上面的代码circle_measures(6)里面的6是传递给装饰器中的wrapper()函数。
使用类装饰器
从python的2.6版本开始,装饰器就被用于处理类。在这种情况下,装饰器不仅可以应用于函数,还可以用于单个实例或类本身。它们使开发人员的意图更加明显,在调用方法或处理对象时,它们可以用于最小化错误。
实现方法
(1)类方法也可以使用装饰器。实例方法是最常见的方法形式,即类中的函数。具体代码,如下所示:
class Cat(object):
"""docstring for Cat"""
def __init__(self, breed, age):
super(Cat, self).__init__()
self.breed = breed
self.age = age
def cat_age(self):
return self.age
def breed(self):
return self.breed
def __repr__(self):
return '{breed}, {age}'.format(breed = self.breed, age = self.age)
(2)要使用这个类,需要创建一个Cat实例,提供初始参数。
chip = Cat('domestic shorthair', 4)
(3)接下来,调用类方法,使其可以正常运行。
print(chip.age)
print(chip.breed)
(4)注意,这些方法被绑定到了一个实例chip中,因此,它们不能再泛型Cat类上调用。
print(Cat.age)
上面的写法是错误的。
(5)静态方法是应用于所有实例的方法。它们用方法定义之前的@staticmethod装饰器标识。另外,方法本身不需要定义self参数。
@staticmethod
def cry():
return '橘猫'
(6)静态方法可以应用于实例和类本身,具体代码,如下所示:
class Cat(object):
"""docstring for Cat"""
def __init__(self, breed, age):
super(Cat, self).__init__()
self.breed = breed
self.age = age
def cat_age(self):
return self.age
def breed(self):
return self.breed
@staticmethod
def cry():
return '橘猫'
def __repr__(self):
return '{breed}, {age}'.format(breed = self.breed, age = self.age)
chip = Cat('橘猫', 10)
print(chip.cry)
print(chip.cry())
print(Cat.cry)
print(Cat.cry())
运行结果,如下所示:
橘猫
橘猫
当调用的是不带括号的静态方法会返回方法的内存位置。方法没有绑定到实例,但是类页可以使用它。只有在使用括号时,才会显示正确的返回对象。
(7)类方法由创建方法之前的@classmethod标识。此外,方法参数是cls而不是self。可以在前面的例子中的静态方法之后添加以下代码。
class Cat(object):
"""docstring for Cat"""
def __init__(self, breed, age):
super(Cat, self).__init__()
self.breed = breed
self.age = age
def cat_age(self):
return self.age
def breed(self):
return self.breed
@staticmethod
def cry():
return '橘猫'
@classmethod
def type(cls):
if cls.__name__ == "Cat":
return "some sort of domestic cat"
else:
return cls.__name__
def __repr__(self):
return '{breed}, {age}'.format(breed = self.breed, age = self.age)
chip = Cat('橘猫',1)
print(chip.type())
运行结果,如下所示:
some sort of domestic cat
在创建实例时,将检查它来自哪一个类。如果泛型Cat类是生成器,则会输出一条消息,如果使用Cat类的子类,则输出该类名称。
最后
第一期的装饰器知识已经写完了,但还没有结束。
敬请期待我下一期写的装饰器知识。
我想对观看到这的小伙伴们说,文章的每一个字都是我用心敲出来的,希望你可以点个【再看】,让我知道,你就是那个陪我一起努力的人。