3.2、函数进阶
函数对象
函数是第一类对象,即函数可以当作数据传递:
#1 可以被引用 #2 可以当作参数传递 #3 返回值可以是函数 #4 可以当作容器类型的元素
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def foo(): print('foo') def bar(): print('bar') dic={ 'foo':foo, 'bar':bar, } while True: choice=input('>>: ').strip() if choice in dic: dic[choice]()
全局变量与局部变量
1.定义:在函数内部的变量拥有一个局部作用域,定义在函数外的拥有全局作用域。
局部变量只能在其被声明的函数内部访问,而全局变量可以在整个程序范围内访问。调用函数时,所有在函数内声明的变量名称都将被加入到作用域中。
##如果函数的内容无global关键字,优先读取局部变量,能读取区部变量,无法对全局变量重新赋值,但对于可变类型,可以对内部元素进行操作。如果函数中有global关键字,变量本质就是全局的那个变量,可读取可复制
gloabal、nonlocal
首先我们写这样一个代码, 首先在全局声明一个变量, 然后再局部调用这个变量, 并改变这 个变量的值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a = 100 def func(): global a # 加了个global表示不再局部创建这个变量了. 而是直接使用全局的a a = 28 print(a) func() print(a)
global表示. 不再使用局部作用域中的内容了. 而改用全局作用域中的变量
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
lst = ["麻花藤", "刘嘉玲", "詹姆斯"] def func(): lst.append("⻢云") # 对于可变数据类型可以直接进⾏访问. 但是不能改地址. 说⽩了. 不能赋值 在函数中赋值就是在局部空间创建了一个变量 print(lst) func() print(lst)
nonlocal 表示在局部作用域中, 调用父级命名空间中的变量.
如果父级命名空间中没有这个变量名,就继续向上查找.最多找到最外成的函数就结束了
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a = 10 def func1(): a = 20 def func2(): nonlocal a a = 30 print(a) func2() print(a) func1() 结果: 加了nonlocal 30 不加nonlocal 20
如果嵌套了很多层, 会是一种什么效果:?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a = 1 def fun_1(): a = 2 def fun_2(): nonlocal a a = 3 def fun_3(): a = 4 print(a) print(a) fun_3() print(a) print(a) fun_2() print(a) print(a) fun_1() print(a)
函数的嵌套
1定义:在一个函数中定义了另外一个函数即一个函数里用def语句来创建其它的函数的情况
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def outer(): def inner(): print('inner') print('outer') inner() outer() inner() # 此句会出错
内部函数不能被外部直接使用,会抛NameError异常,2个体会列子:
函数的作用域,命名空间(名称空间)
名称空间:存放名字和值的关系的空间起一个名字叫: 命名空间即名称空间. 我们的变量在存储的时候就 是存储在这片空间中的.
####在python解释器开始执行之后, 就会在内存中开辟一个空间, 每当遇到一个变量的时候, 就把变量名和值之间的关系记录下来, 但是当遇到函数定义的时候, 解释器只是把函数名读入内存, 表示这个函数存在了, 至于函数内部的变量和逻辑, 解释器是不关心的. 也就是说一开始的时候函数只是加载进来, 仅此而已, 只有当函数被调用和访问的时候, 解释器才会根据函数内部声明的变量来进行开辟变量的内部空间. 随着函数执行完毕, 这些函数内部变量占用的空间也会随着函数执行完毕而被清空.
def fun(): a = 10 print(a) fun() print(a) # a不存在了已经
命名空间分类:
1. 内置命名空间--> 存放python解释器为我们提供的名字, list, tuple, str, int这些都是内置命名空间 (即builtins 内置模块的名称空间)
2. 全局命名空间--> 我们直接在py文件中, 函数外声明的变量都属于全局命名空间 (即globals 全局变量)
3. 局部命名空间--> 在函数中声明的变量会放在局部命名空间 (即locals 包含局部变量和形参)
加载顺序:
1. 内置命名空间
2. 全局命名空间
3. 局部命名空间(函数被执行的时候)
取值顺序:
1. 局部命名空间
2. 全局命名空间
3. 内置命名空间
a = 10 def func(): a = 20 print(a) func() # 20
作用域:就是作用范围(在python中一个函数就是作用域,所有的局部变量放置在其作用域中,代码定义完成后,作用域已经生成,作用域链向上查找)
按照生效范围来看分为 全局作用域 和 局部作用域
全局作用域: 包含内置命名空间和全局命名空间. 在整个文件的任何位置都可以使用(遵循 从上到下逐⾏执行).
局部作用域: 在函数内部可以使用.
作⽤域命名空间:
1. 全局作⽤用域: 全局命名空间 + 内置命名空间
2. 局部作⽤用域: 局部命名空间,只能在局部范围内生效
3.站在全局看:
使用名字的时候:如果全局有,用全局的
如果全局没有,用内置的
4.为什么要有作用域?为了函数内的变量不会影响到全局
5.globals方法:查看全局作用域的名字【print(globals())】
locals方法:查看局部作用域的名字【print(locals())】
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
a = 10 def func(): a = 40 b = 20 def abc(): print("哈哈") print(a, b) # 这⾥里里使⽤用的是局部作⽤用域 print(globals()) # 打印全局作用域中的内容 print(locals()) # 打印局部作用域中的内容 func()
6.nonlocal让内部函数中的变量在上一层函数中生效,外部必须有
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
# x=1 # def f1(): # x=2 # def f2(): # # x=3 # def f3(): # # global x#修改全局的 # nonlocal x#修改局部的(当用nonlocal时,修改x=3为x=100000000,当x=3不存在时,修改x=2为100000000 ) # # 必须在函数内部 # x=10000000000 # f3() # print('f2内的打印',x) # f2() # print('f1内的打印', x) # f1() # # print(x)
7.函数名可以用作参数
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def func(): print('func') def func2(f): f() print('func2') func2(func)
8.函数名可以作为函数的返回值
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def func(): def func2(): print('func2') return func2 f2=func() f2() #func2=func() #func2() 2. def f1(x): print(x) return '123' def f2(): ret = f1('s') #f2调用f1函数 print(ret) f2() 3. def func(): def func2(): return 'a' return func2 #函数名作为返回值 func2=func() print(func2())
函数名的本质
函数名本质上就是函数的内存地址
1.可以被引用
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def func(): print('in func') f = func print(f)
2.可以被当作容器类型的元素
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def f1(): print('f1') def f2(): print('f2') def f3(): print('f3') l = [f1,f2,f3] d = {'f1':f1,'f2':f2,'f3':f3} #调用 l[0]() d['f2']()
3.可以当作函数的参数和返回值(即就当普通变量用)
第一类对象(first-class object)指 1.可在运行期创建 2.可用作函数参数或返回值 3.可存入变量的实体。
闭包
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def func1(): name = "alex" def func2(): print(name) # 闭包 func2() func1() # 结果: alex
使用__closure__来检测函数是否是闭包. 使用函数名.__closure__返回cell就是
闭包. 返回None就不是闭包
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def func1(): name = "alex" def func2(): print(name) # 闭包 func2() print(func2.__closure__) func1() 结果: alex (<cell at 0x0000020077EFC378: str object at 0x00000200674DC340>,) 返回的结果不是None就是闭包
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
def wrapper(): money = 1000 def func(): name = 'eva' def inner(): print(name,money) return inner return func f = wrapper() i = f() i()
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
from urllib.request import urlopen def index(): url = "http://www.xiaohua100.cn/index.html" def get(): return urlopen(url).read() return get xiaohua = index() content = xiaohua() print(content)
高阶函数
变量可以指向函数,函数的参数能接受变量,那么一个函数就可以接受另外一个函数作为参数,这种函数就称为高阶函数。只需要满足以下任意一个条件,即是高阶函数:
接受一个或多个函数作为输入
return返回另外一个函数。 例如:
def func1(): age=83 def func2(): return func2 val=func1() print(val)
def func(): n=10 def func2(): print("func2",n) return func2 f=func() print (f) f()
小结: