Python函数(一)之杵臼之交
Python函数
函数的作用:对功能进行封装,减少重复代码,方便维护,流程清晰明了,易于理解。
函数的结构:
def 函数名():
函数体
return语句
函数的返回值:
- 可以用于终止函数的运行,也可以返回一个值给调用者。
- 如果函数不写返回值默认返回一个None。
- 函数return可以返回任意数据类型。
- 如果return多个值则返回的数据类型是元组。
- 如果return一个数据,返回值是原数据类型。.
return返回一个值,如下:
def fun(): return 1 # return 数字1 ret = fun() print(type(ret),ret) 打印内容如下: <class 'int'> 1
return返回多个值,如下:
def fun(): return 1,2,3,4 # return多个值 ret = fun() print(type(ret),ret) # 打印内容如下: <class 'tuple'> (1, 2, 3, 4)
函数的参数:
形参:分为位置参数、默认参数、混合参数三种。
实参:分为位置参数、关键字参数、混合参数三种。
传参:指从实参到形参的传递过程。.
参数的顺序: 位置参数 > 默认参数(关键字参数),在默认参数前边写位置参数。
位置传参。如下:
def max_num(num1,num2): # 返回最大的数字 if num1 > num2: return num1 else: return num2 ret = max_num(10,20) print(ret) 打印内容如下: 20
默认参数,就是如果我们不传参数,函数执行形参中的默认参数。如下:
def max_num(num1=0,num2=20): # 默认参数 if num1 > num2: return num1 else: return num2 ret = max_num(num1=10) # 我们只传递num1参数 print(f'最大数是:{ret}')
# 打印内容如下 最大数是:20
混合传参:
# 参数中有位置参数和默认参数的称为混合参数 # 注意:位置参数必须放在默认参数前 def max_num(num1,num2=20): # 混合传参 if num1 > num2: return num1 else: return num2 ret = max_num(10) print(f'最大数是:{ret}')
# 打印内容如下 最大数是:20
关于默认参数需要注意:
def fun(val, list_1=[]): # 默认参数是可变的数据类型列表 list_1.append(val) return list_1 list1 = fun(10) list2 = fun('Hello World',[]) # 给默认参数传递一个空列表 list3 = fun('a') list4 = fun('b') print("list1 = %s" % list1) print("list2 = %s" % list2) print("list3 = %s" % list3) print("list4 = %s" % list4) # 打印内容如下 list1 = [10, 'a', 'b'] list2 = ['Hello World'] list3 = [10, 'a', 'b'] list4 = [10, 'a', 'b']
通过打印结果可以发现list1、list3、list4的结果是一样的。它们还有个共同点就是在传参的过程中没有填写默认参数,list2的默认参数因为传递了个空列表,所以结果和其它的不一样。
由此可以知道当默认参数是可变的数据类型时,如果不传递默认参数而是直接使用默认的参数时,虽然多次调用了函数,但是函数的默认参数都是引用同一块的内存地址(应该是为了节省内存),当给默认参数传递一个新值时,函数便开辟了一个新的空间给默认参数使用,所以最终list2和其它的值不一样。
函数动态参数
动态参数分为两种:动态位置参数、动态默认参数。
动态位置参数:动态位置参数会接收所有位置参数,所以要想既使用位置参数又使用动态位置参数,一定要将位置参数放在动态位置参数前面。动态位置参数用return返回的是一个元组,动态位置参数约定俗成使用*args。
在定义形参如:def fun1(*args) 我们将*args定义形参的过程称作聚合,也就是将实参中的多个参数进行聚合。
函数传参如:fun1("Hello",123,"World") 我们将传递多个参数的过程称为打散。
下面是错误的示范:
def fun1(*args,buf): # 动态位置参数在位置参数前,错误 print(buf) print(args) return args fun1("Hello",123,"World") # 打印内容如下: TypeError: fun1() missing 1 required keyword-only argument: 'buf'
原因在于动态位置参数会接收所有的参数,导致后面的位置参数无法接收参数导致报错。
下面是正确的写法:
def fun1(buf,*args): #位置参数在动态位置参数前面 print(buf) print(args) return args ret = fun1("Hello",123,"World") print(type(ret)) # 打印内容如下; Hello (123, 'World') <class 'tuple'> # 动态位置参数的返回类型
但是一般如果用动态位置参数,就很少用位置参数了,上面这种情况一般也只是在特殊情况下会用到。从上面的结果可以得出结论:位置参数必须在动态位置参数前。
动态默认参数:会接收所有默认参数,如果形参中有默认参数,默认参数必须放在动态默认参数前面,动态默认参数用return返回一个字典。动态默认参数约定俗成的使用**kwargs。
def fun1(s2="1",**kwargs): # 默认参数必须放在动态默认参数前面 print(s2) print(kwargs) return kwargs ret = fun1(s="Hello",num=123,s2="World") print(type(ret)) #打印内容如下: World {'s': 'Hello', 'num': 123} <class 'dict'> #动态默认参数返回的是一个字典 def fun(*args,**kwargs): # 万能传参 print(args,kwargs) fun([1,2,3],(3,2,3),**{"电视剧":1,"电影":2}) # 字典需要打散传递给动态默认参数,如果不打散会被动态位置参数给接收了 # 打印内容太如下: ([1, 2, 3], (3, 2, 3)) {'电视剧': 1, '电影': 2}
关于def fun(*args,**kwargs)这是俗称的万能参数,可以接收任意数量的参数。我们将接收参数的过程称为聚合,而聚合的关键点在于*和**。*和**主要是将从实参传过来的多个参数进行聚合。*args将所有位置参数聚合成元组,而**kwargs将所有默认(关键字)参数聚合成字典。还有个需要注意的地方是在传参的过程如func(1,2,3,**{键:值}),在关键字参数字典中的键必须是字符串否则会报错。
总结:
形参的位置顺序:
位置参数 ==>动态位置参数==>默认参数==>动态默认参数
实参的位置顺序:
位置参数 ==>动态位置参数==>关键字参数==>动态关键字参数
名称空间
在python解释器开始执行之后,就会在内存中开辟一个空间,每当遇到一个变量的时候,就把变量名和值之间的关系记录下来,当遇到函数定义的时候,解释器只是把函数名读入内存,表示这个函数存在了,至于函数内部的变量和逻辑,解释器是不关心的。函数只是加载进来,只有当函数被调用和访问的时候,解释器才会根据函数内部声明的变量来进行开辟变量的内部空间,随着函数执行完毕,这些函数内部变量占用的空间也会随着函数执行完毕而被清空。
命名空间分类:
- 内置命名空间:存放python解释器为我们提供的名字、list、tuple、str、int这些都是内置命名空间。
- 全局命名空间:我们直接在py文件中,函数外声明的变量都属于全局命名空间。
- 局部命名空间:在函数中声明的变量会放在局部命名空间。
加载顺序:
- 内置命名空间
- 全局命名空间
- 局部命名空间(函数被执行的时候)
取值顺序:
- 局部命名空间
- 全局命名空间
- 内置命名空间
作用域:作用域就是作用范围, 按照生效范围来看分为全局作用域和局部作用域。
全局作用域:包含内置命名空间和全局命名空间。在整个文件的任何位置都可以使用(遵循 从上到下逐步执行)。
局部作用域:在函数内部可以使用。
作用域命名空间:
- 全局作用域:全局命名空间 + 内置命名空间。
- 局部作用域:局部命名空间。
可以使用globals()函数来查看全局作用域中的内容:
num = 10 print(globals()) # 打印全局作用域的内容 # 打印部分内容如下: '__cached__': None, 'num': 10}
使用locals()函数来查看局部变量:
def fun1(): num = 10 print(locals()) # 打印局部作用域中的内容 fun1() # 打印内容如下: {'num': 10}
gloabal和nonlocal
gloabal:用于在局部空间内修改全局变量,如果全局变量不存在将创建一个全局变量。只有数字和字符串需要global,list,dic,set不需要global。
# 全局变量 num = 20 list_1 = [1,2,3] def fun1(): global num # 声明全局变量 num = 10 # 修改全局变量,如果不用global声明在函数内部是不能修改全局变量的 global new_num # new_num不存在将创建成全局变量 new_num = 200 # 给全局变量赋值 list_1.append(10) # 可变数据类型不需要使用global fun1() # 调用函数 print(f'num={num}') print(f'new_num={new_num}') print(f'list_1={list_1}') # 打印内容如下 num=10 new_num=200 list_1=[1, 2, 3, 10]
注意:在函数内创建全局变量时必须要调用这个函数才能创建全局变量,否则不能创建全局变量。
nonlocal:必须在嵌套函数内,修改离它最近的那一层的局部变量,如果上一级不存在,继续向上一层找,一直到函数的最外层停止查找,找不到会报错。
nonlocal特点:
1、不能修改全局变量。
2、在局部作用域中,对父级作用域(或者更外层作用域非全局作用域)的变量进行引用和修改,并且引用的哪层,从那层及以下此变量全部发生改变。
def fun1(): num = 10 # 局部变量 def fun2(): nonlocal num 向上一层查找局部变量num num = 200 #修改局部变量num fun2() print(num) # 打印局部变量num fun1() # 调用函数打印num最终结果 #打印内容如下: 200 # num被修改成了200
下一篇:函数名,闭包,迭代器:https://www.cnblogs.com/caesar-id/p/10311931.html