Python 函数进阶
python代码运行的时候遇到函数是怎么做的。
从python解释器开始执行之后,就在内存中开辟了一个空间
每当遇到一个变量的时候,就把变量名和值之间的对应关系记录下来。
但是当遇到函数定义的时候解释器只是象征性的将函数名读入内存,表示知道这个函数的存在了,至于函数内部的变量和逻辑解释器根本不关心。
等执行到函数调用的时候,python解释器会再开辟一块内存来存储这个函数里的内容,这个时候,才关注函数里面有哪些变量,而函数中的变量会存储在新开辟出来的内存中。函数中的变量只能在函数的内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
这个“存放名字与值的关系”的空间叫做命名空间。
代码在运行伊始,创建的存储“变量名与值的关系”的空间叫做全局命名空间,在函数的运行中开辟的临时的空间叫做局部命名空间。
1、命名空间
命名空间一共分为三种:
全局命名空间
局部命名空间
内置命名空间
三种命名空间的加载和取值关系:
加载顺序: 内置命名空间(程序运行前加载)->全局命名空间(程序运行中:从上到下加载)->局部命名空间(程序运行中:调用时才加载)
取值:
在局部调用:局部命名空间->全局命名空间->内置命名空间
x = 1 def f(x): print(x) print(10)
在全局调用:全局命名空间->内置命名空间
x = 1 def f(x): print(x) f(10) print(x)
print(max)
1.1内置命名空间:
就是Python解释器一启动就可以使用的名字存储在内置命名空间中,内置的名字再启动解释器的时候被加载进内存里。如:print、input、list、tuple
1.2 全局命名空间:
是在程序从上到下被执行的过程中一次加载进内存的,放置了我们设置的所有变量名和函数名。
1.3 局部命名空间
就是函数内部定义的名字,当调用函数的时候,才会产生这个名称空间,随着函数执行的结束,这个命名空间就又消失了。
注意:
函数名指向的是一个内存地址,“函数名+()”就可以调用函数了。
def func(x): print(x) print(func) #<function func at 0x00546C48>
多个函数应该拥有多个独立的局部名字空间,不能共享。下例中第2个a不能被调用:
func --> 函数的内存地址 函数名() 函数的调用 函数的内存地址() 函数的调用 def func(): a=1 def func1(): print(a) #a不能使用
2、作用域
2.1全局作用域
包含内置名称空间、全局名称空间,作用在全局,在整个文件的任意位置都能被引用、内置和全局名字空间中的名字都属于全局作用域。
a=1 def func1(): print(a) #a能使用
2.2局部作用域
局部名称空间,作用在局部,函数(局部名字空间中的名字属于局部作用)
a = 12 b = 20 def func(): x = 'aaa' y = 'bbb' print(locals()) #{'x': 'aaa', 'y': 'bbb'} print(globals()) func()
print(globals()) print(locals())
Locals在局部就打印局部的内容,在全局就打印全局的(输出什么,根据locals所在的位置)
但globals 永远打印全局的名字.
a = 10 def func(): global a a = 20 print(a) #a=10 func() print(a) #a=20
3、函数的嵌套调用
def max2(x,y): m = x if x>y else y return m
def max4(a,b,c,d): res1 = max2(a,b) res2 = max2(res1,c) res3 = max2(res2,d) return res3 def max()
函数的嵌套定义:
def outer(): def inner(): print('inner') inner() outer()
函数内部使用外部函数的变量,函数先定义再调用
def outer(): a=1 def inner(): print(a) #a=1 print('inner') inner() outer()
4、nonlocal的是使用:
不可变类型变量,不能被修改会报错。
a = 1 def outer(): a=1 def inner(): b = 2 print(a) print('inner') def inner2(): a +=1 #不可变类型的修改 print('inner2') inner2() inner() outer()
a = 1 print('a',a) #1 def outer(): a=2 def inner(): a = 3 def inner2(): global a # nonlocal a a +=1 print('*a*', a) #2 inner2() print('**a**', a) #3 inner() print('***a***',a) #2 outer() print('a', a)#1--->2 因global,在inner2中被修改
a = 1 print('a',a) #a,1 def outer(): a=2 def inner(): a = 3 def inner2(): nonlocal a # nonlocal a a +=1 print('*a*', a) #a=3+1 *a* 4 inner2() print('**a**', a) #a=4 被修改 **a** 4 inner() print('***a***',a) #***a*** 2 outer() print('全局a', a)#全局a,1
global:
对于不可变数据类型 在局部可是查看全局作用域中的变量
但是不能直接修改
如果想要修改,需要在程序的一开始添加global声明
如果在一个局部(函数)内声明了一个global变量,那么这个变量在局部的所有操作将对全局的变量有效
nonlocal:
只能用于局部变量 找上层中离当前函数最近一层的局部变量
声明了nonlocal的内部函数的变量修改会影响到 离当前函数最近一层的局部变量
对全局无效
对局部 也只是对 最近的 一层 有影响
5、作用域链
在内部函数使用变量的时候,是从小局部到大局部,到内置名字的过程,一级一级往上找,找到最近的一个就使用。
a = 1 def outer(): def inner(): def inner2(): print('*a*', a) #a=1 inner2() inner() outer()
6、函数的本质
就是一个变量,保存了函数所在的内存地址。
func()函数名就是内存地址
func2 = func 函数名可以赋值
l = [func,func2] #函数名可以作为容器类型的元素 print(l) for i in l: i()
def func(): print(123) def wahaha(f): f() return f #函数名可以作为函数的返回值
def func(): print(123) def wahaha(f): f() return f qqxing = wahaha(func) # 函数名可以作为函数的参数 qqxing()
第一类对象(first-class object)指
1.可在运行期创建
2.可用作函数参数或返回值
3.可存入变量的实体。
7、闭包
定义:必须是嵌套函数,内部函数调用外部函数的变量。
def outer(): a = 1 def inner(): print(a) return inner inn = outer() inn()
def outer(): a = 1 def inner(): print(a) print(inner.__closure__) #(<cell at 0x0057EBF0: int object at 0x50A756F0>,) outer()
8、urlopen模块
闭包函数获取网络的应用
import urllib #模块 from urllib.request import urlopen ret = urlopen('http://www.xiaohua100.cn/index.html').read() print(ret) def get_url(): url = 'http://www.xiaohua100.cn/index.html' ret = urlopen(url).read() print(ret) get_url()
# 1.编写装饰器,为多个函数加上认证的功能(用户的账号密码来源于文件),要求登录成功一次,后续的函数都无需再输入用户名和密码 # flag=True # def wrapper(login): # def inner(*args,**kwargs): # with open('E:\S9 Python code\practice\zzm',encoding='utf-8') as f: # s = f.readlines() #获取文件中的用户名与密码 # name = s[0].strip() #获取用户名--->name # word = s[1].strip() #获取密码--->word # global flag #在全局调用flag # while flag: # username = input('username:') #输入用户名 # password = input('password:') #输入密码 # if username == name and password == word: # print('认证成功') # flag = False #修改全局变量flag的值,停止循环 # else: # print('用户名或密码错误,请重新输入:') # ret = login(*args,**kwargs) #调用函数 # return # return inner # # @wrapper # def add(): # print('successful add') # add() # # @wrapper # def update(): # print('successful update') # update() # 2.编写装饰器,为多个函数加上记录调用功能,要求每次调用函数都将被调用的函数名称写入文件 # def wrapper(log): # def inner(*args,**kwargs): # ret=log(*args,**kwargs) # with open('E:\S9 Python code\homework\zab',mode='a+',encoding='utf-8')as f: #打开文件,操作方式为追加 # f.write(log.__name__+'\n') #将运行后的函数名写入文件f 函数名.__name__ 可获取函数名 # return ret # return inner # # @wrapper # def update(): # print('successful update') # update() # # @wrapper # def add(): # print('successful add') # add() # 进阶作业(选做): # 1.编写下载网页内容的函数,要求功能是:用户传入一个url,函数返回下载页面的结果 # from urllib.request import urlopen # def get(url): # code = urlopen(url).read # return code # # ret = get('http://www.baidu.com') # print(ret) # 2.为题目1编写装饰器,实现缓存网页内容的功能: # import os # from urllib.request import urlopen # def wrapper(f): # def inner(*args,**kwargs): # if os.path.getsize('zab'): #判断文件是否为空,空则不执行with # with open('zab','rb')as f1: #以读打开zab这个文件,并进行读取 # return f1.read() #获得返回值 # ret = f(*args,**kwargs) # with open('zab','wb')as f1: #以读打开zab这个文件,并进行读取 # f1.write(b'********'+ret) #返回b'********'+ret # return ret #返回获取的网页内容 # return inner # # @wrapper # def get(url): # code = urlopen(url).read() #获取网页内容 # return code # # ret = get('http://www.baidu.com') # print(ret) # ret = get('http://www.baidu.com') # print(ret) # ret = get('http://www.baidu.com') # print(get(ret))

浙公网安备 33010602011771号