作为一个Python的初学者来说,深入理解Python中函数的概念是一件重要的事情。
重点1:如何理解函数是第一类对象(一等公民)
函数是第一类对象的概念:
第一:函数的名字是对函数的引用
第二:函数作为第一类对象可以赋值给其他的变量
第三:可以作为函数的参数传递给其他的函数
第四:可以作为函数的返回值
第五:函数可以作为容器类型的一个元素
简单来说,在python当中,函数可以当做数据来进行传递,即变量有什么特性,函数就有什么特性。
示例程序:
def foo():
"""
关键:foo就是C语言当中的指针变量,里面存放着内存空间的地址
"""
print('我是谁')
print('我在哪儿')
print('我中午吃什么')
fun = foo
print(fun,foo)
#此时此刻fun和foo这两个指针变量指向同一块的内存空间
fun()
def foo():
"""
关键:foo就是C语言当中的指针变量,里面存放着内存空间的地址
"""
print('我是谁')
print('我在哪儿')
print('我中午吃什么')
def g(func):
#我们将func作为一个函数的参数进行传递
print(func)
func()
g(foo)
def foo():
"""
关键:foo就是C语言当中的指针变量,里面存放着内存空间的地址
"""
print('我是谁')
print('我在哪儿')
print('我中午吃什么')
def g(func):
#我们将func作为函数的返回值进行使用
print(func)
return func
f = g(foo)
print(f)
f()
接下来我们将对最后一个进行深入讲解:
示例1:普通版程序
def select(sql):
print('=========>select')
def insert(sql):
print('=========>insert')
def update(sql):
print('=========>update')
def delete(sql):
print('=========>delete')
if __name__ == '__main__':
while True:
sql = input('>>').strip()
if not sql:continue
l = sql.split()
cmd = l[0]
if cmd == 'select':
select(sql)
elif cmd == 'insert':
insert(sql)
elif cmd == 'update':
update(sql)
elif cmd == 'delete':
delete(sql)
else:
pass
上面的这个程序看似没有什么问题,但是如果有20个类型的话,难道你要写20个if....else...吗?
修改版:
def select(sql):
print('=========>select')
def insert(sql):
print('=========>insert')
def update(sql):
print('=========>update')
def delete(sql):
print('=========>delete')
if __name__ == '__main__':
#作为第一类对象,函数可以作为容器类型的元素
dict_func = {
'select':select,
'insert':insert,
'update':update,
'delete':delete,
}
while True:
sql = input('>>').strip()
if not sql:continue
l = sql.split()
cmd = l[0]
if cmd in dict_func:
dict_func[cmd](sql)
经过上面一折腾,是不是代码上升了一个档次。
当然,后续我们讲了面向对象之后会用更高级的方法:反射
import sys
def select(sql):
print('=========>select')
def insert(sql):
print('=========>insert')
def update(sql):
print('=========>update')
def delete(sql):
print('=========>delete')
if __name__ == '__main__':
# 作为第一类对象,函数可以作为容器类型的元素
obj = sys.modules['__main__']
print(obj)
while True:
sql = input('>>').strip() #insert into table(xxxxx)
if not sql: continue
l = sql.split()
cmd = l[0]
#面向当中的反射:
if hasattr(obj,cmd):
func = getattr(obj,cmd)
print(func)
func(sql)
重点2:闭包函数
在学习装饰器的过程中,我们必须先越过一个坎,那就是闭包函数。
关于闭包函数,有三个可以参考的定义:
闭包函数的定义:
定义在内部的函数包含对外部作用域(而非全局作用域)变量的引用,该内部函数就称为闭包函数。
示例1:
def f1():
def f2():
x = 1
print(x)
在上面的例子中,f2这个函数虽然是在一个函数内部进行定义的,但是并没有包含对外部作用域变量的引入,所以f2这个函数并不是闭包函数。
示例:
x = 1
def f1():
def f2():
print(x)
在上面的例子中,f2这个函数虽然是在一个函数内部进行定义的,但是并没有包含对外部作用域变量的引入,因为x=1是一个全局作用域变量,所以2这个函数并不是闭包函数。
闭包函数实例场景:惰性计算(延迟计算)将某种状态先保存起来,什么时候用就用。
示例:
#!/usr/bin/python
# -*- coding:utf-8 -*-
def make_adder(m):
def adder(n):
return m + n
return adder
p = make_adder(30)
q = make_adder(40)
#本是相同的函数adder,但是由于配置信息的不同,导致运行结果并不相同
print(p.__name__,q.__name__)
print(p(20))
print(q(20))
#查看相应的配置信息:即闭包函数引用外部作用域的变量
print(p.__closure__[0].cell_contents)
print(q.__closure__[0].cell_contents)
在上面的例子中,我们不难发现,无论是make_adder(30)还是make_adder(40)返回的都是adder这个函数对象,但是由于配置信息的不同,导致运行结果并不相同。
示例2:
#!/usr/bin/python
# -*- coding:utf-8 -*-
def foo(m,n):
def bar(a):
return m + n + a
return bar
fun = foo(10,50)
print('指针变量fun的数值是:%s'%fun)
print(fun.__name__,fun.__closure__)
for item in fun.__closure__:
print(item.cell_contents)
print(fun(20))
相应debug视图:
OK,闭包函数就讲到这里。
重点3:装饰器
装饰器的概念:
装饰器就是为了在不修改被装饰对象的源代码以及调用方式的前提下,为其添加新的功能;
装饰器的语法:
假设被装饰的函数是index,请写出@timer的含义:
index = timer(index)
场景:假如我现在有3个函数,如下所示:
def index():
print('欢迎来到python世界')
def home():
print('欢迎来到scala世界')
def edit():
print('欢迎来到Java世界')
index()
home()
edit()
现在的需求:求出每一个函数的运行时间:
渣渣aa的做法:
import time
def index():
start_time = time.time()
print('欢迎来到python世界')
end_time = time.time()
print('run time is %s'%(end_time-start_time))
def home():
start_time = time.time()
print('欢迎来到scala世界')
end_time = time.time()
print('run time is %s'%(end_time-start_time))
def edit():
start_time = time.time()
print('欢迎来到Java世界')
end_time = time.time()
print('run time is %s'%(end_time-start_time))
index()
home()
edit()
渣渣bb的做法:
def decorator(func):
start_time = time.time()
func()
end_time = time.time()
print('run time is %s'%(end_time-start_time))
def index():
print('欢迎来到python世界')
def home():
print('欢迎来到scala世界')
def edit():
print('欢迎来到Java世界')
decorator(index)
decorator(home)
decorator(edit)
其中渣渣a的问题:修改了源代码;渣渣b的问题:修改了函数正常的调用方式。
问题产生了:我们如何在不修改被装饰对象的源代码以及调用方式的前提下为函数增加新的功能呢?这个时候我们的装饰器就上场了。
装饰器的解决方案:
import time
def decorator(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('run time is %s' % (end_time - start_time))
return wrapper
@decorator
def index():
print('欢迎来到python世界')
@decorator
def home():
print('欢迎来到scala世界')
@decorator
def edit():
print('欢迎来到Java世界')
index()
home()
edit()
运行结果:
对于上面的代码,第一眼看上去是不是感觉很蒙圈,其实那是因为我中间省略了一个重要的步骤:
#!/usr/bin/python
# -*- coding:utf-8 -*-
import time
def decorator(func):
def wrapper():
start_time = time.time()
func()
end_time = time.time()
print('run time is %s' % (end_time - start_time))
return wrapper
def index():
print('欢迎来到python世界')
index = decorator(index)
print(index,' ',index.__name__)
def home():
print('欢迎来到scala世界')
home = decorator(home)
def edit():
print('欢迎来到Java世界')
edit = decorator(edit)
index()
home()
edit()
随后在上一张图片:当执行完 index = decorator(index) 之后:返回的wrapper实际上是一个指针变量,所以index和wrapper将指向同一块内存空间,也就是说index已经不是我们之前的index了,而是悄悄的变成了闭包函数wrapper,wrapper在控制着index。
我想你也许发现哪里发生变化了,即index = decorato(index)等价于@decorator,对的,这就是装饰器的本质:Python的一种语法糖而已,装饰器就是闭包函数的一种实现。
上面我们使用的是简单的无参装饰器,但是对于上面的程序实际上还是有两个缺陷的:
假如3个原生函数是这样的:
def index(name):
print('欢迎来到python世界')
def home():
print('欢迎来到scala世界')
def edit():
print('欢迎来到Java世界')
如果我们还按照上面的方式去运行代码,将会抛出错误,因为当执行到func()的时候,实际上执行的是原始的index函数,但是我们在调用
的时候并没有传入参数,所以:解决bug1:原生函数带有参数
import time
def decorator(func):
def wrapper(*args,**kwargs): #wrapper('小泡芙')
start_time = time.time()
func(*args,**kwargs) #执行到这里的时候会发现少了一个参数,因为原始的index函数必须传入一个参数
end_time = time.time()
print('run time is %s' % (end_time - start_time))
return wrapper
@decorator
def index(name):
print('欢迎来到python世界')
@decorator
def home():
print('欢迎来到scala世界')
@decorator
def edit():
print('欢迎来到Java世界')
index('小泡芙')
home()
edit()
但是接下来问题又来了,如果原生函数带有返回值我们怎么获取呢?
@decorator
def index(name):
print('欢迎来到python世界')
return 'Hello World'
@decorator
def home():
print('欢迎来到scala世界')
@decorator
def edit():
print('欢迎来到Java世界')
print(index('小泡芙'))
home()
edit()
如果我们还按照以前的方式的话,你会发现:返回值将是None,为什么呢?因为wrapper函数的返回值就是None。
解决bug2:原生函数带有返回值
#!/usr/bin/python
# -*- coding:utf-8 -*-
import time
def decorator(func):
def wrapper(*args,**kwargs): #wrapper('小泡芙')
start_time = time.time()
res = func(*args,**kwargs) #执行到这里的时候会发现少了一个参数,因为原始的index函数必须传入一个参数
end_time = time.time()
print('run time is %s' % (end_time - start_time))
return res
return wrapper
@decorator
def index(name):
print('欢迎来到python世界')
return 'Hello World'
@decorator
def home():
print('欢迎来到scala世界')
@decorator
def edit():
print('欢迎来到Java世界')
print(index('小泡芙'))
home()
edit()
运行结果:
欢迎来到python世界
run time is 0.0
Hello World
欢迎来到scala世界
run time is 0.0
欢迎来到Java世界
run time is 0.0
Process finished with exit code 0
到这里我们将引出常用无参装饰器的模型:
def decorator(func):
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
return res
return wrapper
OK,装饰器就讲到这里....