函数
1. 函数的作用
- 代码重用,减少代码冗余
- 流程分解
2. 函数分类
- 内置函数
- 自定义函数
3. 函数的定义
| def 函数名(arg1, arg2, ...):
"""描述"""
函数体
return value
|
程序运行时会创建一个函数对象并将其赋值给一个变量名,通过在函数名后加括号调用这个函数。
| >>> def bar():
... print("bar")
...
>>> print(bar)
<function bar at 0x000002AEFFF01EA0>
|
4. 函数的参数
4.1 无参数
4.2 位置参数
| def add(x,y):
print (x+y)
add(3,5)
|
4.3 默认参数
| def student(name, age=21):
print('Name:',name)
print('Age:',age)
|
4.4 可变参数
| # 传入的参数个数是可变的,可以是 1 个、2 个到任意个,还可以是 0 个
def calc(*numbers):
sum = 0
for n in numbers:
sum = sum + n * n
return sum
print(calc(1, 2, 3))
|
4.5 关键字参数
| # 关键字参数在函数内部自动组装为一个 dict
def student(name, age, **kwargs):
print('Name:', name)
print('Age:', age)
print('other:', kwargs)
student('张三', 13, sex='boy')
###运行结果###
Name: 张三
Age: 13
other: {'sex': 'boy'}
|
4.6 命名关键字参数
| # *后面的参数被视为命名关键字参数,只接收 city 和 job 作为关键字参数
def person(name, age, *, city, job):
print(name, age, city, job)
person('张三', 18, city="xi'an", job='IT')
|
4.7 组合参数
参数顺序:必选参数、默认参数、可变参数/命名关键字参数和关键字参数
5. 作用域法则LEGB
- L-Local(function);函数内的名字空间
- E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
- G-Global(module);函数定义所在模块(文件)的名字空间
- B-Builtin(Python);Python内置模块的名字空间
6. 函数的返回值
6.1 默认返回None
| >>> def bar():
... print ("hello")
...
>>> f = bar()
hello
>>> print(f)
None
|
6.2 指定返回值
| >>> def bar():
... print("hello")
... return 1
...
>>> f = bar()
hello
>>> print(f)
|
6.3 返回计算结果
| >>> def bar(x,y):
... return x + y
...
>>> f = bar(1,2)
>>> print(f)
|
6.4 结束函数
| >>> def bar():
... print("hello")
... return 1
... print("world")
>>> bar()
hello
|
6.5 返回元组
| # 用逗号分隔开多个值,会被return返回成元组
>>> def bar(x,y):
... return x,y
...
>>> bar(1,2)
(1, 2)
|
return的作用
- 结束函数
- 将一个结果对象传回给调用者
7. 闭包(closure)
在一个内部函数对在外部的作用的作用域进行一个引用,那么内部函数就是一个闭包
| >>> def maker(N):
... def action(X): #一个内部函数
... return X ** N #N为上级函数的一个变量
... return action
...
>>> f = maker(2)
>>> f
<function maker.<locals>.action at 0x000002C47A1E2A60>
>>> f(3)
|
闭包的三个特点:
- 闭包函数必须有内嵌函数
- 内嵌函数需要引用该嵌套函数上一级
namespace
中的变量
- 闭包函数必须返回内嵌函数
8. 高阶函数
8.1 变量名可以指向函数
| >>> abs(-10)
>>> f = abs
>>> f(-10)
|
8.2 函数名可以作为其他函数的参数
| >>> def bar(x,y,f):
... return f(x)+f(y)
...
>>> bar(-5,-3,abs)
|
8.3 函数名可以作为返回值
| >>> def bar():
... def foo():
... return "hello"
... return foo
...
>>> bar()
<function bar.<locals>.foo at 0x00000194EED32A60>
|
9. 装饰器(Decorator)
开放封闭原则:
开放:对代码的扩展开放
封闭:对代码的修改封闭
装饰器完全遵循开放封闭原则。在不改变原函数的代码以及调用方式的前提下,为其增加代码功能。
装饰器三要素:LEGB、高阶函数、闭包
9.1 无参装饰器
9.1.1 装饰器实现思路
第一步:统计hello函数执行的时间
1
2
3
4
5
6
7
8
9
10
11
12
13 | def hello():
print("hello")
# 函数名可以作为其他函数的参数
def show_time(func):
print("------------")
func()
print("------------")
show_time(hello)
# 或者如下
# hello = show_time(hello)
# hello
# 但调用方式还是发生了改变
|
第二步:保留函数调用方式
如果把一个函数名赋值给hello,hello()就可以执行函数了,关键问题是如何让show_time(hello)作为函数名赋值给hello
想想高阶函数中函数名可以作为返回值返回的特性
可以把上述的函数改为如下
1
2
3
4
5
6
7
8
9
10
11
12 | def hello():
print("hello")
def show_time(func):
def inner():
print("------------")
func()
print("------------")
return inner #此时如果调用show_time,返回的就是inner函数的函数名
hello=show_time(hello) #将inner函数赋值给hello
hello() #实际执行的是inner函数
|
第三步: 语法糖@func
在python中hello=show_time(hello)可以写为@show_time,如下
| def show_time(func):
def inner():
print("------------")
func()
print("------------")
return inner
@show_time # hello=show_time(hello)
def hello():
print("hello")
hello()
|
9.2 被装饰函数的参数
1
2
3
4
5
6
7
8
9
10
11
12 | def show_time(func):
def inner(name):
print("------------")
func(name)
print("------------")
return inner
@show_time
def hello(name):
print("hello",name)
hello('Bob')
|
使用*args,**kwargs
组合来满足被装饰函数参数的所有情况
| def wrapper(func):
def inner(*args, **kwargs):
res = func(*args, **kwargs)
return res
return inner
|
9.2 有参装饰器
给装饰器加参数来实现不同的功能。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27 | import time
def logger(flag=''):
def show_time(func):
def inner(*args,**kwargs):
start = time.time()
time.sleep(1)
func(*args,**kwargs)
end = time.time()
print('running time:',end - start)
if flag == 'true':
print('this is loggger')
return inner
return show_time
@logger('true')
def foo(*args,**kwargs):
sums = 0
for i in args:
sums += i
print(sums)
@logger()
def bar():
print('bar..')
foo(1,2,3)
bar()
|
9.3 保留原函数的文档和函数名属性
functools.wraps
的作用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 | def show_time(func):
def inner(name):
print("------------")
func(name)
print("------------")
return inner
@show_time
def hello(name):
'''
say hello
:param name:
:return:
'''
print("hello", name)
print(hello.__name__)
print('----------')
print(help(hello)) # 使用help(函数名)来查看函数的文档注释,原函数的名称变成了inner,不是原来的hello
####运行结果####
inner
----------
Help on function inner in module __main__:
inner(name)
None
|
可以通过如下方式来修正装饰器:
| def show_time(func):
def inner(name):
print("------------")
func(name)
print("------------")
inner.__doc__ = func.__doc__
inner.__name__ = func.__name__
return inner
|
按照上述方式来实现保留原函数属性过于麻烦,functools
模块下提供一个装饰器wraps专门用来解决这个问题。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 | from functools import wraps
def show_time(func):
@wraps(func)
def inner(name):
print("------------")
func(name)
print("------------")
return inner
@show_time
def hello(name):
print("hello",name)
print(hello.__name__)
####运行结果####
>>hello
|
9.3 多个装饰器叠加
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42 | def deco1(func1): # func1 = wrapper2的内存地址
def wrapper1(*args, **kwargs):
print("deco1.wrapper1开始执行,func1:", func1) # <function deco2.<locals>.wrapper2 at 0x0000021CB8462EA0>
res = func1(*args, **kwargs)
print("deco1.wrapper1执行结束")
return res
return wrapper1
def deco2(func2): # func2 = wrapper3的内存地址
def wrapper2(*args, **kwargs):
print("deco1.wrapper2开始执行,func2:", func2) # <function deco3.<locals>.wrapper3 at 0x0000021CB8462D90>
res = func2(*args, **kwargs)
print("deco1.wrapper2执行结束")
return res
return wrapper2
def deco3(func3): # func3 = 被装饰对象foo的内存地址
def wrapper3(*args, **kwargs):
print("deco1.wrapper3开始执行,func3:", func3) # <function foo at 0x0000022282592E18>
res = func3(*args, **kwargs)
print("deco1.wrapper3执行结束")
return res
return wrapper3
@deco1 # foo = deco2(wrapper2的内存地址)
@deco2 # foo = deco2(wrapper3的内存地址)
@deco3 # foo = deco3(foo) ==> foo = deco3(被装饰对象foo的内存地址)
def foo():
print("hello world!")
print('foo:', foo)
foo()
# 加载顺序自下而上
|
执行结果
| foo: <function deco1.<locals>.wrapper1 at 0x00000176D6512F28>
deco1.wrapper1开始执行,func1: <function deco2.<locals>.wrapper2 at 0x00000176D6512EA0>
deco1.wrapper2开始执行,func2: <function deco3.<locals>.wrapper3 at 0x00000176D6512D90>
deco1.wrapper3开始执行,func3: <function foo at 0x00000176D6512E18>
hello world!
deco1.wrapper3执行结束
deco1.wrapper2执行结束
deco1.wrapper1执行结束
|
10 global
和 nonlocal
10.1 global
| x = 88
def bar():
global x
x = 99
bar()
print (x)
|
10.2 nonlocal
用于嵌套函数中,只作用于外层函数的作用域中,而不是全局
| def bar():
x = 1
def foo():
nonlocal x
x += 1
foo()
print(x)
bar()
|
11 匿名函数
lambda 形参:返回值
| >>> f = lambda a,b:a+b
>>> f(1,2)
3
|
匿名用于临时调用一次的场景:更多的是将匿名与其他函数配合使用
12 函数式编程
12.1 map
12.2 filter
12.3 reduce
13 函数的多态
函数中x,y的意义完全取决于x和y的对象类型
| def add(x,y):
print (x+y)
add(3,5) # 加法
add('hello','world') # 字符串拼接
|
给函数附加属性
| foo():
pass
foo.name = 'root'
print(foo.name)
|