函数 | 机械细胞
0%

函数

函数的参数

形参与实参

形参: 相当于变量名,定义的参数

实参: 相当于变量值,传入的值

赋值: 在调用时,将实参内存地址绑定给形参

形参只能在函数体内使用

绑定关系只在调用时生效

形式:

1
func(int("1"), fuc(1,2), a = 1, 2)  # 实参: 值

参数分类

位置形参: 直接定义的变量名, 必须传值

位置实参:顺序与形参一一对应

关键字实参: 按key =value与形参对应

位置实参与关键字实参混用: 1. 位置实参必须放在关键字实参之前;2. 不能重复传值

默认形参: 直接定义的变量名并赋值,可以不传值, 用于被多次使用的实参,一般不推荐可变类型(函数的返回值应符合期望,不应受其他代码的影响)

位置形参与默认形参混用: 位置形参必须放在默认形参之前

可变长度的*args参数:

  1. 形参中的*保存溢出的位置实参元组类型赋值*后面的参数名,规范使用args作为变量名,(封装),例 相加

    1
    2
    3
    4
    5
    6
    7
    8
    def sum(*args):  # args = (1, 2, 3, 4, 5)
    res = 0
    for item in args:
    res += item
    print(res)


    sum(1, 2, 3, 4, 5)
  2. 实参中的*拆分*后面的可被for循环的数据类型位置实参赋值溢出的位置形参,(打碎),例

    1
    2
    3
    4
    5
    6
    def func(x, y, z):
    print(x, y,z)


    l = [1, 2, 3]
    func(*l) # x, y, z = l # func(1, 2, 3)
  3. 在形参与实参均存在*,(先打碎后封装),例

    1
    2
    3
    4
    5
    6
    def func(x, y, *args):  # args = ([3, 4, 5],)  # args = (3, 4, 5)
    print(x, y, args) # 1, 2, ([3, 4, 5], ) # 1, 2, (3, 4, 5)


    func(1, 2, [3, 4, 5])
    func(1, 2, *[3, 4, 5]) # func(1, 2, 3, 4, 5)

可变长度的**kwargs参数:

  1. **保存溢出的关键字实参字典类型赋值**后面的参数名,规范使用kwargs作为变量名,(封装),例 用户数据

  2. **拆分**后面的字典类型关键字实参赋值溢出的默认形参,(打碎),例

    1
    2
    3
    4
    5
    6
    7
    def func(x, y, z):
    print(x, y,z)


    l = {'x': 1, 'y': 2, 'z': 3} # 字典`:`
    func(*l) # x, y, z = l.key
    func(**l) # x, y, z = l.value
  3. 在形参与实参均存在**(先打碎后封装)

可变长度的位置参数与可变长度的关键字参数混用:** *args必须在*kwargs之前**

可变参数的骚操作: (先封装后打碎),index直接获得wrapper传入的实参

1
2
3
4
5
6
7
8
9
def index(x, y, z): # x = 1, y = 2, z = 3
print(x, y, z)


def wrapper(*args, **kwargs): # args = (1, 2) kwargs = {'y': 1,} # 封装
index(*args, **kwargs) # x, y = args, z = kwargs.value # 打碎


wrapper(1, 2, z=3)

命名关键字参数:*后加形参,则该形参必须以关键字实参的形式传入

1
2
3
4
5
6
def func(x, y, *, a, b):  # a,b为命名关键字参数
print(x, y)
print(a, b)


func(1, 2, a = 3, b = 4)

组合使用:

  • 位置形参,默认形参,*args,命名关键字形参,**kwargs
  • 位置实参,*args,关键字实参,**kwargs

总结

  1. python所有的传递都是内存地址的传递,即引用传递

  2. 默认形参的赋值不使用可变类型,应赋值为None,然后在函数体内定义为可变类型

    1
    2
    3
    4
    5
    6
    7
    def dunc(x, y, z, l = None)
    if l is None:
    l = []
    l.append(x)
    l.append(y)
    l.append(z)
    print(l)
    • (先封装再打碎), 获得原值
    • (先打碎再封装),将可被for循环的数据类型转为元组,将字典转为字典
    • 在这个过程中,填充形参与实参缺失的部分,使其一一对应.
    • 位置形参,默认形参,*args,命名关键字形参,**kwargs
    • 位置实参,*args,关键字实参,**kwargs

名称空间与作用域

名称空间

名称空间: 存放名字,是对栈区的划分

好处: 可以在栈区存放相同的名字

名称空间分类

内置名称空间: 存放在python解释器内置的名字,例<built-in function print>

  • 存活时间: python解释器的运行时间

全局名称空间: 非内置或函数内,剩下的都是, 包括iffor

  • 存活时间: python文件的运行时间

