跳转至

📔 Python函数 学习笔记

1. 函数概述

  • 函数是为了解决代码重复和程序结构差的一种代码块。

  • 函数相当于具备某个功能的工具。对于函数的使用必须遵循一个原则:

    • 先定义后调用(先准备工具再使用)

1.1 为什么需要函数?

为了解决代码存在的一些问题:

  • 组织结构不清晰,可读性较差
  • 代码会冗余
  • 可维护性差,可扩展性差。
  • 程序的模块管理差
  • 代码重复率高

所以在一定程度上,函数就是通过封装,让代码可重复利用提高,保持程序的模块性,也让代码块符合单一性的原则

1.2 函数定义语法介绍

1
2
3
4
def 函数名(参数列表): # 参数可有可无
    """文档描述"""
    函数体
    return  # return可有可无

语法解释:

  • def:定义函数的关键字
  • 函数名:函数名指向函数内存地址,是对函数体代码的引用。函数的命名可以反映出函数的功能
  • 括号:括号内定义参数,参数可有可无,且无需指定参数的类型
  • 冒号:括号后面要加冒号。然后在下一行开始缩进编写函数体的代码
  • """文档描述""":描述函数功能,参数介绍等信息的文档。非必要,但是建议加上,从而增强函数的可读性。【一般在开源软件中详细,其余情况可写可不写】
  • 函数体:由语句和表达式组成
  • return 值:定义函数的返回值,return 是可有可无。

1.3 函数定义三种形式及其应用场景

# 方式1 :无参函数的定义
def print_log():
    print("Hello World")

print_log() # 函数调用
# 定义函数不会执行函数体代码,但是会检测函数体语法

x = 111
def foo(): # 函数名字不加括号,则打印函数的内存地址(函数名即可获取函数的地址)
    print(x)
    print("function foo()")

# 方式2 :有参函数的定义
x = 3
y = 7
def operation(x,y):
    print("x + y = ",x+y)
    print("x - y = ",x-y)
    print("x * y = ",x*y)
    print("x / y = ",x/y)

operation() # 调用函数

# 方式3:空函数,函数体代码为pass
def func(x,y):
    pass

1.4 函数调用三种形式及其应用场景

1
2
3
4
5
6
7
# 方式1 直接调用函数,只加括号进行调用函数
func()

# 方式2 表达式形式(数学表达式)
res = operation(x,y)

# 方式3 函数调用可以当做参数(函数内嵌套其它函数)

1.5 函数返回值三种形式及其应用场景

  • return 是函数结束的标志
    • 即函数体代码一旦运行到return就会立即终止函数的执行。并且会将return后的值当作本次运行的结果返回
# 返回None:函数体内没有return
def func01():
    pass # 函数返回为None
    return None # 也是会直接返回值None

# 函数返回一个值:return 值
def func02():
    return 10

# 返回多个值:用逗号分隔开多个值,会被return返回成元组
def func03():
    return 10,'aaa',[1,2] # 返回后的值格式为元组

2. 参数相关概念和分类

2.1 形参和实参

  • 形参:在定义函数阶段定义的参数称为形式参数。简称形参,相当于变量名。
  • 实参:在调用函数阶段传入的值称为实际参数。简称实参数,相当于变量值。

形参与实参之间的关系:

  • 在调用阶段,实参(变量值)会绑定给形参(变量名)
  • 绑定关系只能在函数体内使用。
  • 绑定关系在函数调用时生效,函数调用结束后接触绑定关系。

实参的形式:

# 方式1
func(1,2)

# 方式2
a = 1
b = 2
func(a,b)

# 方式3
func(int('1'),2)
func(func1(1,2,3),func2(1,2,3),4)

2.2 位置参数

  • 位置参数:按从左往右的顺序依次定义的参数称为 位置参数

  • 位置形参

    • 按照从左往右的顺序直接定义的“变量名”
    • 特点:必须被传值,不可多不可少
    def func(x,y):
        print(x,y)
    
  • 位置实参

    • 按照从左到右的顺序依次传入的值
    • 特点:按照顺序与形参 一一对应
    func(y = 3,x = 1) # 调用函数
    

2.3 关键字参数

  • 在函数调用阶段,按照 key = value 的形式传入的值
  • 特点:给指定形参传值,可以完全不按照顺序进行传值。

    1
    2
    3
    4
    5
    6
    7
    8
    def func(x,y):
        print(x,y)
    
    func(x = 2,y = 3) 
    
    func(1, y = 4)
    func(y=2,1) # 会直接报错
    func(1,y = 2, x = 4) # 会直接报错,不可以同时给同一个形参传值
    
  • 如果是混合使用,位置实参必须放在关键字实参之前。

  • 不能同时给同一个形参重复传值。

2.4 默认形参

  • 在定义函数阶段,就已经被赋值的形参,称之为 默认参数

  • 特点:在定义阶段就已经赋值,意味着在调用阶段可以不进行赋值操作。

    1
    2
    3
    4
    def func(x,y = 3):
        print(x,y)
    
    func(x = 1,y = 4) # 如果要传值,则以传值为主
    
  • 如果位置形参和默认形参混合使用,位置形参 必须在 默认形参左边

  • 默认参数的值是在函数定义阶段被赋值的。

