名称空间与函数对象
名称空间
名称空间即存放名字与对象映射/绑定关系的地方。对于x=3,Python会申请内存空间存放对象3,然后将名字x与3的绑定关系存放于名称空间中,del x表示清除该绑定关系。
在程序执行期间最多会存在三种名称空间:
一 名称空间的分类
1.1 内置名称空间
伴随python解释器的启动/关闭而产生/回收,因而是第一个被加载的名称空间,用来存放一些内置的名字,比如内置函数名
max
<built-in function max> #built-in 内置
?
1.2 全局名称空间
伴随python文件的开始执行/执行完毕而产生/回收,是第二个被加载的名称空间,文件执行过程中产生的名字都会存放于该名称空间中,如下名字
name='jerry'
def index():
pass
if a:
pass
while b:
pass
for i in range(10):
pass
1.3 局部名称空间
伴随函数的调用/结束而临时产生/回收,函数的形参、函数内定义的名字都会被存放于该名称空间中
def foo(x):
y=3 #调用函数时,才会执行函数代码,名字x和y都存放于该函数的局部名称空间中
名称空间的加载顺序是:内置名称空间->全局名称空间->局部名称空间,而查找一个名字,必须从三个名称空间之一找到,查找顺序为:局部名称空间->全局名称空间->内置名称空间。
二 作用域
2.1 全局作用域与局部作用域?
按照名字作用范围的不同可以将三个名称空间划分为两个区域:
- 全局作用域:位于全局名称空间、内建名称空间中的名字属于全局范围,该范围内的名字全局存活(除非被删除,否则在整个文件执行过程中存活)、全局有效(在任意位置都可以使用);
- 局部作用域:位于局部名称空间中的名字属于局部范围。该范围内的名字临时存活(即在函数调用时临时生成,函数调用结束后就释放)、局部有效(只能在函数内使用)。
?
2.2 作用域与名字查找的优先级?
在局部作用域查找名字时,起始位置是局部作用域,所以先查找局部名称空间,没有找到,再去全局作用域查找:先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常
x=100 #全局作用域的名字x
def foo():
x=300 #局部作用域的名字x
print(x) #在局部找x
foo()#结果为300
在全局作用域查找名字时,起始位置便是全局作用域,所以先查找全局名称空间,没有找到,再查找内置名称空间,最后都没有找到就会抛出异常?
?
x=100
def foo():
x=300 #在函数调用时产生局部作用域的名字x
foo()
print(x) #在全局找x,结果为100
提示:可以调用内建函数locals()和globals()来分别查看局部作用域和全局作用域的名字,查看的结果都是字典格式。在全局作用域查看到的locals()的结果等于globals()
?
global和nonlocal的使用
在函数内,无论嵌套多少层,都可以查看到全局作用域的名字,若要在函数内修改全局名称空间中名字的值,当值为不可变类型时,则需要用到global关键字
x = 10
def index():
global x # 声明这个x用的是全局的x
x = 20 # 我想在局部修改全局的x的值
# print(x)
index()
print(x) # 10
name_list = ['kevin', 'jerry']
def func():
name_list.append('tank')
func()
print(name_list) # ['kevin', 'jerry', 'tank']
"""
如果你想在局部修改全局的变量:
1. 如果你修改的是不可变的类型,需要global关键字的声明
2. 如果你修改的是可变类型的,无需使用global关键字声明
"""
对于嵌套多层的函数,使用nonlocal关键字可以将名字声明为来自外部嵌套函数定义的作用域(非全局)
def outer():
x = 666
def inner():
nonlocal x
x = 999
inner()
print(x) # 此时的x是多少? 666
outer()
def outer():
name_list = ['a', 'b']
def inner():
name_list.append('c')
inner()
print(name_list) # 此时的x是多少? 666
outer()
"""
如果你想在内部的局部修改外部的局部:
1. 如果你修改的是不可变的类型,需要nonlocal关键字的声明
2. 如果你修改的是可变类型的,无需使用nonlocal关键字声明
"""
?
函数对象
一 函数对象的使用
函数对象就是函数的名字
四种玩法
玩法一:函数名可以当成变量赋值
# 玩法一:
def index():
print('index')
return 123
# print(index())
'''函数名就是函数在内存中得地址'''
# print(index) # <function index at 0x0000022FD03AD550>
a = index
# print(a) # <function index at 0x0000022D4E03D550>
res=a() # index()
print(res)
# 第二种方法:函数名可以的当成函数的实参
def index():
print('index')
return None
def func(a):
# a:index
print(a)
res=a() # index()
print(res) # res的结果? None
# index:index函数所在的内存地址
func(index)
# 第三种玩法:函数名可以当成函数的返回值
def index():
print('index')
return 'index2'
def func():
print('func')
# return index() # 'index'
return index # 'index'
res = func() #
print(res)
res1=res() # index()
print(res1)
# 玩法四: 函数名可以当成容器类型的元素
def index():
print('index')
return None
ll = [11, 22, 33, index]
# ll = [11, 22, 33, index()]
# print(ll[3])
res=ll[3]() # index()
print(res)
?用例:
# 搭建一个ATM的框架
# 注册功能 登录功能 转账功能 提现功能 充值功能 等
def register():
pass
def login():
pass
def transfer():
pass
def withdraw():
pass
def pay():
pass
def shopping():
pass
def shopping1():
pass
func_dict = {
'1': register,
'2': login,
'3': transfer,
'4': pay,
'5': withdraw,
'6': shopping,
'7': shopping1,
}
### 代码启动,展示所有的功能编号
while True:
print("""
1. 注册功能
2. 登录功能
3. 转账功能
4. 充值功能
5. 提现功能
6. 购物功能
7. 购物功能
""")
# 让用户输入对应的功能编号
cmd = input('请输入你要执行的功能编号:').strip()
if not cmd.isdigit():continue
if cmd in func_dict:
func_dict.get(cmd)()
else:
print('请好好输入')
?
?二 函数的嵌套调用
嵌套:函数套函数
案例:
# 案例:给你四个数,比较大小,返回最大的
# 两个数的比较大小,返回大的
def my_max(a, b):
if a >b:
return a
return b
# res=my_max(1, 2)
# print(res)
##
def many_max(a, b, c, d, e):
'''四个数的比较大小其实还是要两两比较!'''
res=my_max(a, b) # res得到a和b中得大的那个
# 怎么样比较res和剩余的大小, 只需要拿res和c或者d中得一个进行比较
res1=my_max(res, c) # res1:a,b,c中得最大的那个
res2=my_max(res1, d)
res3=my_max(res2, e)
return res3
res=many_max(11, 12, 3, 24)
print(res)
三?闭包函数
3.1 闭与包
基于函数对象的概念,可以将函数返回到任意位置去调用,但作用域的关系是在定义完函数时就已经被确定了的,与函数的调用位置无关。
x=1
def f1():
def f2():
print(x)
return f2
def f3():
x=3
f2=f1() #调用f1()返回函数f2
f2() #需要按照函数定义时的作用关系去执行,与调用位置无关
f3() #结果为1
也就是说函数被当做数据处理时,始终以自带的作用域为准。若内嵌函数包含对外部函数作用域(而非全局作用域)中变量的引用,那么该’内嵌函数’就是闭包函数,简称闭包(Closures)
x=1
def outer():
x=2
def inner():
print(x)
return inner
func=outer()
func() # 结果为2
?可以通过函数的closure属性,查看到闭包函数所包裹的外部变量
func.__closure__
(<cell at 0x10212af78: int object at 0x10028cca0>,)
func.__closure__[0].cell_contents
2
?“闭”代表函数是内部的,“包”代表函数外’包裹’着对外层作用域的引用。因而无论在何处调用闭包函数,使用的仍然是包裹在其外层的变量。
3.2 闭包的用途
目前为止,我们得到了两种为函数体传值的方式,一种是直接将值以参数的形式传入,另外一种就是将值包给函数
import requests
# 方式一:
def get(url):
return requests.get(url).text
# 方式二:
def page(url):
def get():
return requests.get(url).text
return get
提示:requests模块是用来模拟浏览器向网站发送请求并将页面内容下载到本地,需要事先安装:pip3 install requests
对比两种方式,方式一在下载同一页面时需要重复传入url,而方式二只需要传一次值,就会得到一个包含指定url的闭包函数,以后调用该闭包函数无需再传url
# 方式一下载同一页面
get('https://www.python.org')
get('https://www.python.org')
get('https://www.python.org')
……
# 方式二下载同一页面
python=page('https://www.python.org')
python()
python()
python()
……
闭包函数的这种特性有时又称为惰性计算。使用将值包给函数的方式,在接下来的装饰器中也将大有用处。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!