函数对象
一、函数名是可以被引用的
秉承着一切皆对象的理念,我们再次回头来看函数(function)。
函数也是一个对象,具有属性(可以使用dir()查询)。作为对象,它还可以赋值给其它对象名,或者作为参数传递。
# 变量可以被传递
name = 'kai'
x = name
print(x)
# kai
print(id(name), id(x))
#2385705498608 2385705498608
# 尝试函数像变量一样被传递
def func():
print('from func')
print(func)
# <function func at 0x0000016E5E062E18>
f = func # 其实指向的也是函数func 指向的函数体代码的内存地址
print(f)
# <function func at 0x000001B4D0D92E18>
f()
# from func
print(id(func), id(f))
# 3085463137816 3085463137816
def func():
print("from func")
def index(args):
print(args)
args() # 函数主要一定义(先定义)就可以在任意地方调用
print("from index")
index(func)
# <function func at 0x000001B7429A2E18>
# from func
# from index
二、函数名可以被当做函数的返回值
def index():
print('index')
def func():
print('func')
return index
res = func() # 将返回的函数名index 赋值给变量res
# func
print(res)
# <function index at 0x000001EF64362E18>
res()
# index
三、函数名可以被当做作容器类型的参数
def func():
print('func')
l = [1, 2, func, func()] # 定义的时候默认执行了func(),所以下面先打印了 func
# func # func函数没有返回值,所以默认是None
print(l)
# [1, 2, <function func at 0x0000013931C92E18>, None]
函数对象小练习
def register():
print("注册了")
pass
def login():
print("登录了")
pass
def shopping():
print("购物了")
pass
def output_func_list():
print("----- 请选择功能!")
for key in func_list:
print(f"---- {key}.{func_list[key][1]}")
func_list = {
0: [register, '注册'],
1: [login, '登录'],
2: [shopping, '购物'],
}
while True:
output_func_list()
chose_func = input("请输入功能编号(q 退出系统):").strip()
if chose_func.isdigit():
# 执行相应功能
chose_func = int(chose_func)
# 判断输入的编号在不在功能列表里
if chose_func in func_list:
func_list[chose_func][0]() # 取到功能函数名,加括号调用
else:
print("您输入的功能编号不存在,请重新输入!")
elif chose_func.lower() in ['q', 'quit']:
print("感谢您的使用,祝您生活愉快~")
break
else:
print("请正确输入数字!")
函数名可以作为容器对象的元素值、函数名(即函数内存地址)可以加括号直接调用
四、函数的嵌套
函数的嵌套调用: # 在函数内部调用其他函数
def index():
print('index')
def func():
index() # 在定义 func 函数的时候不会直接调用 index 的方法 --> 函数定义的时候不执行代码
print('func')
func()
# index # 通过 func()函数内部调用了index() 函数,打印出了 index
# func
函数的嵌套调用可以将复杂的逻辑简单化
小练习:写一个可以求四个数中的最大值
def my_max(x, y):
if x > y:
return x
return y
def my_max4(a, b, c, d):
res = my_max(a, b)
res = my_max(res, c)
res = my_max(res, d)
return res
print(my_max4(1, 5, 7, 1))
# 7
嵌套定义
def outer():
x = 1
print("outer")
def inner():
print("inner")
inner()
# inner() # 会报错,在外部无法访问内部内容
outer()
# outer
# inner
实现在外部调用 outer函数的内部函数 inner
# 想在外部调用inner 可通过把内部的函数名当做外部函数的返回值来返回给外部
def outer():
x = 1
print("outer")
def inner():
print("inner")
return inner # 把 inner 函数当做函数的返回值返回给 outer函数的调用者
res = outer()
# outer
res() # 变相调用inner
# inner
实现代码
五、名称空间
名称空间: 存放的是变量名 与 变量值 的内存地址 绑定关系的地方 ,后文可能称之为命名空间。
访问变量的值: 要想访问一个变量的值,必须先去名称空间拿到对应的名字,才能访问变量的值
命名空间的分类:
命名空间分为:内置名称空间,全局名称空间,局部名称空间 三大类
内置名称空间: python 解释器提前已经定义好了的名字(已经存放到了内置名称空间中了)
print("hello world")
max(1, 44, 62, 15)
len('26515f1asfafqw')
sum([1, 2, 3, 4, 5])
# 像上面的print max len sum 并没有定义就可以值使用,它们就是python解释器提前定义好了的函数,属于内置命名空间的
全局命名空间:文件级别的代码
x = 1
if x == 1:
y = 2
print(y)
# 2
for i in [1, 2]:
print(i)
print(i)
# 1
# 2
# 2
# 上面的 x y z 都在全局名称空间,不要以为缩进的就是局部的(if、 for、 while 无论嵌套,多少层,他们内部所创建的名字都是全局名称空间的)
局部命名空间: (目前所学)函数体内创建的名字都属于局部名称空间(最外层的函数名是属于全局名称空间的)
def func():
username = 'jason'
# print(username) # 会报错 NameError: name 'username' is not defined
func()
至于为什么上面的 print(username) 为什么会报错,学完下面的知识你就知道啦。
六、命名空间的生命周期
名称空间的生命周期
内置名称空间:(最长)只要 python解释器启动,立马创建 关闭 python解释器时自动销毁
全局名称空间: 只要右键运行 py文件就会自动创建 py文件程序运行结束自动销毁
局部名称空间:(动态创建动态销毁)函数被调用的时候自动创建 函数执行结束后立即销毁
名称空间生命周期结束 -- > 里面存的变量与指向值的内存地址解绑,内存中的值等待垃圾回收机制回收
def 删除变量 -- > 里面存的变量与指向值的内存地址解绑,内存中的值等待垃圾回收机制回收 ---> 等同于名称空间里删除了一个变量(绑定关系)
垃圾回收机制:垃圾回收机制隔一段时间就会检查一次,内存中的值如果没有变量指向它(引用),那垃圾回收机制就会把它清除掉(释放内存)
如果多次检查都有变量等指向它,那就会把它等级提升,检查频率就会变低
命名空间的查找顺序
验证思路: # 找一个三个地方都有的东西来验证(比如 len、max等,暂时忽略命名规范不能与关键字重复) , # 分别注释来测试其查找顺序(全局、局部 )
len = '我是全局名称空间的len'
def func():
len = '我是局部名称空间的len'
print(len)
print(len) # 这里是全局的位置
# 我是全局名称空间的len
'''
# 把全局的len 注释掉,就去找了内置的len
print(len) # 是全局的位置
# <built-in function len>
'''
func()
# 我是局部名称空间的len
(******)名称空间的查找顺序
1.需要先确定当前的在哪(全局、局部),大前提
1.1 站在全局:全局 >>> 内置
1.2 站在局部:局部 >>> 全局 >>> 内置
1.2.2 站在局部的内部(多个局部嵌套):局部 >>> 上一级局部 >>> 上一级局部 >>> .... >>> 全局 >>> 内置 会在作用域同级的前后(这句代码前后的同级语句)去找,然后再上一级2.函数在定义阶段查找名字的顺序(范围)就已经固定了, 不会因为函数的调用位置变化而变化(*******)
可以在函数定义的时候写个注释,指出他查找的位置,防止逻辑复杂了搞不清楚
七、作用域
python中的作用域有 全局作用域 与 局部作用域 , 全局作用域: # 全局有效: 内置名称空间、全局名称空间 都属于全局作用域 , 局部作用域: # 局部有效:局部名称空间
# 尝试修改不可变类型的全局变量
x = 1
def func():
x = 2 # 实质是又创建了一个局部变量 x
func()
print(x) # 局部无法修改不可变类型的全局变量
# 1
# 尝试修改可变类型的局部变量
x = []
def func():
x.append('嘿嘿嘿')
func()
print(x) # 修改成功,局部可以修改可变类型的全局变量
# ['嘿嘿嘿']
# 全局访问不了局部的变量,所以不展开研究
小结论: # 局部无法修改不可变类型的全局变量 , # 局部可以修改可变类型的全局变量 (前提:在不使用 global 和 nonlocal 关键字的情况下)
通过 global 关键字在局部修改全局,修改多个用 , 隔开
x = 1 # 不可变类型
username = 'jason'
def func():
global x,username
x = 999 # 修改全局变量,而不是创建局部变量
username = 'egon'
func()
print(x, username)
# 999 egon
通过 nonlocal 关键字在局部修改局部,修改多个用 , 隔开
def func():
x = 1
def index():
x = 2
index()
print(x)
func()
# 1
# 想就在 index 里把 x 改了
def func():
x = 1
def index():
nonlocal x
x = 2
index()
print(x)
func()
# 2