2.5 可变长参数(*** 的用法)

  • 可变长度指的是在调用函数时,传入的值(实参)个数不固定

  • 实参是用来为形参赋值的,所以对应性针对溢出的实参必须有对应的形参来接收。

  • 可变长度的位置参数

    # 使用形式:*形参名:用来接收溢出的位置实参
    # 溢出的位置实参会被 * 保存成元组的格式,然后赋值紧跟其后的形参。
    # * 后面可以使用任意名字,但是规范上来说,一般是使用 args。格式为:*args
    def func(x,y,*z):
        print(x,y,z)
    
    func(1,2,3,4,5,6) # x = 1,y = 2,z=(3,4,5,6)
    
    # 对 *内的值进行传值
    def mysum(*args): # 参数规范
        res = 0
        for i in x:
            re += i
        return res
    
    res = mysum(1,2,3,4,5)
    print(res)
    
  • 可变长度的关键字参数

    1
    2
    3
    4
    5
    6
    7
    8
    # **形参名:用来接收溢出的关键字实参数。** 会将溢出的关键字实参保存成字典格式,然后赋值给紧跟其后的形参
    
    # ** 后可以紧跟任意名字,但规范一般是使用kwagrs,格式:**kwargs
    
    def func(x,y,**kwargs):
        print(x,y,kwargs)
    
    func(1,y = 2,a = 1,b = 3,c = 4)
    
  • 如果 *实参中,实参中带 *,会先将 * 后的值拆分成位置实参

    1
    2
    3
    4
    def func(x,y,z):
        print(x,y,z)
    
    func(*[1,2,3])
    
  • 如果是形参和实参都包含 *,会直接将实参的值拆分,然后再进行赋值操作。

    1
    2
    3
    4
    def func(x,y,*args): # args = (3,4,5,6) 存储成元组的形式
        print(x,y,args)
    
    func(1,2,*[3,4,5,6]) # 会拆分值
    
  • ** 可以用在实参中(** 后只能使用字典格式),在实参中带 **,先将 ** 后的值得拆分成为位置实参

    1
    2
    3
    4
    def func(x,y,z):
        print(x,y,z)
    
    func(**{'x':1,'y':2,'z':3}) # func(x = 1,y = 2,z = 3)
    
  • 形参实参中都包含 **

    1
    2
    3
    4
    def func(x,y,**kwargs):
        print(x,y,z)
    
    func(**{'x':1,'y':2,'a':3,'b':4}) # func(x = 1,y = 2,z = 3)
    
  • 如果是 *** 混合使用

    • *args 必须在 **kwargs 之前
    1
    2
    3
    def func(*args,**kwargs): # 后续传什么值都可以,也为后续参数的扩展留有余地
        print(args)
        print(kwargs)
    
  • 命名关键字参数(了解)

    • 在定义函数时, * 后定义的参数,称为命名关键字参数。
    1
    2
    3
    4
    5
    def func(x,y,*args,a,b): # 使用 * 和 *args 是一样的,a和b是命名关键字参数,所以传值时使用关键字方式传
        print(x,y)
        print(a,b) 
    # 主要特点:命名关键字实参必须按照 key=value 的形式为其传值。
    # 
    

3. 生命周期

  • 变量在程序中的生存周期(也就是所谓的存活周期)。

3.1 名称空间

名称空间:存放变量名字的地方。

  • 内置名称空间

    • 存放的名字:存放Python解释器内置的名字

    • 存活周期:Python解释器启动则产生,Python解释器关闭则销毁

  • 全局名称空间

    • 存放的名字:运行顶级代码所产生的名字,不是函数内定义、也不是内置,剩下的全局名字

    • 存活周期:Python文件执行则产生,python文件运行完毕后销毁。

  • 局部名称空间

    • 存放的名字:在调用函数时,运行函数代码过程中产生的函数内的名字/

    • 存活周期:函数调用时生效,函数调用完毕后则销毁。

3.2 变量作用域

  • 限制变量的访问权限。

变量的作用域决定了程序可以访问某个特定的变量名称。常见的两种变量作用域:

  • 全局变量

    • 内置名称看见、全局名称空间。
    • 程序内有效,可以被所有的函数共享。
  • 局部变量

    • 临时有效
    • 局部有效:函数内有效。
      • global
      • local

具体的实例分析:

1
2
3
4
5
6
7
# 如果在局部想要修改全局的名字对应的值(不可变类型),需要使用global
def func():
    global x # 可以通过global将全局变量的值,在函数内将其修改为局部变量的值
    x = 22

func()
print(x) #  打印x

nonlocal(了解):修改函数外层函数包含的名字对应的值(不可变类型)

x = 0
def f1():
    x = 11
    def f2():
        nonlocal x
        x = 22
    f2()
    print("f1 内的x:",x)

f1()

4. 函数对象

# func 就是内存地址
def func():
    print("from func")

# 可以当函数的参数传给另一个函数
def foo(x):
    x()
    #print(x)
foo(func) # 传func的内存地址

# 可以当作函数另外一个函数的返回值

# 可以当作容器类型的一个元素


# 具体实例
def login():
    print("登陆成功")

def transfer():
    print("转账成功")

def check_balance():
    print("查看余额")

def register():
    print("注册成功")

# 使用一个字典来存储函数对象,使得代码的可扩展性很强
func_dict = {
    '1':login,
    '2':transfer,
    '3':check_balance,
    '4':register
}

# 优化后的字典
func_dict = {
    '0':['退出',None],
    '1':['登陆',login],
    '2':['转账',transfer],
    '3':['查询余额',check_balance],
    '4':['注册',register]
}
while True:
    # 直接优化下面的代码,使用for循环来进行替代
    print("""
        0 退出
        1 登陆
        2 转账
        3 查看余额
        4 注册
    """)
    # 优化后的代码为
    for k in func_dict:
        print(k,func_dict[k][0]) #选择
    choice = input("请输入操作编号:").strip()
    if not choice.isdigit():
        print("请输入数字编号")
        continue

    if choice == '0':
        break

    if choice in func_dict:
        func_dict[choice]()

5. 参考资料

  • Python官方文档
  • W3CSchool Python教程
  • 路飞学城Python开发视频课:B站