局部名称空间: 函数体内

  • 存活时间:** 函数调用**时间

名称空间的加载顺序:** 内置**名称空间>全局名称空间>>局部名称空间(不一定加载)

名称空间的查找顺序:

  • 函数体内: 局部名称空间>全局名称空间>内置名称空间
  • 全局名称空间>内置名称空间

作用域

全局作用域: 全局存活,全局有效

局部作用域: 临时存活,局部有效

global:局部修改全局的名字对应的值(不可变类型)

1
2
3
4
5
6
7
8
def func():
global x
x = 222


x = 111
func()
print(x)

nonlocal:局部修改外层函数的名字对应的值(不可变类型)

1
2
3
4
5
6
7
8
9
10
def a():
x = 1
def b():
nonlocal x
x = 2


x =0
a()
print(x)

总结

  1. 解释器在函数体内(内置名称空间)找不到名称时,将在定义该函数体的全局名称空间或局部名称空间找,且以调用函数时的名称空间内数据为准.

    函数定义时的位置决定是哪个名称空间,调用的位置决定名称空间里的数据

    1
    2
    3
    4
    5
    6
    def func():
    print(x)


    x = 1
    func() # x=1而不是报错,此时名称空间内有x=1
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    def a():
    y = z
    print(x)

    def b():
    print(y)

    b()


    def c():
    x = 3 # 提示未引用
    y = 4 # 提示未引用
    a()


    x = 1
    y = 2
    z = 5
    c() # x=1,y=5
    print(x, y)
  2. LEGB

    • L —— Local(function);函数内的名字空间
    • E —— Enclosing function locals;外部嵌套函数的名字空间(例如closure)
    • G —— Global(module);函数定义所在模块(文件)的名字空间
    • B —— Built_in(Python);Python内置模块的名字空间

函数对象与闭包函数

函数对象

函数对象: 函数可以被当做’数据’来处理

  • 函数可以被引用
  • 函数可以作为参数传入另外一个函数
  • 函数的返回值可以是一个函数
  • 函数可以作为容器类型的元素
1
2
3
4
5
6
7
8
9
def foo(x):  # x = func, 函数可以被引用
return x # 函数的返回值可以是一个函数

def func():
pass


res = [foo(func),] # 函数可以作为参数传入另外一个函数
res[0]() # res = [func,], 函数可以作为容器类型的元素

函数嵌套

  1. 函数的嵌套调用:在函数内调用其他的函数
  2. 函数的嵌套定义:在函数内定义其他的函数

闭包函数

闭包函数=名称空间与作用域+函数嵌套+函数对象

核心点:函数定义时的位置决定是哪个名称空间,调用的位置决定名称空间里的数据

闭包函数: 若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该’内嵌函数’就是闭包函数,简称闭包(Closures)

  • “闭”代表是内嵌函数
  • “包”代表函数外’包裹’着对外层作用域(而非全局作用域)的引用。
  • 内嵌函数能够在全局使用
  • 无论在何处调用闭包函数,使用的仍然是包裹在其外层的变量
1
2
3
4
5
6
7
8
9
def f1():
x = 1 # 内嵌函数f2永远使用当前层变量(名称空间与作用域)
def f2(): # 内嵌函数f2(函数嵌套)
pass
return f2 # 在外部引用f2(函数对象)


f = f1()
f()

闭包函数的用途

为函数体传值的方式:

  • 直接将值以参数的形式传入

  • 将值包给函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    import requests

    #方式一:
    def get(url):
    return requests.get(url).text

    #方式二:
    def page(url):
    def get():
    return requests.get(url).text
    return get

    # 方式一下载同一页面
    get('https://www.python.org')
    get('https://www.python.org')
    get('https://www.python.org')
    ……

    # 方式二下载同一页面
    python=page('https://www.python.org')
    python()
    python()
    python()
    ……

对比两种方式,方式一在下载同一页面时需要重复传入url,而方式二只需要传一次值,就会得到一个包含指定url的闭包函数,以后调用该闭包函数无需再传url

总结

  1. python所有的传递都是内存地址的传递,即引用传递

  2. 函数作为容器元素的应用: 批量管理函数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    def login():
    pass

    def transfer():
    pass

    def add():
    pass


    func_dic={
    '1'= login,
    '2'= transfer,
    '3'= add,
    }
    func_dic['1']() # login()
    1. 函数对象的引用不加括号
    2. 闭包函数的精髓是:
      • 返回的既可以是函数对象也可以是值
      • 可以对原函数重新命名
      • 可以为原函数提供新的参数,并提高原有参数的可拓展性
      • 可以为原函数添加新功能而不改变调用方式
-------------本文结束感谢您的阅读-------------

欢迎关注我的其它发布渠道

}