函数是执行特定任务的一段代码,程序通过将一段代码定义成函数,并为该函数指定一个函数名,这样即可在需要的时候多次调用这段代码。因此,函数是代码复用的重要手段。
学习函数需要重点掌握定义函数、调用函数的方法。此外,本章也会介绍大量有关 Python 的高级内容,读者应该紧跟本章的讲解要点。
与函数紧密相关的另一个知识点是 lambda 表达式。lambda 表达式可作为表达式、函数参数或函数返回值,因此使用 lambda 表达式可以让程序更加简洁。
函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段。
函数能提高应用的模块性,和代码的重复利用率。你已经知道Python提供了许多内建函数,比如print()。但你也可以自己创建函数,这被叫做用户自定义函数。
定义一个函数 你可以定义一个由自己想要功能的函数,以下是简单的规则: 函数代码块以 def 关键词开头,后接函数标识符名称和圆括号 ()。 任何传入参数和自变量必须放在圆括号中间,圆括号之间可以用于定义参数。 函数的第一行语句可以选择性地使用文档字符串—用于存放函数说明。 函数内容以冒号起始,并且缩进。 return [表达式] 结束函数,选择性地返回一个值给调用方。不带表达式的return相当于返回 None。
匿名函数 python 使用 lambda 来创建匿名函数。 所谓匿名,意即不再使用 def 语句这样标准的形式定义一个函数。 lambda 只是一个表达式,函数体比 def 简单很多。 lambda的主体是一个表达式,而不是一个代码块。仅仅能在lambda表达式中封装有限的逻辑进去。 lambda 函数拥有自己的命名空间,且不能访问自己参数列表之外或全局命名空间里的参数。 虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,后者的目的是调用小函数时不占用栈内存从而增加运行效率。
Python函数(函数定义、函数调用)用法详解
从函数定义者(实现函数的人)的角度来看,其至少需要想清楚以下 3 点:
- 函数需要几个关键的需要动态变化的数据,这些数据应该被定义成函数的参数。
- 函数需要传出几个重要的数据(就是调用该函数的人希望得到的数据),这些数据应该被定义成返回值。
- 函数的内部实现过程。
Python函数的定义
定义函数,也就是创建一个函数,可以理解为创建一个具有某些用途的工具。定义函数需要用 def 关键字实现,具体的语法格式如下:
def 函数名(形参列表): //由零条到多条可执行语句组成的代码块 [return [返回值]]
其中,用 [] 括起来的为可选择部分,即可以使用,也可以省略。
此格式中,各部分参数的含义如下:
- 函数名:从语法角度来看,函数名只要是一个合法的标识符即可;从程序的可读性角度来看,函数名应该由一个或多个有意义的单词连缀而成,每个单词的字母全部小写,单词与单词之间使用下画线分隔。
- 形参列表:用于定义该函数可以接收的参数。形参列表由多个形参名组成,多个形参名之间以英文逗号(,)隔开。一旦在定义函数时指定了形参列表,调用该函数时就必须传入相应的参数值,也就是说,谁调用函数谁负责为形参赋值。
注意,在创建函数时,即使函数不需要参数,也必须保留一对空的“()”,否则 Python 解释器将提示“invaild syntax”错误。另外,如果想定义一个没有任何功能的空函数,可以使用 pass 语句作为占位符。
Python函数的调用
调用函数也就是执行函数。如果把创建的函数理解为一个具有某种用途的工具,那么调用函数就相当于使用该工具。
函数调用的基本语法格式如下所示:
函数名([形参值])
其中,函数名即指的是要调用的函数的名称;形参值指的是当初创建函数时要求传入的各个形参的值。需要注意的是,创建函数有多少个形参,那么调用时就需要传入多少个值,且顺序必须和创建函数时一致。即便该函数没有参数,函数名后的小括号也不能省略。
为函数提供说明文档
前面介绍过可以使用 Python 内置的 help() 函数查看其他函数的帮助文档,我们也经常通过 help() 函数查看指定函数的帮助信息,这对于 Python 开发者来说非常重要。
我们还可以为函数编写说明文档,只要把一段字符串放在函数声明之后、函数体之前,这段字符串将被作为函数的部分,这个文档就是函数的说明文档。
程序既可通过 help() 函数查看函数的说明文档,也可通过函数的 __doc__ 属性访问函数的说明文档。下面程序示范了为函数编写说明文档:
[root@kube function]# cat demo.py #coding:utf-8 """ first function definition test x * y function good job """ def my_max(x,y): #x ,y 在这里作为函数的形参 """ #在函数中定义的注释,作为函数的帮助说明文档 first function definition test x * y function good job """ a = x if x > y else y return a #return 作为函数的返回值 def say_hi(name): print('say_hi function begin test') return name + " hello" a = 10 b = 11 result = my_max(a,b) print("result: ",result) print(say_hi('tom_jerry')) print('---------------------------------- ') print(help(my_max)) [root@kube function]# py demo.py result: 11 say_hi function begin test tom_jerry hello ---------------------------------- Help on function my_max in module __main__: my_max(x, y) first function definition test x * y function good job [3]+ Stopped py demo.py [root@kube function]#
Python函数值传递和引用传递(包括形式参数和实际参数的区别)
通常情况下,定义函数时都会选择有参数的函数形式,函数参数的作用是传递数据给函数,令其对接收的数据做具体的操作处理。
在使用函数时,经常会用到形式参数(简称“形参”)和实际参数(简称“实参”),二者都叫参数,之间的区别是:
[root@kube function]# py demo1.py 24 [root@kube function]# cat demo1.py #coding:utf-8 def len_count(x): #定义函数时这里的 x 就是形参, return len(x) s_string = 'restestescvfff334454ffff' print(len_count(s_string)) #执行函数时,这里实际传入的参数s_string 就是实参 [root@kube function]# py demo1.py 24 [root@kube function]#
Python 中,根据实际参数的类型不同,函数参数的传递方式可分为 2 种,分别为值传递和引用(地址)传递:
- 值传递:适用于实参类型为不可变类型(字符串、数字、元组);
- 引用(地址)传递:适用于实参类型为可变类型(列表,字典);
- 值传递和引用传递的区别是,函数参数进行值传递后,若形参的值发生改变,不会影响实参的值;而函数参数继续引用传递后,改变形参的值,实参的值也会一同改变。
例如,定义一个名为 demo 的函数,分别为传入一个字符串类型的变量(代表值传递)和列表类型的变量(代表引用传递):
[root@kube function]# cat demo2.py #coding:utf-8 def demo(obj): obj += obj print("xing can:", obj) print('------value transfer------') a = 1 print(a) print(demo(a)) print(a) print('-----reference transfer------') b = [1,2,3] print(b) print(demo(b)) print(b) [root@kube function]# py demo2.py ------value transfer------ 1 #可以看到在值传递时改变了形参的值,实际的参数值没有被修改 xing can: 2 None 1 -----reference transfer------ [1, 2, 3] #引用传递看到改了形参的值,那么实际参数的值也被改变了 xing can: [1, 2, 3, 1, 2, 3] None [1, 2, 3, 1, 2, 3] [root@kube function]#
Python函数参数的值传递机制
所谓值传递,实际上就是将实际参数值的副本(复制品)传入函数,而参数本身不会受到任何影响。
Python函数参数的引用传递
如果实际参数的数据类型是可变对象(列表、字典),则函数参数的传递方式将采用引用传递方式。需要注意的是,引用传递方式的底层实现,采用的依然还是值传递的方式。
什么是位置参数,Python位置参数
位置参数,有时也称必备参数,指的是必须按照正确的顺序将实际参数传到函数中,换句话说,调用函数时传入实际参数的数量和位置都必须和定义函数时保持一致。
实参和形参数量必须一致
在调用函数,指定的实际参数的数量,必须和形式参数的数量一致(传多传少都不行),否则 Python 解释器会抛出 TypeError 异常,并提示缺少必要的位置参数。
[root@kube function]# cat demo3.py #coding:utf-8 def cube(lang,width,height): return lang*width*height print('jisuan cube') print(cube(2,3)) print('--------------------------------- ') print(cube(2,3,4)) [root@kube function]# py demo3.py jisuan cube Traceback (most recent call last): File "demo3.py", line 6, in <module> print(cube(2,3)) TypeError: cube() missing 1 required positional argument: 'height' #参数未发现指定的参数 [root@kube function]#
Python函数关键字参数及用法
目前为止,我们使用函数时所用的参数都是位置参数,即传入函数的实际参数必须与形式参数的数量和位置对应。而本节将介绍的关键字参数,则可以避免牢记参数位置的麻烦,令函数的调用和参数传递更加灵活方便。
关键字参数是指使用形式参数的名字来确定输入的参数值。通过此方式指定函数实参时,不再需要与形参的位置完全一致,只要将参数名写正确即可。
因此,Python 函数的参数名应该具有更好的语义,这样程序可以立刻明确传入函数的每个参数的含义
[root@kube function]# cat demo3.py #coding:utf-8 def cube(lang,width,height): return lang*width*height print('jisuan cube') print(cube(lang=1,height=2,width=3.5)) print('--------------------------------- ') print(cube(2,3,height=4)) #print(cube(width=2,3,4)) #需要说明的是,如果希望在调用函数时混合使用关键字参数和位置参数,则关键字参数必须位于位置参数之后。换句话说,在关键字参数之后的只能是关键字参数。 #并且不能调换位置 是print(cube(2,3,lang=4) 会报错TypeError: cube() got multiple values for argument 'lang' ,为 lang 定义了多个值 [root@kube function]# py demo3.py jisuan cube 7.0 --------------------------------- 24 [root@kube function]#
Python函数默认参数设置(超级详细)
我们知道,在调用函数时,如果不指定某个参数,解释器会抛出异常。为了解决这个问题,Python 允许为参数设置默认值,即在定义函数时,直接给形式参数指定一个默认值,这样的话,即便调用函数时没有给拥有默认值的形参传递参数,该参数可以直接使用定义函数时设置的默认值。
定义带有默认值参数的函数,其语法格式如下: def 函数名(...,形参名=默认值): 代码块
注意,在使用此格式定义函数时,指定有默认值的形式参数必须在所有没默认值参数的最后,否则会产生语法错误。
再次强调,由于 Python 要求在调用函数时关键字参数必须位于位置参数的后面,因此在定义函数时指定了默认值的参数(关键字参数)必须在没有默认值的参数之后。
[root@kube function]# cat demo4.py #coding:utf-8 def msg(name='tom',message='hello word!!'): print('hi:',name,'neirong:',message) print(msg('abc','hahhaha')) print(msg(name='jerry')) print(msg(message='wo are fimaly')) #print(msg('big boom',mssage='test error')) #这行就是关键字参数在默认参数后面就会报错 [root@kube function]# py demo4.py hi: abc neirong: hahhaha None hi: jerry neirong: hello word!! None hi: tom neirong: wo are fimaly None [root@kube function]#
Python函数可变参数(*args,**kwargs)详解
很多编程语言都允许定义个数可变的参数,这样可以在调用函数时传入任意多个参数。Python 也不例外,在定义函数时也可以使用可变参数。
可变参数,又称不定长参数,即传入函数中的实际参数可以是任意多个。Python 定义可变参数,主要有以下 2 种形式。
1) 可变参数:形参前添加一个 '*'
此种形式的语法格式如下所示: *args 其中,args 表示创建一个名为 args 的空元组,该元组可接受任意多个外界传入的非关键字实参。
2) 可变参数:形参前添加两个'*'
这种形式的语法格式如下: **kwargs *kwargs 表示创建一个名为 kwargs 的空字典。该字典可以接收任意多个以关键字参数赋值的实际参数。
[root@kube function]# py demo5.py 1 2 4 ('tom', 'jerry', 'vn') {'score': 89, 'width': 90, 'height': 185} None [root@kube function]# cat demo5.py #coding:utf-8 def test(a,b,c=3,*name,**v_dict): print(a,b,c) print(name) print(v_dict) print(test(1,2,4,'tom','jerry','vn',score=89,width=90,height=185)) [root@kube function]# py demo5.py 1 2 4 ('tom', 'jerry', 'vn') {'score': 89, 'width': 90, 'height': 185} None [root@kube function]#
Python逆向参数收集详解
所谓逆向参数收集,指的是在程序己有列表、元组、字典等对象的前提下,把它们的元素“拆开”后传给函数的参数。逆向参数收集需要在传入的列表、元组参数之前添加一个星号,在字典参数之前添加两个星号。
[root@kube function]# cat demo6.py #coding:utf-8 def test(name,age): return print('you name: %s ,you age: %d' % (name,age)) a1 = ['tom',18] print(test(a1)) #函数把传入的列表当成一个参数进行处理,报错了 [root@kube function]# py demo6.py Traceback (most recent call last): File "demo6.py", line 6, in <module> print(test(a1)) TypeError: test() missing 1 required positional argument: 'age' [root@kube function]#
[root@kube function]# py demo6.py you name: tom ,you age: 18 None [root@kube function]# cat demo6.py #coding:utf-8 def test(name,age): return print('you name: %s ,you age: %d' % (name,age)) a1 = ['tom',18] print(test(*a1)) #在传入的列表或者元组前面加一个* 号,对列表进行逆向参数收集进行拆分就可以了,如果参数给多了也会报错提示TypeError: test() takes 2 positional arguments but 3 were given [root@kube function]# py demo6.py you name: tom ,you age: 18 None [root@kube function]#
[root@kube function]# cat demo6.py #coding:utf-8 def test(name,age): return print('you name: %s ,you age: %d' % (name,age)) a1 = {'name':'laowang','age':20} print(test(**a1)) #加上两个** 号对字典进行关键字拆分处理 [root@kube function]# py demo6.py you name: laowang ,you age: 20 None [root@kube function]#
Python None(空值)及用法
在 Python 中,有一个特殊的常量 None(N 必须大写)。和 False 不同,它不表示 0,也不表示空字符串,而表示没有值,也就是空值。
这里的空值并不代表空对象,即 None 和 []、“” 不同:
[root@kube function]# cat demo7.py #coding:utf-8 print(None is []) print(None is '') print(type(None)) [root@kube function]# py demo7.py False False <class 'NoneType'> [root@kube function]#
除此之外,None 常用于 assert、判断以及函数无返回值的情况。举个例子,在前面章节中我们一直使用 print() 函数输出数据,其实该函数的返回值就是 None。因为它的功能是在屏幕上显示文本,根本不需要返回任何值,所以 print() 就返回 None。
Python return函数返回值详解
目前为止,我们创建的函数都只是对传入的数据进行了处理,处理完了就结束。但实际上,在某些场景中,我们还需函数将处理的结果反馈回来,就好像主管向下级员工下达命令,让其去打印文件,员工打印好文件后并没有完成任务,还需要将文件交给主管。
Python中,用 def 语句创建函数时,可以用 return 语句指定应该返回的值,该返回值可以是任意类型。需要注意的是,return 语句在同一函数中可以出现多次,但只要有一个得到执行,就会直接结束函数的执行。
函数中,使用 return 语句的语法格式如下: return [返回值]
[root@kube function]# cat demo8.py #coding:utf-8 def test(a): if a > 0: return 'success' else: return 'failed' print(test(5)) print(test(0)) [root@kube function]# py demo8.py success failed [root@kube function]#
Python函数返回多个值的方法
如果程序需要有多个返回值,则既可将多个值包装成列表之后返回,也可直接返回多个值。如果 Python 函数直接返回多个值,Python 会自动将多个返回值封装成元组。
如下程序示范了函数直接返回多个值的情形:
[root@kube function]# cat demo9.py #coding:utf-8 def sum_and_avg(list): sum = 0 count = 0 for i in list: if isinstance(i,int) or isinstance(i,float): count += 1 #累加是数值的个数 sum += i #数值累加 return sum,sum / count my_lt = [20,15,30,4.6,'w','r',7.8,30] print(sum_and_avg(my_lt)) a ,b = sum_and_avg(my_lt) print(a,b) [root@kube function]# py demo9.py (107.39999999999999, 17.9) #获得元组 107.39999999999999 17.9 #分别获取值 [root@kube function]#
Python partial偏函数及用法
简单的理解偏函数,它是对原始函数的二次封装,是将现有函数的部分参数预先绑定为指定值,从而得到一个新的函数,该函数就称为偏函数。相比原函数,偏函数具有较少的可变参数,从而降低了函数调用的难度。
定义偏函数,需使用 partial 关键字(位于 functools 模块中),其语法格式如下:
偏函数名 = partial(func, *args, **kwargs)
[root@kube function]# cat demo10.py #coding:utf-8 from functools import partial #偏函数就是对函数的重新定义和封装 def sum_one(a,b): return a + b sum_two = partial(sum_one,a=100) print(sum_two(b=200)) #形参必须以关键字形式定义, [root@kube function]# py demo10.py 300 [root@kube function]#
Python函数递归
在一个函数体内调用它自身,被称为函数递归。函数递归包含了一种隐式的循环,它会重复执行某段代码,但这种重复执行无须循环控制。
总之,只要在一个函数的函数体中调用了函数自身,就是函数递归。递归一定要向已知方向进行。
最经典的问题就是斐波那契数列的解决办法:
斐波那契数列(Fibonacci sequence),又称黄金分割数列、因数学家列昂纳多·斐波那契(Leonardoda Fibonacci)以兔子繁殖为例子而引入,故又称为“兔子数列”,指的是这样一个数列:1、1、2、3、5、8、13、21、34、……在数学上,斐波纳契数列以如下被以递归的方法定义:F(1)=1,F(2)=1, F(n)=F(n-1)+F(n-2)(n>=2,n∈N*)
[root@kube function]# cat demo11.py #coding:utf-8 def recu(n): print(n) if n == 0: return n else: n = n - 1 return recu(n) #这是一个简单的递归调用自己的函数 print(recu(10)) [root@kube function]# py demo11.py 10 9 8 7 6 5 4 3 2 1 0 0 [root@kube function]#
下面用斐波那契数列进行测试:
[root@kube function]# cat demo14.py #coding:utf-8 a = 0 #计算500 以内的斐波那契数列 b = 1 while b < 500: print(b,end= ' ') a,b = b,a+b #用换位相加的方式进行计算 a=b ,b=a+b ,斐波那契数列就是前两项相加的和 [root@kube function]# py demo14.py 1 1 2 3 5 8 13 21 34 55 89 144 233 377 [root@kube function]#
[root@kube function]# cat demo15.py #coding:utf-8 lis = [] for i in range(10): #使用列表的方式来计算斐波那契数列 if i == 0 or i == 1: lis.append(1) else: lis.append(lis[i-1]+lis[i-2]) #从第3 位开始的值就是索引列表的第一位和第二位相加,依次计算根据索引来计算,这个真的很不错 print(lis) [root@kube function]# py demo15.py [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] [root@kube function]#
[root@kube function]# cat demo12.py #coding:utf-8 def fibo(n): #用递归的方法计算,每一个值是前两位之和依次递归先计算第一位和第二位,然后第二位和第三位,然后第三和第四位得到第五位 if n <= 1: return n else: return fibo(n-1) + fibo(n-2) result = fibo(5) #fibo(5) #fibo(4) + fibo(3) #fibo(3)+fibo(2) + fibo(2)+1 #fibo(2) + 1 + 1 + 1 +1 #1 + 1+ 1+1+1 =5 print(result) #这里没有使用循环依次获取值看下面例子 [root@kube function]# py demo12.py 5 [root@kube function]#
[root@kube function]# cat demo12.py #coding:utf-8 def fibo(n): if n <= 1: return n else: return fibo(n-1) + fibo(n-2) for i in range(1,6): #使用 for 循环得到五位斐波那契数列 print(fibo(i)) #fibo(5) #fibo(4) + fibo(3) #fibo(3)+fibo(2) + fibo(2)+1 #fibo(2) + 1 + 1 + 1 +1 #1 + 1+ 1+1+1 =5 #print(result) [root@kube function]# py demo12.py 1 1 2 3 5 [root@kube function]#
[root@kube function]# cat demo16.py #获取某个值以内的所有乘积,那么每个值得乘积都要计算一遍,递归就类似抽梯子,循环体的执行就是为了获得最下面的那个值然后依次往上进行计算完成循环 #coding:utf-8 #因为所有的值都依赖前一个值,依次进行计算,开销比较大 def cj(n): print('test:',n) if n == 1: return 1 else: return n * cj(n-1) print(cj(5)) [root@kube function]# py demo16.py test: 5 test: 4 test: 3 test: 2 test: 1 120 [root@kube function]#
Python变量作用域(全局变量和局部变量)
在程序中定义一个变量时,这个变量是有作用范围的,变量的作用范围被称为它的作用域。换句话说,变量的作用域指的是程序代码能够访问该变量的区域,如果超过该区域,将无法访问该变量。
根据定义变量的位置(有效范围),可以将变量分为局部变量和全局变量
Python局部变量
局部变量是指在函数内部定义并使用的变量,它只在函数内部有效。
每个函数在执行时,系统都会为该函数分配一块“临时内存空间”,所有的局部变量都被保存在这块临时内存空间内。当函数执行完成后,这块内存空间就被释放了,这些局部变量也就失效了,因此离开函数之后就不能再访问局部变量了,否则解释器会抛出 NameError 错误。
[root@kube function]# cat demo17.py #coding:utf-8 def test(a): b = 'hello' return b + a print(test('world')) print(b) [root@kube function]# py demo17.py helloworld Traceback (most recent call last): #没有找到变量b 报错了,也就是变量b 只在函数test() 中生效了 File "demo17.py", line 7, in <module> print(b) NameError: name 'b' is not defined [root@kube function]#
Python全局变量
和局部变量相对应,全局变量指的是能作用于函数内外的变量,即全局变量既可以在各个函数的外部使用,也可以在各函数内部使用。
定义全局变量的方式有以下 2 种:
- 在函数体外定义的变量,一定是全局变量,例如:
[root@kube function]# cat demo17.py #coding:utf-8 b = 'wadaxiwa' #b 在函数体外,定义位全局变量 def test(a): return b + a print(test('world')) print(b) [root@kube function]# py demo17.py wadaxiwaworld wadaxiwa [root@kube function]#
在函数体内定义全局变量。即使用 global 关键字对变量进行修饰后,该变量就会变为全局变量。例如:
注意,在使用 global 关键字修饰变量名时,不能直接给变量赋初值,否则会引发语法错误。
[root@kube function]# cat demo17.py #coding:utf-8 #b = 'wadaxiwa' def test(a): global b b = 'tom_jerry' return b + a print(test('world')) print(b) [root@kube function]# py demo17.py tom_jerryworld tom_jerry [root@kube function]#
获取指定作用域范围中的变量
不管是在函数的局部范围内还是在全局范围内,都可能存在多个变量,每个变量“持有”该变量的值。从这个角度来看,不管是局部范围还是全局范围,这些变量和它们的值就像一个“看不见”的字典,其中变量名就是字典的 key,变量值就是字典的 value。实际上,Python 提供了如下三个工具函数来获取指定范围内的“变量字典”:
globals():该函数返回全局范围内所有变量组成的“变量字典”。
locals():该函数返回当前局部范围内所有变量组成的“变量字典”。
vars(object):获取在指定对象范围内所有变量组成的“变量字典”。如果不传入object 参数,vars() 和 locals() 的作用完全相同。
globals() 和 locals() 看似完全不同,但它们实际上也是有联系的,关于这两个函数的区别和联系大致有以下两点:
locals() 总是获取当前局部范围内所有变量组成的“变量字典”,因此,如果在全局范围内(在函数之外)调用 locals() 函数,同样会获取全局范围内所有变量组成的“变量字典”;而 globals() 无论在哪里执行,总是获取全局范围内所有变量组成的“变量字典”。
一般来说,使用 locals() 和 globals() 获取的“变量字典”只应该被访问,不应该被修改。但实际上,不管是使用 globals() 还是使用 locals() 获取的全局范围内的“变量字典”,都可以被修改,而这种修改会真正改变全局变量本身:但通过 locals() 获取的局部范围内的“变量字典”,即使对它修改也不会影响局部变量。
[root@kube function]# cat demo18.py #coding:utf-8 x = 10 def test(): age = 20 print(age) print('locals:' , locals()) print('globals:', globals()) print('--------------------------------- ') locals()['age'] = 12 #修改局部变量 print('xxxx:', age) globals()['x'] = 19 #修改全局变量 print(test()) print(x) print('wai locals:' , locals()) print('wai globals:', globals()) [root@kube function]# py demo18.py 20 locals: {'age': 20} #locals 获取局部变量 globals: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fe0b0819d10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'demo18.py', '__cached__': None, 'x': 10, 'test': <function test at 0x7fe0b073c200>} #globals 获取全局变量不包括局部变量
--------------------------------- xxxx: 20 #修改局部变量。没有改变 None 19 #全局变量修改了 wai locals: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fe0b0819d10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'demo18.py', '__cached__': None, 'x': 19, 'test': <function test at 0x7fe0b073c200>} wai globals: {'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x7fe0b0819d10>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'demo18.py', '__cached__': None, 'x': 19, 'test': <function test at 0x7fe0b073c200>} [root@kube function]#
#注意locals 在局部范围内无法修改局部变量的值,locals() 只读, globals() 可读可写
Python如何在函数中使用同名的全局变量
我们知道,全局变量默认可以在所有函数内被访问,但是,如果当函数中定义了与全局变量同名的变量时,就会发生局部变量遮蔽(hide)全局变量的情形
[root@kube function]# cat demo19.py #coding:utf-8 name = 'tom' #这种是不冲突的分别在自己范围生效,作用范围不一样 def test(): name = 'jojo' print(name) test() print(name) [root@kube function]# py demo19.py jojo tom [root@kube function]#
[root@kube function]# cat demo19.py #coding:utf-8 name = 'tom' 这种是局部范围内未定义,就引用全局变量 def test(): # name = 'jojo' print(name) test() print(name) [root@kube function]# py demo19.py tom tom [root@kube function]#
[root@kube function]# cat demo19.py #coding:utf-8 name = 'tom' def test(): # name = 'jojo' print(name) name = 'jojo' test() print(name) [root@kube function]# py demo19.py Traceback (most recent call last): #Python 语法规定,在函数内部对不存在的变量赋值时,默认就是重新定义新的局部变量,这会使得函数内部遮蔽重名的 name 全局变量。由于局部变量 name 在 print(name) 后才初始化,所以程序会报错 File "demo19.py", line 7, in <module> test() File "demo19.py", line 5, in test print(name) UnboundLocalError: local variable 'name' referenced before assignment [root@kube function]#
访问被遮蔽的全局变量。如果希望程序依然能访问 name 全局变量,且在函数中可重新定义 name 局部变量,也就是在函数中可以访问被遮蔽的全局变量,此时可通过 globals() 函数来实现,将上面程序改为如下形式即可
[root@kube function]# cat demo19.py #coding:utf-8 name = 'tom' def test(): # name = 'jojo' #print(name) print(globals()['name']) name = 'jojo' test() print(name) [root@kube function]# py demo19.py tom tom [root@kube function]#
在函数中声明全局变量。为了避免在函数中对全局变量赋值(不是重新定义局部变量),可使用 global 语句来声明全局变量。因此,可将程序改为如下形式:
name = 'Charlie' def test (): # 声明name是全局变量,后面的赋值语句不会重新定义局部变量 global name # 直接访问name全局变量 print(name) # Charlie name = '孙悟空' test() print(name) # 孙悟空
增加了“global name”声明之后,程序会把 name 变量当成全局变量,这意味着 test() 函数后面对 name 赋值的语句只是对全局变量赋值,而不是重新定义局部变量。
[root@kube function]# cat demo19.py #coding:utf-8 name = 'tom' def test(): global name print(name) name = 'jojo' test() print(name) [root@kube function]# py demo19.py tom jojo [root@kube function]#
Python局部函数及用法(包含nonlocal关键字)
前面所看到的函数都是在全局范围内定义的,它们都是全局函数。Python 还支持在函数体内定义函数,这种被放在函数体内定义的函数称为局部函数。
在默认情况下,局部函数对外部是隐藏的,局部函数只能在其封闭(enclosing)函数内有效,其封闭函数也可以返回局部函数,以便程序在其他作用域中使用局部函数
[root@kube function]# py demo20.py ------------- 100 1000 30 [root@kube function]# cat demo20.py #coding:utf-8 def get_math_func(type,n): def square(n): return n * n def cube(n): return n * n * n def acc(n): return n + n + n if type == 'square': return square(n) elif type == 'cube': return cube(n) else: return acc(n) print('------------- ') print(get_math_func('square',10)) print(get_math_func('cube',10)) print(get_math_func('acc',10)) [root@kube function]# py demo20.py ------------- 100 1000 30 [root@kube function]#
通过 nonlocal 语句即可声明访问赋值语句只是访问该函数所在函数内的局部变量。
[root@kube function]# py demo21.py Charlie 孙悟空 [root@kube function]# cat demo21.py def foo (): # 局部变量name name = 'Charlie' def bar (): nonlocal name # 访问bar函数所在的foo函数的name局部变量 print(name) # Charlie name = '孙悟空' print(name) bar() foo() [root@kube function]# py demo21.py Charlie 孙悟空 [root@kube function]#
Python函数使用方法(高级用法)
Python 的函数是“一等公民”,因此函数本身也是一个对象,函数既可用于赋值,也可用作其他函数的参数,还可作为其他函数的返回值。
使用函数变量
Python 的函数也是一种值:所有函数都是 function 对象,这意味着可以把函数本身赋值给变量,就像把整数、浮点数、列表、元组赋值给变量一样。
当把函数赋值给变量之后,接下来程序也可通过该变量来调用函数。
[root@kube function]# cat demo22.py #coding:utf-8 def cube(n,m): return n * m cj = cube print(cj(4,5)) [root@kube function]# py demo22.py 20 [root@kube function]#
使用函数作为函数形参
有时候需要定义一个函数,该函数的大部分计算逻辑都能确定,但某些处理逻辑暂时无法确定,这意昧着某些程序代码需要动态改变,如果希望调用函数时能动态传入这些代码,那么就需要在函数中定义函数形参,这样即可在调用该函数时传入不同的函数作为参数,从而动态改变这段代码。
[root@kube function]# cat demo23.py #coding:utf-8 def cube(n): return n * n * n def map(data,fn): result = [] for i in data: result.append(fn(i)) return result data = [1,2,3,4,5,6] print(map(data,cube)) #将cube()函数作为参数传递 [root@kube function]# py demo23.py [1, 8, 27, 64, 125, 216] [root@kube function]#
使用函数作为返回值(闭包)
Python 还支持使用函数作为其他函数的返回值
[root@kube function]# cat demo24.py #coding:utf-8 def get_fun(type): def square(n): return n + n def cube(n): return n * n * n if type == 'square': return square else: return cube a = get_fun('square') b = get_fun('cube') print(a(5)) print(b(5)) [root@kube function]# py demo24.py 10 125 [root@kube function]#
什么是闭包,Python闭包
闭包,又称闭包函数或者闭合函数,其实和前面讲的嵌套函数类似,不同之处在于,闭包中外部函数返回的不是一个具体的值,而是一个函数。一般情况下,返回的函数会赋值给一个变量,这个变量可以在后面被继续执行调用。
[root@kube function]# cat demo24.py #coding:utf-8 def get_fun(n): def square(m): return n * m return square a = get_fun(5) #在将get_fun(5) 赋值给a 时,是将函数square() 赋值给 a ,并且这时 n 的值为5 b = get_fun(6) print(a(4)) #执行 print(a(4)) 就是执行 square(4),那么 m = 4 ,n =5 print(b(4)) [root@kube function]# py demo24.py 20 24 [root@kube function]#
Python闭包的__closure__属性
闭包比普通的函数多了一个 __closure__ 属性,该属性记录着自由变量的地址。当闭包被调用时,系统就会根据该地址找到对应的自由变量,完成整体的函数调用
[root@kube function]# cat demo24.py #coding:utf-8 def get_fun(n): def square(m): return n * m return square a = get_fun(5) b = get_fun(6) print(a.__closure__) [root@kube function]# py demo24.py (<cell at 0x7fab21dd82d0: int object at 0x907320>,) [root@kube function]#