在python中,函数是第一类对象,函数是第一等公民
本质上函数可以当变量用
def func(): # func=函数的内存地址
print("from func")
1.可以赋值
f = func
print(f) # 打印的结果就是func的内存地址
f() # 运行代码func(),from func
2.可以当作参数传给另一个函数
def foo(x):
print(x) # 函数名func的内存地址
x() # 加括号触发func代码的运行,from func
foo(func)
3.可以当作函数的返回值
def foo(x):
return x
res = foo(func) # func当成参数传入foo(func),返回值是func重新赋值给res
print(res) # res = func的函数内存地址
4.可以当作容器类型的元素
l = [func]
l[0]() # 把func函数名当作元素放进l的列表中.运行代码结果为from func
银行取款功能
def withdraw():
print("提款")
def tranfer():
print("转账")
def check_balance():
print("查询余额")
def save():
print("存款")
func_dic = {
"1": ["提款", withdraw],
"2": ["转账", tranfer],
"3": ["查询余额", check_balance],
"4": ["存款", save]
}
while True:
print("0" "退出")
for k,v in func_dic.items():
print(k)
choice = input("输入指令").strip()
if choice == "0":
break
if choice in func_dic:
func_dic[choice][1]()
else:
print("输入错误")
函数的嵌套调用
def max2(x,y):
if x > y:
return x
else:
return y
def max4(a, b, c, d):
res1 = max2(a, b)
res2 = max2(res1, c)
res3 = max2(res2, d)
return res3
print(max4(11, 99, 66, 55)) #打印结果为99
函数的嵌套定义
def f1():
x = 10
def f2():
print("from f2")
print(x) # 10 (一个变量放进函数里面只能在函数里面使用)
print(f1) # 函数f1的内存地址
print(f2) # <function f1.<locals>.f2 at 0x00E18388> 函数f1内部函数2的内存地址
f1()
# print(x) 打印结果不显示
# print(f2) 打印结果不显示
名称空间
名称空间(namespaces):存放名字的地方
内置名称空间:存放内置的名字
生命周期:python解释器启动则产生,关闭则销毁
全局名称空间:存放的是顶级的名字(只有不在函数内的都是顶级名字)
生命周期:运行python文件时则产生,python文件运行完毕则销毁
运行python,启动python解释器,先运行内置名称空间文件,然后会立刻运行全局名称空间文件,再是局部空间
x = 10
y = 20
if 1 > 0:
z = 30
with open("a.txt", mode="wt")as f:
a = 333
while True:
c = 444
以上这些都是属于全局名称空间
局部名称空间:存放的是函数内的名字
生命周期:定义函数时是不会产生名称空间的,调用函数则产生,函数调用完毕则销毁
x = 10 # 全局名称空间
def foo(m):
m = 111 # m,n都是局部名称空间的名字
n = 222
foo(111)
内置名称空间就一个,一个文件就一个全局名称空间,调用一个函数就有一个局部空间名称,局部空间名称空间有好多个
名称空间找名字的优先级关系:先从当前空间找,一级一级往上找.名称空间之间没有嵌套关系
在函数内找名称,先从函数局部名称空间里找名字,再去全局名称空间找,最后再去内置名称空间找
如果在全局名称空间找,就先从全局名称空间里面找名字,再去内置空间找,不会去局部名称空间找
核心:名字的访问优先级
基于当前的位置向外查找
函数内->外层函数->...->全局->内置
在定义的时候不要把内置的名字拿来定义.这样有可能会覆盖内置的名字
len = 10
def func():
len = 20
print(len) # len在局部名称空间内,运行结果先运行len=20
func()
print(len) # len在全局名称空间内,运行结果len=10
案例
def f1():
# x=555
def f2():
# x=666
print(x)
f2()
x = 444
f1()
寻找顺序:
该函数代码运行时,先从局部名称空间最里层,先找到x = 666,
再找到x = 555,如果局部名称空间内没有找到名称时,会找到全局名称空间x = 444,
按照顺序找到任意一个名称都不会再继续往外层寻找了
LEGB:(在函数定义阶段就成成立了)
L:local 函数内部作用域
E:Enclosing 函数内部与内嵌函数之间
G:Global 全局作用域
B:build-in 内置作用
名称空间与作用域的关系时在函数定义阶段(扫描语法时)就确立的,与什么时候调用以及调用位置无关
案例1:
x = 111
def f1():
print(x) # 111
def f2():
x = 222
f1()
f2()
# 定义阶段全局变量x=111,在函数f2内部调用f1的函数,f1找x的时候,函数内部没有x,会找到全局x=111,找不到就报错,不会找到f2的函数内部
案例2:
x = 111
def f1():
print(x)
x = 222 # 局部名称空间的x是先引用后定义的,运行代码会直接语法报错
f1()
作用域
全局作用域:内置名称空间+全局名称空间
特点:全局存活,全局有效
局部作用域:局部名称空间的名字
特点:临时存货,局部有效
global
案例1 全局名称空间的名称为可变类型时
l = []
def func():
l.append(1111)
func()
print(l)
案例2 全局空间名称为不可变类型时
x = 111
def func():
global #当全局的值是不可变类型时,加了global全局作用域后会把全局的x=111的值改成函数里面x=222的值
x = 222
func()
print(x)
尽量不要用global局部作用域改全局的作用域
nonlocal
x = 111
def f1():
x = 222
def f2():
nonlocal x # nonlocal表示函数名是来自外层函数的,不是全局。加了nonlocal打印结果为333
x = 333
f2()
print(x) # x=222与x=333同时在函数内部的时候,调用f1函数运行f2代码,直接打印f1定义的变量值x=222
f1()
nonlocal会把外层的f1的x=222改成内层的x=333,如果说第一个外层没有找到局部名会一直往外层找,找到全局层之前还找不到会直接报错
闭包函数
闭:指的是该函数的定义时在函数内的函数
包:值得是该函数引用了一个外层函数作用域的名字
def outter():
x = 111
def wrapper():
print(x)
return wrapper
f = outter() # f = wrapper = outter()
#f() f()= wrapper() = outter()()
print(f) # 函数名wrapper的内存地址
def foo():
x = 222
f() # 等同于wrapper(),wrapper内部没有定义,去函数outter内部找,如果找不到就去全局找,找不到就报错。不会在函数foo里面找
foo()
为函数体代码传参的方案
方案1:直接用参数传
def wrapper(x):
print(x)
wrapper(111)
wrapper(222)
wrapper(333)
方案2:闭包函数传参
def outter(x):
# x=111 如果outter定义了变量x=111,那么打印结果为111,函数的名称空间查找顺序,先查找内部,没有查外层函数,最后查全局
def wrapper():
print(x)
return wrapper
f1 = outter(111) # f1 = wrapper
f1()
f2 = outter(222)
f2()
f3 = outter(333)
f3()
装饰器
什么是装饰器
装饰器就是一个用来为被装饰对象添加新功能的工具
为何要用装饰器
开放封闭原则:一旦软件上线运行之后,应该对修改源代码封闭,对扩展功能开发
原则:
1.不修改函数内的源代码
2.不修改函数的调用方式
装饰器就是在遵循原则1和2的前提下,为被装饰对象添加上新功能
如何实现装饰器
需求:为函数index添加统计运行时间的功能
方案一:
import time
def index():
start = time.time()
time.sleep(1)
print("from index")
stop = time.time()
print("run time is %s" % (stop - start))
index()
方案二:
import time
def index():
time.sleep(1)
print("from index")
start = time.time()
index()
stop = time.time()
print("run time is %s" % (stop - start))
方案三:
import time
def index():
time.sleep(1)
print("from index")
def wrapper():
start = time.time()
index()
stop = time.time()
print("run time is %s" % (stop - start))
wrapper()
方案四:
import time
def index():
time.sleep(1)
print("from index")
def wrapper(func):
start = time.time()
func()
stop = time.time()
print("run time is %s" % (stop - start))
wrapper(index)