跳转至

函数

1. 函数的作用

  1. 代码重用,减少代码冗余
  2. 流程分解

2. 函数分类

  1. 内置函数
  2. 自定义函数

3. 函数的定义

1
2
3
4
def 函数名(arg1, arg2, ...):
    """描述"""
    函数体
    return value

程序运行时会创建一个函数对象并将其赋值给一个变量名,通过在函数名后加括号调用这个函数。

1
2
3
4
5
>>> def bar():
...     print("bar")
...
>>> print(bar)
<function bar at 0x000002AEFFF01EA0>

4. 函数的参数

4.1 无参数

1
2
def bar():
    print("bar")

4.2 位置参数

1
2
3
def add(x,y):
    print (x+y)
add(3,5)

4.3 默认参数

1
2
3
def student(name, age=21):
    print('Name:',name)
    print('Age:',age)

4.4 可变参数

1
2
3
4
5
6
7
# 传入的参数个数是可变的,可以是 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 关键字参数

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
# 关键字参数在函数内部自动组装为一个 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 命名关键字参数

1
2
3
4
5
# *后面的参数被视为命名关键字参数,只接收 city 和 job 作为关键字参数
def person(name, age, *, city, job):
    print(name, age, city, job)

person('张三', 18, city="xi'an", job='IT')

4.7 组合参数

参数顺序:必选参数、默认参数、可变参数/命名关键字参数和关键字参数

5. 作用域法则LEGB

  1. L-Local(function);函数内的名字空间
  2. E-Enclosing function locals;外部嵌套函数的名字空间(例如closure)
  3. G-Global(module);函数定义所在模块(文件)的名字空间
  4. B-Builtin(Python);Python内置模块的名字空间

6. 函数的返回值

6.1 默认返回None

1
2
3
4
5
6
7
>>> def bar():
...     print ("hello")
...
>>> f = bar()
hello
>>> print(f)
None

6.2 指定返回值

1
2
3
4
5
6
7
>>> def bar():
...     print("hello")
...     return 1
...
>>> f = bar()
hello
>>> print(f)

6.3 返回计算结果

1
2
3
4
5
>>> def bar(x,y):
...     return x + y
...
>>> f = bar(1,2)
>>> print(f)

6.4 结束函数

1
2
3
4
5
6
>>> def bar():
...     print("hello")
...     return 1
...     print("world")
>>> bar()
hello

6.5 返回元组

1
2
3
4
5
6
# 用逗号分隔开多个值,会被return返回成元组
>>> def bar(x,y):
...     return x,y
...
>>> bar(1,2)
(1, 2)

return的作用

  1. 结束函数
  2. 将一个结果对象传回给调用者

7. 闭包(closure)

在一个内部函数对在外部的作用的作用域进行一个引用,那么内部函数就是一个闭包

1
2
3
4
5
6
7
8
9
>>> def maker(N):
...     def action(X):    #一个内部函数
...         return X ** N  #N为上级函数的一个变量
...     return action
...
>>> f = maker(2)
>>> f
<function maker.<locals>.action at 0x000002C47A1E2A60>
>>> f(3)

闭包的三个特点:

  1. 闭包函数必须有内嵌函数
  2. 内嵌函数需要引用该嵌套函数上一级namespace中的变量
  3. 闭包函数必须返回内嵌函数

8. 高阶函数

8.1 变量名可以指向函数

1
2
3
>>> abs(-10)
>>> f = abs
>>> f(-10)

8.2 函数名可以作为其他函数的参数

1
2
3
4
>>> def bar(x,y,f):
...     return f(x)+f(y)
...
>>> bar(-5,-3,abs)

8.3 函数名可以作为返回值

1
2
3
4
5
6
7
>>> 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,如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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组合来满足被装饰函数参数的所有情况

1
2
3
4
5
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

可以通过如下方式来修正装饰器:

1
2
3
4
5
6
7
8
9
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()

# 加载顺序自下而上

执行结果

1
2
3
4
5
6
7
8
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 globalnonlocal

10.1 global

1
2
3
4
5
6
x = 88
def bar():
    global x
    x = 99
bar()
print (x)

10.2 nonlocal

用于嵌套函数中,只作用于外层函数的作用域中,而不是全局

1
2
3
4
5
6
7
8
9
def bar():
    x = 1
    def foo():
        nonlocal x
        x += 1
    foo()
    print(x)

bar()

11 匿名函数

lambda 形参:返回值

1
2
3
>>> 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的对象类型

1
2
3
4
5
def add(x,y):
    print (x+y)

add(3,5)  # 加法
add('hello','world') # 字符串拼接

给函数附加属性

1
2
3
4
5
6
foo():
    pass

foo.name = 'root'

print(foo.name)