目录

Python基础语法(15)函数提高

Python基础语法(15)函数提高

学习目标

  • 变量的作用域
  • 多函数程序执行流程
  • 函数多返回值
  • 函数的参数写法
  • 拆包和交换两个变量的值
  • 引用
  • (复习)可变和不可变类型

变量的作用域

变量的作用域指的是变量的生效的范围,主要分为两类:局部变量全局变量

  • 局部变量:指的是定义在函数体内部的变量,即只在函数体内部生效。在函数体外部访问变量是报错的。
1
2
3
def test():
  a = 1
print(a)	# 报错,函数内的变量为局部变量,函数外无法访问

作用:局部变量用于在函数体内部临时保存数据,当函数调用完成则销毁局部变量

  • 全局变量:指的是在函数体内外都能生效的变量。
1
2
3
4
5
6
7
a = 1
def testA():
  print(a)
def testB():
  print(a)
testA()
testB()	# 不报错,因为a为全局变量,函数体内外都能访问。

全局变量要想在函数体内发生修改要加global关键字。

错误的修改:

1
2
3
4
5
6
7
8
9
a = 1
def test():
    a = 2		# 只是定义了一个局部变量
    print(a)
def test2():
    print(a)
test()      # 此时访问的是定义的局部变量值为2
test2()     # 此时访问的全局变量1
print(a)    # 此时访问的全局变量1

正确的修改:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
a = 1
def test():
    global a
    a = 2   # 将全局变量修改为2
    print(a)
def test2():
    print(a)
test()      # 此时访问的是修改后的全局变量为2
test2()     # 此时访问的也是修改后的全局变量2
print(a)    # 此时访问的是修改后的全局变量2

多函数执行流程

一般在实际开发过程中,一个程序往往由多个函数组成,并且多个函数共享某个数据。

  • 共用全局变量
1
2
3
4
5
6
7
8
9
glo_a = 100
def test1():
    global glo_a
    glo_a = 200
    print(glo_a)
def test2():
    print(glo_a)
test1()	# 打印修改后的全局变量200
test2()	# 也是修改后的全局变量被打印200
  • 返回值作为参数传递
1
2
3
4
5
6
7
def test1():
    return 99
def printer(num):
    print(num)

result = test1()
printer(result)	# 一个函数的返回值可以作为另一个函数的参数传入

函数多返回值

在一个函数有多个返回值的情况下,只返回第一个返回值,第一个return以下的代码均不执行,也意味着return只要返回值就会终止当前函数

1
2
3
4
def test():
    return 1
    return 2
print(test())   # 打印为1 第二个return不执行

如果需要一个函数返回多个值(默认以元组保存),需要这样写:

1
2
3
def test():
    return 1,2
print(test())   # 打印为(1,2) 第二个return不执行

如果需要一个函数返回一个数据序列,例如返回一个列表,可以这样写:

1
2
3
def test():
    return [1,2]
print(test())   # 打印为[1,2] 第二个return不执行

函数的参数写法

  • 位置参数:调用时根据函数定义的参数顺序位置对应个数来传递参数。
1
2
3
4
5
def user_info(name,age,gender):
    print({'name':name,'age':age,'gender':gender})
user_info('小明',21,'male')	# 传入个数和顺序一致,正确输出
user_info(1)	# 传入个数不一致,报错
user_info(21,'小明','male')	# 传入顺序不一致,意义错误
  • 关键字参数:调用时通过"键=值"的形式指定,让函数更加清晰容易使用,同时也清除了参数的顺序需求,若参数里面有位置参数,必须让位置参数在关键字参数前面
1
2
3
4
def user_info(name,age,gender):
    print({'name':name,'age':age,'gender':gender})
user_info('小明',gender='male',age=21)	# 顺序错误也能正常打印,意义不错误。
user_info(gender='male',age=21,'小明')	# 这样写报错,位置参数‘小明’在关键字参数后面
  • 缺省参数:也叫默认参数,在定义函数时为参数提供默认值,调用函数时可以不传该默认参数的值。在定义和调用时若有位置参数必须让其在默认参数前。
