函数定义:
@装饰器1
@装饰器2
...
def 函数名(位置形参, *元组形参, 命名关键字形参, **字典形参):
"""文档字符串"""
语句块
正式介绍装饰器前,先让我们学习一下闭包的概念:
闭包 closure:
闭包是指引用了此函数外部嵌套函数作用域变量的函数
闭包必须满足3个条件:
1.必须有内嵌函数
2.内嵌函数必须引用外部函数中的变量
3.外部函数返回值必须是“内嵌函数”
1.有如下函数,各方面测试已OK:
def decorated(x):
print('x ** x = %d' % x ** x)
decorated(2)
decorated(3)
2.现在想在每次调用此函数前,均输出一次当前的时间,且不允许修改函数内部:
import time
def decorated(x):
print('x ** x = %d' % x ** x)
def decorate(x):
print("%d:%d:%d" % time.localtime()[3:6])
decorated(x)
decorate(2)
decorate(3)
此方法存在的一个问题是:如果原程序中多处调用了decorated函数,则需把每一处都修改为decorate,工作量很大。下面尝试在程序调用时无需修改原函数名:
import time
def decorated(x):
print('x ** x = %d' % x ** x)
def decorate(func):
print("%d:%d:%d" % time.localtime()[3:6])
return func
decorated = decorate(decorated)
decorated(2)
decorated(3)
运行结果:
14:50:25
x ** x = 4
x ** x = 27
此方法可以实现在程序调用时,无需修函数名;但仍存在一个非常严重的错误,decorated函数本身并未做任何改变,因此运行结果中,只在第一次调用次函数时打印一次时间,decorate函数只在decorated = decorate(decorated)赋值时运行一次。
3.为了解决2中存在的问题,我们可以借助闭包来实现:
import time
def decorated(x):
print('x ** x = %d' % x ** x)
def decorate(func):
def inner(x):
print("%d:%d:%d" % time.localtime()[3:6])
func(x)
return inner
decorated = decorate(decorated)
decorated(2)
decorated(3)
运行结果:
14:58:36
x ** x = 4
14:58:36
x ** x = 27
上述程序中,使用闭包实现了在不修改函数内部及调用程序的情况下,改变了原函数的功能。decorate函数内嵌了一个inner函数,且返回值为inner函数,并且inner函数访问了enclosing作用域变量func,形成了一个闭包。更值得庆幸的是,python中为了简化3中的写法,为我们提供了一个语法糖"@装饰函数",即装饰器:
import time
def decorate(func):
def inner(x):
print("%d:%d:%d" % time.localtime()[3:6])
func(x)
return inner
@decorate
def decorated(x):
print('x ** x = %d' % x ** x)
decorated(2)
decorated(3)
运行结果:
15:8:36
x ** x = 4
15:8:36
x ** x = 27
装饰器:
装饰器是一个函数,主要作用是用来包装另一个函数和类
是在不改变原函数名(或类名)的情况下,改变被包装对象的行为
什么是函数装饰器:
1.函数装饰器是指装饰器是一个函数,传入的是一个函数,返回的也是一个函数
2.函数一旦被装饰,它绑定的将是装饰器函数的返回值。
语法结构:
def 装饰器函数名(函数名作为参数):
语句块
return 函数对象
@装饰器函数名<换行>
def 函数名(形参列表):
语句块
函数装饰器的应用:
def decorator_fun(fn):
def fx():
print("在函数original_fun调用之前,新增的处理程序!")
fn()
print("在函数original_fun调用之后,新增的处理程序!")
return fx
@decorator_fun
def original_fun():
print("此函数已经过测试,一切OK!")
original_fun()
备注:
1.在程序开发过程中,一旦已经测试OK的函数,在后期如需新增功能时,应尽量避免去修改
原函数,通常使用函数装饰器来完成。且使用函数装饰器,调用部分的的程序也无需修改。
2.对于被装饰函数需要支持参数的情况,装饰器的内嵌函数须支持同样的签名
函数说明:
1.函数本身可以赋值给变量,赋值后变量绑定函数
2.允许将函数本身作为参数,传递给另一个函数
3.允许函数返回一个函数
4.应使用函数式编程思想,但程序中修改模块内部是大忌。
函数的4种形参定义方式:
位置形参
星号元组形参
命名关键字形参
双星号字典形参
位置形参:
语法:
def 函数名(形参名1, 形参名2, ...):
语句块
星号元组形参:
语法:
def 函数名(*元组形参名):
语句块
作用:
允许传入任意个位置实参
命名关键字形参:
语法:
def 函数名(*, 命名关键字形参)
或:
def 函数名(*args,命名关键字形参)
说明:
强制所有的传参都必须使用关键字传参
双星号字典形参:
语法:
def 函数名(**字典形参名):
语句
作用:
允许传入任意个关键字实参
形参定义的顺序:
位置形参
星号元组形参
命名关键字形参
双星号字典形参
函数的文档字符串:
语法格式:
def 函数名(形参列表)
"""函数的文档字符串"""
函数语句块
示例:
def func():
'''此函数用来打招呼...
这是函数的文档字符串
'''
pass
可在交互模式下输入如下命令,查看函数的文档字符串:
>>>import 模块名
>>>help(模块名.func)
说明:
文档字符串用来说明本函数的功能和使用方法,方便程序的阅读和后期维护
函数的__doc__属性:
备注:
所有系统定义的特殊含义的标识符使用的是双下划线,为了避免与用户定义的标志负产生冲突
作用:
用于记录文档字符串
操作:
>>>max.__doc__
>>> import 模块名
>>> 模块名.函数名.__doc__
函数的__name__属性:
作用:
用于记录函数的名称
函数的4中实参传递方式:
位置传参 / 序列传参(字符串,列表,元组)
关键字传参 / 字典关键字传参
位置传参:
实参与形参的对应关系按位置来依次对应
def func(a, b, c):
...
func(1, 2, 3)
序列传参:
函数在调用过程中用 “*” 将序列拆解后按位置进行传递的传参方式
def func(a, b, c):
...
s = "ABC"
L = [4, 5, 6]
t = (1.1, 1.2, 1.3)
func(*s)
func(*L)
func(*t)
关键字传参:
按着形参的名称给形参赋值;实参和形参按名称进行匹配
字典关键字传参:
将字典用 “**” 拆解后进行关键字传参的传参方式
def func1(a, b, *, c, d):
...
def func2(a, b, *args, c, d):
...
func1(1, 2, c=300, d=400)
func2(1, 2, 3, 4, c=300, d=400)
func2(1, 2, 3, 4, **{'d':400, 'c':300})
综合传参:
1.函数的传参方式确定形参能唯一匹配到相应的实参。
2.位置传参在前,关键字传参在后