[toc]
一、函數(shù)對(duì)象
在python中,函數(shù)是一等對(duì)象,“一等對(duì)象”滿足下述條件的程序?qū)嶓w:
- 可以被引用
- 可以當(dāng)作參數(shù)傳遞給函數(shù)
- 能做為函數(shù)的返回結(jié)果
- 可以當(dāng)作容器數(shù)據(jù)類型的元素,例如:可以當(dāng)作字典中某個(gè)key的value
- 在運(yùn)行時(shí)創(chuàng)建
1.可以被引用
def foo():
print('from foo')
func=foo
print(foo)
print(func)
func()
# 輸出:
<function foo at 0x0000000000BAE1E0>
<function foo at 0x0000000000BAE1E0>
from foo
2. 可以當(dāng)作參數(shù)傳遞給另一個(gè)函數(shù)
def foo():
print('from foo')
def bar(func):
print(func)
func() # 此時(shí)func() == foo()
bar(foo) #將函數(shù)foo內(nèi)存對(duì)象當(dāng)做參數(shù)傳給bar()
# 輸出:
<function foo at 0x000000000114E1E0>
from foo
3.可以當(dāng)作一個(gè)函數(shù)的返回結(jié)果
def foo():
print('from foo')
def bar(func):
return func
f=bar(foo) # 此時(shí)f = foo
print(f)
f() # 此時(shí)執(zhí)行f() 相當(dāng)于執(zhí)行foo()
# 輸出:
<function foo at 0x0000000000B8E1E0>
from foo
4. 當(dāng)作其他容器類型數(shù)據(jù)的元素
def foo():
print('from foo')
dic={'func':foo} # 定義一個(gè)字典dic,將函數(shù)foo的內(nèi)存對(duì)象做為value
print(dic['func']) # 相當(dāng)于打印foo的內(nèi)存對(duì)象
dic['func']() # 相當(dāng)于執(zhí)行foo()函數(shù)
# 輸出:
<function foo at 0x0000000000A4E1E0>
from foo
應(yīng)用:
def select(sql):
print('========>select')
def insert(sql):
print('========>add')
def delete(sql):
print('=======>delete')
def update(sql):
print('=======>update')
func_dic={
'select':select,
'update':update,
'insert':insert,
'delete':delete
} # 定義一個(gè)字典,將上面定義的函數(shù)名做為value
def main():
while True:
sql = input('>>: ').strip()
if not sql:continue
l = sql.split()
cmd=l[0]
if cmd in func_dic:
func_dic[cmd](l)
main()
二、函數(shù)嵌套
1. 函數(shù)的嵌套定義
簡(jiǎn)單理解就是在定義一個(gè)函數(shù)時(shí),函數(shù)體內(nèi)又定義一個(gè)子函數(shù),示例如下:
def f1():
def f2():
print('from f2')
def f3():
print('from f3')
f3()
f2()
f1()
# 輸出:
from f2
from f3
2. 嵌套函數(shù)的調(diào)用
下面示例是一個(gè)求4個(gè)數(shù)中最大值的小程序:
# 嵌套函數(shù)
def mymax(x,y):
return x if x > y else y
def four(a,b,c,d):
res1=mymax(a,b) # 調(diào)用mymax()函數(shù)先比較a和b,較大的值給res1
res2=mymax(c,res1)
res3=mymax(d,res2)
return res3
print(four(8,45,9,34))
# 輸出:
45
三、名稱空間與作用域
1.定義名字的方法
- 導(dǎo)入的模塊名稱time,如:
import time
- 定義變量的名稱name,如:
name = 'caigy'
- 定義函數(shù)的名稱func,如:
def func():
pass
- 定義類的名稱foo,如:
class foo:
pass
2. 三種名稱空間
2.1 內(nèi)置名稱空間
隨著Python解釋器的啟動(dòng)而產(chǎn)生,比如python內(nèi)置的一些函數(shù):sun()、max()、min()等,這些函數(shù)隨著python的啟動(dòng)就定義好了,所以在定義名稱時(shí)不要與這些關(guān)鍵字重名
可以用以下方法查看python的內(nèi)置函數(shù):
import builtins
for i in dir(builtins):
print(i)
2.2 全局名稱空間
py文件的執(zhí)行會(huì)產(chǎn)生全局名稱空間,指的是文件級(jí)別定義的名字都會(huì)放入該空間
2.3 局部名稱空間
調(diào)用函數(shù)時(shí)會(huì)產(chǎn)生局部名稱空間,只在函數(shù)調(diào)用時(shí)臨時(shí)綁定,函數(shù)調(diào)用結(jié)束后解除綁定。
如下示例:
# !/user/bin/env python
# -*- coding:utf-8 -*-
name = 'caigy' # 1
def func():
x = 1 # 2
代碼中:
- 變量name是全局名稱空間
- 變量x在函數(shù)func()中,是局部名稱空間
3. 作用域
- 全局作用域:內(nèi)置名稱空間、全局名稱空間
- 局部作用域:局部名稱空間
python中名字的查找順序:
局部名稱空間 ---> 全局名稱空間 ---> 內(nèi)置名稱空間
- 查看全局作用域內(nèi)的名字: globals()
- 查看局部作用域內(nèi)的名字: locals()
示例代碼:
name = 'caigy'
def foo():
name = 'egon'
print(name)
print(locals())
print(globals())
foo()
以上代碼輸出結(jié)果:
egon
{'name': 'egon'}
{'__file__': 'D:/PycharmProjects/s17/day04/test.py', 'foo': <function foo at 0x013B5660>, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x01755A90>, 'name': 'caigy', '__name__': '__main__', '__spec__': None, '__package__': None, '__builtins__': <module 'builtins' (built-in)>, '__doc__': None, '__cached__': None}
作用域的有效范圍
- 全局作用域的名字:全局有效,在任何位置都能被訪問(wèn)到,除非del刪掉,否則會(huì)一直存活到文件執(zhí)行完畢
- 局部作用域的名字:局部有效,只能在局部范圍內(nèi)調(diào)用,只在函數(shù)調(diào)用時(shí)才有效,調(diào)用結(jié)束就失效
注:當(dāng)全局變量與局部變量同名時(shí),在定義局部變量的子程序內(nèi),局部變量起作用;在其它地方全局變量起作用
四、閉包函數(shù)
閉包函數(shù)的特性:
- 定義在一個(gè)函數(shù)的內(nèi)部的函數(shù)
- 包含對(duì)外部作用域而非全局作用域的引用
該內(nèi)部函數(shù)就稱為閉包函數(shù)
如下示例:
def func():
name = 'caigy'
def foo():
print(name)
return foo
f = func() # func()返回結(jié)果是foo ,所以f = foo
print(f) # 打印的實(shí)際上是foo的內(nèi)存對(duì)象地址
f()
# 輸出結(jié)果:
<function func.<locals>.foo at 0x01342150>
caigy
如上述代碼中,foo()就是一個(gè)閉包函數(shù)
閉包函數(shù)的應(yīng)用:隨性計(jì)算
爬取一個(gè)網(wǎng)頁(yè)
from urllib.request import urlopen
def index(url):
def get():
return urlopen(url).read()
return get
f= index('http://www.360duohui.com')
res = f().decode('utf-8')
print(res)
上述代碼中g(shù)et()函數(shù)就是一個(gè)閉包函數(shù)
總結(jié):閉包函數(shù)實(shí)現(xiàn)了內(nèi)部函數(shù)在外部可以被調(diào)用
獲取閉包函數(shù)所引用的外部變量(非全局變量)的值
def func():
name='caigy'
def foo():
print(name)
return foo
f = func()
print(f.__closure__[0].cell_contents)
# 輸出:
caigy
五、裝飾器(decorator)
裝飾器,顧名思義,就是用來(lái)裝飾用的,具體點(diǎn)說(shuō)就是給其它程序添加功能的。其本質(zhì)就是函數(shù),功能是為其他函數(shù)添加新功能。
裝飾器本身可以是任何可調(diào)用對(duì)象,被裝飾的對(duì)象也可以是任意可調(diào)用的對(duì)象。
1. 為什么要用裝飾器呢?
源于程序開(kāi)的開(kāi)放封裝原則:
- 對(duì)修改是封閉的,對(duì)擴(kuò)展是開(kāi)放的。
- 裝飾器就是為了在不修改被裝飾對(duì)象的源代碼以及調(diào)用方式的前提下,為其添加新功能
如下示例:
def run():
time.sleep(3)
print('already test 3s')
run()
為上述代碼添加一個(gè)打印日志的功能:
無(wú)參裝飾器
import time
def timer(func):
def wrapper(*args,**kwargs):
start_time = time.time()
func() #3
stop_time = time.time()
print('run is %ss'%(stop_time-start_time))
return wrapper
timer #1
def run():
time.sleep(3)
print('already test 3s')
run() #2
#1 @timer 實(shí)際上就是做了run = timer(run) 的操作;
#2 因?yàn)閠imer()函數(shù)的返回值是wrapper,所以此時(shí)的run = wrapper , 所run()實(shí)際就相當(dāng)于調(diào)用了wrapper()
#3 run 做為參數(shù)傳給timer()函數(shù),所以此時(shí)func=run,此時(shí)的func()相當(dāng)于調(diào)用了run()
以上代碼的輸出結(jié)果如下:
already test 3s
run is 3.0033679008483887s
2. 附加多個(gè)裝飾器
有參裝飾器
import time
def timer(func_1):
def wrapper_1(*args,**kwargs):
start_time = time.time()
func_1(*args,**kwargs) #3
stop_time = time.time()
print('run is %s秒 '%(stop_time-start_time))
return wrapper_1
user_stat = {'name':None,'stats':False}
def auth(func):
def wrapper(*args,**kwargs):
if user_stat['name'] and user_stat['stats']:
print('login successful')
func(*args, **kwargs)
else:
name = input('name: ')
pwd = input('password: ')
if name == 'egon' and pwd == '123':
# 登錄成功后記錄用戶名和登錄狀態(tài),下次調(diào)用時(shí)就不用再輸入用戶名登錄了
user_stat['name'] = 'egon'
user_stat['stats'] = True
print('login successful')
func(*args,**kwargs)
# time.sleep(3)
else:
print('login error')
return wrapper
@timer #2
@auth #1
def index():
print('Welcome to Oldboyedu.com')
@auth
def home(name):
print('Welcome to %s homepage'%name)
index() #4
home('egon')
輸出結(jié)果:
name: egon
password: 123
login successful
Welcome to Oldboyedu.com
run is 3.2893900871276855秒
login successful
Welcome to egon homepage
#1 @auth 實(shí)際上就是做了index = auth(index)的操作,因?yàn)閍uth()函數(shù)的返回值是wrapper,所以index此時(shí)等于wrapper,在下方加括號(hào)()就可以直接調(diào)用wrapper()函數(shù)了
#2 由于此時(shí)@auth 為wrapper, 所以@timer 就是wrapper = timer(wrapper) , timer()的返回值是wrapper_1,所以此時(shí)wrapper = wrapper_1
#3 由于wrapper被當(dāng)作參數(shù)傳給timer(),所以此時(shí)func_1 = wrapper
#4 在執(zhí)行index()時(shí),相于當(dāng)于執(zhí)行wrapper_1() ;當(dāng)程序執(zhí)行到func_1(args,*kwargs)時(shí),此時(shí)的func_1 就是 wrapper,所以就會(huì)調(diào)用wrapper(args,*kwargs);當(dāng)wrapper()函數(shù)內(nèi)的程序執(zhí)行到func(args,*kwargs)時(shí),此時(shí)的func = index ,所以用調(diào)用真實(shí)定義的index()函數(shù);然后返回wrapper_1(),繼續(xù)執(zhí)行func_1()后的代碼。
六、迭代器(iterator)
1. 迭代的概念
重復(fù)的過(guò)程稱為迭代,每次重復(fù)即為一次迭代,并且每次迭代的結(jié)果作為下一次迭代的初始值;
不是迭代:
while True: #只滿足重復(fù),因而不是迭代
print('====>')
下面才為迭代
l = [1, 2, 3]
count = 0
while count < len(l):
print('====>', l[count])
count += 1
# 輸出:
====> 1
====> 2
====> 3
注:以上這種方式迭代的是列表,而列表是有序的對(duì)象,那像無(wú)序的對(duì)象,比如:字典,集合,文件等如何迭代呢?
2. 為什么要有迭代器
對(duì)于沒(méi)有索引的數(shù)據(jù)類型,必須提供一種不依賴索引的迭代方式
可迭代的對(duì)象:內(nèi)置__iter__
方法的對(duì)象,都是可迭代的對(duì)象
[1,2].__iter__() # 列表
'hello'.__iter__() # 字符串
(1,2,).__iter__() # 元組
{'a':1,'b':2}.__iter__() # 字典
{1,2,3}.__iter__() # 集合
迭代器: 執(zhí)行__iter__
方法,得到的結(jié)果就是迭代器,迭代對(duì)象都具體__next__
方法
i = [1,2,3].__iter__()
print(i) # 打印可迭代對(duì)象的內(nèi)存地址
print(i.__next__())
print(i.__next__())
print(i.__next__())
print(i.__next__())
# 超出迭代的對(duì)象個(gè)數(shù)后會(huì)拋出異常:StopIteration
# 輸出:
<list_iterator object at 0x011B5AB0>
1
2
3
Traceback (most recent call last):
File "D:/PycharmProjects/s17/day04/test.py", line 209, in <module>
print(i.__next__())
StopIteration
迭代字典
i={'a':1,'b':2,'c':3}.__iter__()
print(i.__next__()) # 迭代字典中的key
print(i.__next__())
print(i.__next__())
print(i.__next__())
# 超出字典i中的鍵值對(duì)的個(gè)數(shù),會(huì)拋出異常:StopIteration
換一種方法:
i={'a':1,'b':2,'c':3}
I = iter(i)
print(next(I))
print(next(I))
print(next(I))
注:
__iter__()
=iter()
,__next__()
=next()
,兩種方法得到的結(jié)果是一樣的。
利用while循環(huán)迭代字典
dic={'a':1,'b':2,'c':3}
i=dic.__iter__() # 生成迭代器對(duì)象
while True:
try:
key=i.__next__()
print(dic[key])
except StopIteration: # 遇到指定異常后,執(zhí)行子代碼
break
3. 如何判斷一個(gè)對(duì)象是可迭代對(duì)象,還是迭代器對(duì)象
- 可迭代對(duì)象:只有
__iter__
方法,執(zhí)行該方法得到的是迭代器對(duì)象 - 迭代器對(duì)象:有
__next__
方法和__iter__
方法,迭代器對(duì)象執(zhí)行__iter__方法,
得到的結(jié)果仍然是它本身。
判斷可迭代的對(duì)象需要導(dǎo)入Iterable
模塊
from collections import Iterable,Iterator
f = open('a.txt','w')
f.__iter__()
# 下列數(shù)據(jù)類型都是可迭代的對(duì)象
print(isinstance('abc',Iterable)) # 字符串
print(isinstance([1,2,3],Iterable)) # 列表
print(isinstance({'a':1,},Iterable)) # 字典
print(isinstance({1,2,3},Iterable)) # 集合
print(isinstance((1,2,),Iterable)) # 元組
print(isinstance(f,Iterable)) # 文件
# 輸出:
True
True
True
True
True
True
判斷迭代器對(duì)象需要導(dǎo)入Iterator
模塊
from collections import Iterable,Iterator
f = open('a.txt','w')
f.__iter__()
# 只有文件是迭代器對(duì)象
print(isinstance('abc',Iterator))
print(isinstance([],Iterator))
print(isinstance((),Iterator))
print(isinstance({'a':1},Iterator))
print(isinstance({1,2},Iterator))
print(isinstance(f,Iterator))
# 輸出:
False
False
False
False
False
True
驗(yàn)證迭代器對(duì)執(zhí)行__iter__
方法得到的結(jié)果仍然是其本身
f = open('a.txt','w')
f1 = f.__iter__()
print(f)
print(f1)
print(f1 is f) # 輸出`True`表示結(jié)論正確
# 輸出:
<_io.TextIOWrapper name='a.txt' mode='w' encoding='cp936'>
<_io.TextIOWrapper name='a.txt' mode='w' encoding='cp936'>
True
4. 迭代器的優(yōu)點(diǎn)和缺點(diǎn)
其特點(diǎn):
- 訪問(wèn)者不需要關(guān)心迭代器內(nèi)部的結(jié)構(gòu),僅需通過(guò)next()方法不斷去取下一個(gè)內(nèi)容
- 不能隨機(jī)訪問(wèn)集合中的某個(gè)值 ,只能從頭到尾依次訪問(wèn)
- 訪問(wèn)到一半時(shí)不能往回退
- 便于循環(huán)比較大的數(shù)據(jù)集合,節(jié)省內(nèi)存
-
優(yōu)點(diǎn):
- 提供了一種不依賴于下標(biāo)的迭代方式
- 就迭代器本身來(lái)說(shuō),更節(jié)省內(nèi)存
-
缺點(diǎn):
- 無(wú)法獲取迭代器對(duì)象的長(zhǎng)度
- 不如序列類型的數(shù)據(jù)取值靈活,而且是一次性的,只能往后取值,不能往前退
驗(yàn)證:只能往后取值,不能往前退
l=[10000,2,3,4,5]
i=iter(l)
for item in i:
print(item)
print('=====================')
for item in i:
print(item)
# 輸出:
10000
2
3
4
5
====================
# 通過(guò)上述代碼 的結(jié)果可以看出,通過(guò)列表l生成的迭代器i ,通過(guò)for循環(huán)迭代后,再次通過(guò)for循環(huán)迭代,就取不到值了
擴(kuò)展:enumerate()
方法生成的也是迭代器對(duì)象
l=[2,3,4]
i=enumerate(l)
print(next(i))
print(next(i))
print(next(i))
# 輸出:
(0, 2)
(1, 3)
(2, 4)
總結(jié):python中for循環(huán)就是通過(guò)迭代器的方式來(lái)實(shí)現(xiàn)的,而while只是普通的循環(huán),for迭代 == while + try ... except(異常處理)
七、生成器(generator)
只要函數(shù)體內(nèi)包含yield關(guān)鍵字,該函數(shù)就是生成器函數(shù)
1. 生成器簡(jiǎn)單示例
生成器就是迭代器
def index():
print('first')
yield 1
print('second')
yield 2
print('third')
yield 3
g = index()
for i in g:
print(i)
上述代碼輸出:
first
1
second
2
third
3
通過(guò)next()方法:
def index():
print('first')
yield 1
print('second')
yield 2
print('third')
yield 3
g = index()
print(next(g)) # next()方法觸發(fā)迭代器g的執(zhí)行,進(jìn)而觸發(fā)函數(shù)的執(zhí)行
print(next(g))
print(next(g))
# 輸出:
first
1
second
2
third
3
生成器應(yīng)用 :
def counter(n):
print('start...')
i=0
while i < n:
yield i
i+=1
print('end...')
g=counter(5)
print(g) # 這一步不會(huì)執(zhí)行counter()函數(shù),如果是普通函數(shù),就會(huì)被執(zhí)行
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g))
# 輸出:
<generator object counter at 0x0196DB10>
start...
0
1
2
3
4
注:上述代碼中,迭代器對(duì)象g,只有通過(guò)next()方法(調(diào)用時(shí),才會(huì)觸發(fā)函數(shù)counter()的執(zhí)行
總結(jié):
yield的功能:
- 相當(dāng)于為函數(shù)封裝好
__iter__
和__next__
return
只返回一次值,函數(shù)就終止了,而yield
能返回多次值,每次返回都會(huì)將函數(shù)暫停,下一次next()
會(huì)從上一次暫停的位置繼續(xù)執(zhí)行
2. 生成器執(zhí)行流程
def foo():
print('in the foo ...')
food = yield '您好'
print('food >>>',food)
print('我在后面')
food1= yield '你壞'
print('food1 >>> ',food1)
g= foo()
res = next(g)
print(res)
res1 = g.send('x')
print(res1)
##res2= g.send('xx')
'''
生成器執(zhí)行流程:
1.g=foo(),只是將foo()生成器對(duì)象賦值給g,然后繼續(xù)往下執(zhí)行;
2.遇到next()或g.send('x')就開(kāi)始執(zhí)行foo()內(nèi)部的代碼,\
執(zhí)行遇到第一個(gè)yield時(shí),就暫停(我也理解為進(jìn)入休眠狀態(tài)),\
并將yield后面的值返回給next(g),并跳出到foo()外面next(g)所在的那一行,\
將yield返回的值賦值給res
3.res接收yield返回給next(g)的值,然后往下執(zhí)行代碼,打印res的值;
4.當(dāng)再次遇到next()或g.send('x')時(shí),喚醒foo()繼續(xù)從上次 \
暫停的位置開(kāi)始執(zhí)行, 同時(shí)將g.send(‘x’)中的'x'發(fā)送 \
給第一個(gè)yield,并賦值給food,然后繼續(xù)往下執(zhí)行;
5.當(dāng)遇到第二個(gè)yield時(shí),進(jìn)入暫停(休眠),\
同時(shí)將yield后面的值返回給g.send('x'),\
跳出到g.send('x')所在的那一行,并將yield返回的值賦值給res1,\
然后繼續(xù)執(zhí)行至結(jié)束。
注意:
print(res1)后面沒(méi)有代碼了,此時(shí)foo()中的food1是空,\
如果print(res1)后面再出現(xiàn)g.send('xx')代碼,\
才會(huì)將'xx'發(fā)送給第二個(gè)yield,并賦值給food1;
但是,foo()內(nèi)部會(huì)從第二個(gè)yield那一行繼續(xù)往下執(zhí)行,\
如果后面沒(méi)有yield關(guān)鍵字了,程序就會(huì)拋出一個(gè)StopIteration異常。
'''
生成器的應(yīng)用:
實(shí)現(xiàn)Linux命令tail -f a.txt | grep 'python'
的功能
import time
def tail(filepath):
with open(filepath,encoding='utf-8') as f:
f.seek(0,2) # 跳到文件末尾
while True:
line=f.readline().strip()
if line:
yield line #3
else:
time.sleep(0.2)
def grep(pattern,lines): #1
for line in lines: #2
if pattern in line: #4
yield line #5
g=grep('python',tail('a.txt'))
print(g)
for i in g: #6
print(i)
程序解析:
#1 pattern = 'python' , lines = tail('a.txt')
#2 此時(shí)的lines是一個(gè)迭代器,經(jīng)過(guò)for循環(huán)會(huì)觸發(fā)tail('a.txt')函數(shù)的執(zhí)行,這時(shí)運(yùn)行tail()函數(shù)內(nèi)的程序,會(huì)通過(guò)while循環(huán)監(jiān)控文件末尾是否有內(nèi)容
#3 如果有新內(nèi)容,則通過(guò)yield返回
#4 第#3通過(guò)yield返回的line傳給#4行的line
#5 這里將line通過(guò)yield返回給#6
#6 這時(shí)i = line