1
2
3
4
5
6
def user_info(name,age,gender='male'):
    # def user_info(name,gender='male',age): 这样写报错,因为位置参数age在关键字参数后面
    print({'name':name,'age':age,'gender':gender})
user_info('小明',22)  # 未传入默认参数时使用默认参数
user_info('小明',gender='female',age=21)  # 也可以传入默认参数使其修改
user_info('小明',gender='female',21)  # 这样写报错参数21为位置参数必须在gender默认参数前面
  • 不定长参数:在不确定调用时会传入多少个数据的时候(不传参数也可以)的场景,在定义和调用时可以用packing包裹位置参数,或者包裹关键字参数来进行参数传递,会比较方便。

    • 包裹位置传递
    1
    2
    3
    4
    5
    6
    7
    
    def user_info(*args):
        print(args)
      
    user_info(1) # 打印结果默认元组(1,)
    user_info('小明',21)  # ('小明', 21)
    user_info() # 不传参数也可以
    # user_info(name = 'a')   # 报错,位置参数传入不允许使用关键字参数
    

    ⚠️注意:传入的参数都会被args变量收集,会根据传入参数的位置合并为一个元组,即args时元组类型,这就叫包裹位置传递

    • 包裹关键字传递
    1
    2
    3
    4
    5
    6
    
    def user_info(**kwargs):   # python底层都用此名字
        print(kwargs)
    user_info(age = 123, name = 'ab')   # 返回两个元素字典
    user_info() # 返回空字典
    user_info(age = 12) # 返回单个元素字典
    # user_info(12)   # 报错,关键字参数传入不能使用位置参数
    

    ⚠️注意:传入的参数被kwargs变量收集,会根据传入参数的位置合并为一个字典,即kwargs为字典类型,这个过程叫包裹关键字传递,也叫组包


拆包和交换变量的值

拆包

  • 元组拆包
1
2
3
4
5
def return_num():
  return 100,200
print(return_num()) # 打印结果为组合的元组
a, b = return_num()
print(a,b)  # 打印结果分别为两个返回值
  • 字典拆包
1
2
3
4
dict1 = {'name':'小明', 'age':23}
a,b = dict1
print(a,b)  # name, age,字典拆包得到的是key值
print(dict1[a],dict1[b]) # 小明, 23   # 取字典中的value值

交换变量的值

  • 方法一:借助第三个变量存储数据
1
2
3
4
5
6
a = 100
b = 200
c = a # 将a的数据存储到c
a = b # b的数据交换到a
b = c # c的数据存储到b,实现a和b数据的交换
print(a,b)  # 200,100
  • 方法二:直接两个变量交换赋值(通常采用此方法)
1
2
3
a,b = 100,200
a,b = b,a
print(a,b)

引用

什么是引用?

在python中,数据(值)是靠引用来传递的。我们可以用id()来判断两个变量是否为同一个数据(值)的引用,我们可以将id值理解为内存地址标识

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
a = 1 # 内存中开辟一道空间,存储1这个值,a指向这个内存空间,即a是内存空间的命名,即对内存地址进行命名
b = a
print(b)    # 1
print(id(a))    # 140180812077360
print(id(b))    # 140180812077360(十进制地址一样)
a = 2   # 开辟另外一道空间存储2这个值
print(b)    # 1 说明int类型为不可变类型,不可变指的是int类型开辟的内存空间地址上存储的值与地址一一对应不会变化
print(id(a))    # 140474446911824 此时变为数据2的十进制地址
a = 1
print(id(a) == id(b))    # True 此时还原为数据1的十进制地址

int数据类型之所以为不可变数据类型是因为:int开辟的内存空间地址上的值不可以变化,一个值对应一个地址。

1
2
3
4
5
6
7
8
list1 = [1,2,3]
list2 = list1
print(id(list1),id(list2))  # 140391521099840 列表也是靠引用进行来传值的
print(list1,list2)  # [1, 2, 3] 两个变量名指向的十进制地址(引用)相同

list1.append(4) # 改变值后看一下情况
print(list1,list2)  # 此时list2也跟着变了,说明列表为可变类型
print(id(list1),id(list2)) # 140391521099840 内存地址不变,意味着在同一个地址(引用)上的值可以变化

list数据类型之所以为可变数据类型是因为:list开辟的内存空间地址上的值是可以变化的,例如在原列表中增加一个元素,但地址不会变。

引用当实参

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
def test1(a):
    print(a)    # 100   [1, 2]
    print(id(a))    # 140306725275088  140640233624832
    a += a
    print(a)    # 200   [1, 2, 1, 2]
    print(id(a))    # 140306725278352 值改变,则引用改变; 140640233624832  列表为可变类型,值改变引用不变

b = 100
test1(b)    # 将变量b传入函数,能打印100则说明引用可以当作实参传入函数
print(id(b))    # 140306725275088;打印100数值对应的引用

c = [1,2]
test1(c)
print(id(c))  # 140640233624832;打印[1,2]和[1,2,1,2]数值对应的引用

可变和不可变类型

所谓可变和不可变数据类型指的是:数据能够在引用上进行修改,如果能直接修改就是可变,否则是不可变。

  • 可变类型
    • 列表
    • 字典
    • 集合
  • 不可类型
    • 整型
    • 布尔型
    • 浮点型
    • 字符串
    • 元组

总结

  • 函数变量的作用域:

    • 全局变量指的是在函数体内外访问都能生效的;
    • 局部变量指的是仅当前函数体内生效。
  • 多函数执行流程:

    • 多函数共享一个全局变量时,只要先使用该变量进行gloabl声明并修改,则其他函数后使用此共享变量时则同时取改变后的值
    • 多函数执行时可以使用一个函数的返回值作为另一个函数调用时的参数进行传值。
  • 函数的返回值:

    • 函数返回语句return执行后有终止函数的作用,即只会返回第一个return语句的返回值,后面的任何操作均不执行。
    • 函数返回多个值时写法应该是return a, b,即用逗号隔开两个需要返回的值,并默认使用元组保存。
    • 函数可以返回一个数据序列,比如列表,字符串,字典,元组或者集合。
  • 函数的参数写法:

    • 位置参数:形参和实参的书写顺序和个数必须一致
    • 关键字参数:写法是在调用时使用key=value写法,形参和实参书写顺序可以不一致但个数一定要一致,且位置参数必须在关键字参数前面。
    • 缺省参数:写法是在定义时使用key=value写法,形参和实参书写顺序和个数可以不一致,但调用时只允许省略缺省参数,且位置参数也必须在缺省参数前。
    • 不定长参数:写法是在定义时使用*args这样一个*号接一个变量名的写法,形参和实参书写顺序和个数可以不一致,也可以不传参数调用时不能使用关键字参数,收集的参数以元组保存。
    • 不定长关键字参数:写法是在定义时使用**kwargs这样两个*号接一个变量名的写法(建议直接使用kwargs),形参和实参的书写顺序和个数可以不一致,也可以不传参数调用时必须使用关键字参数,收集的参数以字典保存。
  • 拆包和交换变量的值:

    • 元组拆包可以使用a,b = (1,2)这样两个变量接收拆包;
    • 字典拆包可以使用a,b = {'name':'小明', age:21}这样两个变量先获取key值然后在根据key值获取value值(将字典先保存一个变量)。
  • 引用:在Python中数据的传递通过引用进行,a = 1即为int数据类型在内存中开辟一个空间,这个空间的十进制地址为数据1的引用,a即为此引用的代名词。可以使用id()公共方法来获取数据(值)的十进制地址。

  • 可变类型指的是该数据(值)在其引用上能发生改变,不可变则指其数据(值)在引用上不可以发生改变。