閉包函數html
包含對外的做用域,在內部定義函數,引用自身函數外的函數,python
name ='alex'
def func() :
name ='egon'
def bar() :
print(name) #name屬於外層func(),引用外部的函數變量,先找外面一層的
return bar
b=func()
b()---結果 egon
閉包函數不受定義級別的限制,任何位置均可用,
f._closure_閉合的意思,全部的閉包函數都有這個 屬性
特色:
自帶做用域,本身就帶着一層,因此先找外部做用域,
延遲計算,何時用,何時執行func()
例子:一直又錢:
def f() :
money=100
def tell_infor(name): ----
print('%s have money %s' %(name,money))-----先找tell_infor是否money有值,沒有,再向外找,
tell_infor('egon')
f()
---egon have money 100
總結:想給函數一種狀態,就把函數定義在內部,在這個外部定一個變量,還要不受位置限制,就用return返回,
例子2:
def b():
money=100
def c():
print('egon have money %s' %(money))
return c
c=b()
def foo():
money=1
c()
foo() ----egon have money 100
解析:money在b()已經被定義,因此在foo()再調用從C(時不會從foo()裏去找,
----
閉包函數的基本形式:
def 外部函數名():
內部函數須要的變量
def 內部函數():
引用外部的變量
return 內部函數
----
def deco():
x=1 #x=1就x寫死了,能夠寫在 deco(x)裏,當作參數,
def wrapper():
print(x) #調用外面函的參數
return wrapper
wrapper=deco() #把deco()函數賦給wrapper,wrapper就有了全局做用域
wrapper() #執行wrapper函數
---結果 1
-------閉包函數的應用場景-------------
提取網頁連接,網頁下載
from urllib.request import urlopen ---導入模塊
def url_func(url):
def wrapper():
return urlopen(url).read()
return wrapper
python=url_func('http://www.python.org')
print(python())
--- 結果 b'<!doctype html>\n<!--[if lt IE 7]>
<html class="no-js ie6 lt-ie7 lt-ie8 lt-ie9"> <![endif]-->\n<!--[if IE 7]> ......
============================
軟件開發原則
開發封閉原則
1.對擴展是開發的,增長功能
2.對修改是封閉的,對源代碼,函數,不宜修改,影響全局
=============================================
裝飾器
功能:不修改被裝飾對象的源代碼,以及被裝飾對象的調用方式的前提下,對其增長新功能。
原則:不修改源代碼,不修改調用方式,
目標:增長新功能
例子:在index的基本功能上增長,函數到底運行了多久,就知道了index的網絡延遲
import time #導入時間模塊
import random #導入用於生成隨機數模塊
def timmer(func): #裝飾器timmer.能夠用在index函數,也能夠用在別的函數上
#func=index() 能夠直接當作timmer 的參數,更靈活,
def wrapper():
start_time=time.time() #功能開始時間,
func() #不能寫死,賦給一個變量,index()=func
stop_time=time.time() #功能結束時間
print('run time is %s' %(stop_time-start_time))
return wrapper #必定要返回wrapper值,
@timmer #用語法實現裝飾器的功能,
def index():#index的函數功能是在4秒的時間內顯示出網頁的內容,還想要增長新功能,就要添加新功能,就用到了裝飾器
time.sleep(random.randrange(1,5))
print('welcome to index page')
index()
---結果
welcome to index page #原功能 ,
run time is 1.0044817924499512 #新增裝飾器的功能,函數的運行時間
解析:沒有修改源碼,沒有改變調用方式,增長了新功能,,
------
裝飾器
閱讀: 229372
因爲函數也是一個對象,並且函數對象能夠被賦值給變量,因此,經過變量也能調用該函數。算法
>>> def now(): ... print '2013-12-25' ... >>> f = now >>> f() 2013-12-25
函數對象有一個__name__
屬性,能夠拿到函數的名字:sql
>>> now.__name__ 'now' >>> f.__name__ 'now'
如今,假設咱們要加強now()
函數的功能,好比,在函數調用先後自動打印日誌,但又不但願修改now()
函數的定義,這種在代碼運行期間動態增長功能的方式,稱之爲「裝飾器」(Decorator)。設計模式
本質上,decorator就是一個返回函數的高階函數。因此,咱們要定義一個能打印日誌的decorator,能夠定義以下:緩存
def log(func): def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapper
觀察上面的log
,由於它是一個decorator,因此接受一個函數做爲參數,並返回一個函數。咱們要藉助Python的@語法,把decorator置於函數的定義處:網絡
@log def now(): print '2013-12-25'
調用now()
函數,不只會運行now()
函數自己,還會在運行now()
函數前打印一行日誌:閉包
>>> now()
call now(): 2013-12-25
把@log
放到now()
函數的定義處,至關於執行了語句:app
now = log(now)
因爲log()
是一個decorator,返回一個函數,因此,原來的now()
函數仍然存在,只是如今同名的now變量指向了新的函數,因而調用now()
將執行新函數,即在log()
函數中返回的wrapper()
函數。dom
wrapper()
函數的參數定義是(*args, **kw)
,所以,wrapper()
函數能夠接受任意參數的調用。在wrapper()
函數內,首先打印日誌,再緊接着調用原始函數。
若是decorator自己須要傳入參數,那就須要編寫一個返回decorator的高階函數,寫出來會更復雜。好比,要自定義log的文本:
def log(text): def decorator(func): def wrapper(*args, **kw): print '%s %s():' % (text, func.__name__) return func(*args, **kw) return wrapper return decorator
這個3層嵌套的decorator用法以下:
@log('execute') def now(): print '2013-12-25'
執行結果以下:
>>> now() execute now(): 2013-12-25
和兩層嵌套的decorator相比,3層嵌套的效果是這樣的:
>>> now = log('execute')(now)
咱們來剖析上面的語句,首先執行log('execute')
,返回的是decorator
函數,再調用返回的函數,參數是now
函數,返回值最終是wrapper
函數。
以上兩種decorator的定義都沒有問題,但還差最後一步。由於咱們講了函數也是對象,它有__name__
等屬性,但你去看通過decorator裝飾以後的函數,它們的__name__
已經從原來的'now'
變成了'wrapper'
:
>>> now.__name__ 'wrapper'
由於返回的那個wrapper()
函數名字就是'wrapper'
,因此,須要把原始函數的__name__
等屬性複製到wrapper()
函數中,不然,有些依賴函數簽名的代碼執行就會出錯。
不須要編寫wrapper.__name__ = func.__name__
這樣的代碼,Python內置的functools.wraps
就是幹這個事的,因此,一個完整的decorator的寫法以下:
import functools def log(func): @functools.wraps(func) def wrapper(*args, **kw): print 'call %s():' % func.__name__ return func(*args, **kw) return wrapper
或者針對帶參數的decorator:
import functools def log(text): def decorator(func): @functools.wraps(func) def wrapper(*args, **kw): print '%s %s():' % (text, func.__name__) return func(*args, **kw) return wrapper return decorator
import functools
是導入functools
模塊。模塊的概念稍候講解。如今,只需記住在定義wrapper()
的前面加上@functools.wraps(func)
便可。
小結
在面向對象(OOP)的設計模式中,decorator被稱爲裝飾模式。OOP的裝飾模式須要經過繼承和組合來實現,而Python除了能支持OOP的decorator外,直接從語法層次支持decorator。Python的decorator能夠用函數實現,也能夠用類實現。
decorator能夠加強函數的功能,定義起來雖然有點複雜,但使用起來很是靈活和方便。
請編寫一個decorator,能在函數調用的先後打印出'begin call'
和'end call'
的日誌。
再思考一下可否寫出一個@log
的decorator,使它既支持:
@log def f(): pass
又支持:
@log('execute') def f(): pass
============================
迭代器
1.重複 2.下次的重複基於上次的結果
模擬迭代
while True :
cmd = input('>>:')
print(cmd)# 只重複,不迭代
l=['a','b','c']
count=0
while count < len(l) :
print(l[count])
count+=1 #每次的結果都是基於上次
有序列的數據類型,都有索引
l=['a','b','c']
for count in range(len(l)) :
print(l[count]) #依賴索引求值
#沒有索引取值
d={'a':1,'b':2,'c':3}
for k in d:
print(d[k]) #獲得1,2,3
print(k) #獲得a,b,c
Python提供一種不依賴索引的迭代方式,
給一些對象內置了__iter__方法,只要對象有__iter__稱爲可迭代對象
-------------------------------
s1.__iter__() #字符串是可迭代對象
l=[1,2,3]
l.__iter__() #列表是可迭代對象
t=(1,2,3)
t.__iter__() #元組是可迭代對象
set={1,2,3}
set.__iter__() #集合是可迭代對象
f=open('1.txt',encoding='utf-8')
f.__iter__() #文件是可迭代對象
d={'a':1,'b':2,'c':3}
d.__iter__() #字典是可迭代對象
--------------------------------------------
做用:執行obj.__iter__() 獲得的結果就是迭代器
獲得迭代器既有__iter__() 要獲得值還要用 __next__()方法
d={'a':1,'b':2,'c':3}
a=d.__iter__() #字典是可迭代對象 a就是迭代器
print(a)-----結果 <dict_keyiterator object at 0x00000116709D85E8>
d={'a':1,'b':2,'c':3}
a=d.__iter__() #字典是可迭代對象
print(a.__next__())
print(a.__next__())
print(a.__next__())
結果
a
b
c
迭代器的特色:
1.提供一種不依賴於索引的求值方法
2.惰性計算,什麼時間用,什麼 時間調,節省內存空間
缺點:
1.取值麻煩,要依次按照順序取值
2.迭代只能日後取,不能向前取
3.不能獲取迭代的長度,
for i in list: list是通過了兩步 1. list__iter__() 結果賦值給 i , #生產迭代器
2. i.__next__() 獲得list的每一個值
==============================================================================
關於函數做業:
一:編寫函數,(函數執行的時間是隨機的)
import random
import time
def timmer():
start_time=time.time()
time_sleep=random.randrange
stop_time=time.time()
res=start_time-stop_time
return res
print(res)
二:編寫裝飾器,爲函數加上統計時間的功能,,# 就是說一個功能在一個時間段內啓動到結束的時間,就限定在一個範圍,
import time
import random
def timmer(func):
def wrapper():
start_time=time.time()
func()
stop_time=time.time()
res=stop_time-start_time
print('run time is %s ' %(stop_time-start_time))
return wrapper #不能條括號,加了就是是執行函數,只須要wrapper返回值就能夠
@timmer
def index():
time.sleep(random.randrange(1,4))
print('welcome to index page')
index()
--結果
welcome to index page #index函數功能,
run time is 3.0003855228424072 #統計出函數運行的時
四:編寫裝飾器,爲多個函數加上認證的功能(用戶的帳號密碼來源於文件)
,要求登陸成功一次,後續的函數都無需再輸入用戶名和密碼
注意:從文件中讀出字符串形式的字典,
能夠用eval('{"name":"egon","password":"123"}')轉成字典格式
def auth(func):
def wrapper():
name=input('name:').strip()
pwd=input('pwd:').strip()
filepath=r'C:\Users\lenovo\PycharmProjects\untitled\6.14\file1.txt'
with open(filepath,'rb') as f :
dic_user=eval(f.read()) #eval把文件轉換爲字典
#for i in dic_user:
if name in dic_user or pwd == dic_user[name] :
print('ok')
func()
else:
print('err')
return wrapper
@auth
def index() :
print('welcome to index page')
index()
五:編寫下載網頁內容的函數,要求功能是:用戶傳入一個url,函數返回下載頁面的結果
from urllib.request import urlopen
def index(url):
return urlopen(url).read()
print(index('https://www.baidu.com/'))
結果:
b'<html>\r\n<head>\r\n\t<script>\r\n\t\tlocation.replace
(location.href.replace("https://","http://"));\r\n\t</s
六:爲題目五編寫裝飾器,實現緩存網頁內容的功能:
# 具體:實現下載的頁面存放於文件中,若是文件內有值
# (文件大小不爲0),就優先從文件中讀取網頁內容,不然,就去下載,而後存到文件中
from urllib.request import urlopen
import os
cache_file=r'C:\Users\lenovo\PycharmProjects\untitled\6.14\cache.txt'
def make_cache(func):
def wrapper(*args,**kwargs):
with open(cache_file,'rb') as f :
if os.path.getsize(cache_file) : #判斷文件大小
res=f.read()
else:
res=func(*args,**kwargs) #func()就是get()函數,無論get傳入什麼,都能接受,
with open(cache_file,'wb') as f :# 把寫的方式打開文件,
res=f.write(res) #把寫的文件返回
return res
return wrapper
@make_cache
def get(url):
return urlopen(url).read()
print(get('http://www.baidu.com'))
---結果--- b'<html>\r\n<head>\r\n\t<script>\r\n\t\tlocation.replace(location.href.replace("https://",........
cache.TXT文件內容
![](http://static.javashuo.com/static/loading.gif)
===========
# 基於上題所講網頁緩存裝飾器的基礎上,實現緩存不一樣網頁的功能
# 要求,用戶提交的不一樣url,都能緩存下來,對相同的url發起下載請求,優先從緩存裏取內容
# 分析:
#1,判斷網頁是否在有緩存,有緩存,就從文件裏讀,沒有緩存,就下載,
#2.沒有緩存,不一樣的網頁根據他的哈希值來判斷,存到哪一個文件夾
未完,待續
===========================
生成器(generator)
先定義概念,生成器就是迭代器,也有next() 方法,
函數體內包含yield的關鍵字,該函數的執行結果就是生成器,
iteration 迭代器
generation 生成器
yield至關於return,一次只能返回一次值,
爲何要有生成器?
經過列表生成式,咱們能夠直接建立一個列表。可是,受到內存限制,列表容量確定是有限的。並且,建立一個包含100萬個元素的列表,不只佔用很大的存儲空間,若是咱們僅僅須要訪問前面幾個元素,那後面絕大多數元素佔用的空間都白白浪費了。
因此,若是列表元素能夠按照某種算法推算出來,那咱們是否能夠在循環的過程當中不斷推算出後續的元素呢?這樣就沒必要建立完整的list,從而節省大量的空間。在Python中,這種一邊循環一邊計算的機制,稱爲生成器(Generator)。
簡單生成器
要建立一個generator,有不少種方法。第一種方法很簡單,只要把一個列表生成式的[]改爲(),就建立了一個generator:
>>> L = [x * x for x in range(10)]
>>> L
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
>>> g = (x * x for x in range(10))
>>> g
<generator object <genexpr> at 0x104feab40>
簡單生成器
要建立一個generator,有不少種方法。第一種方法很簡單,只要把一個列表生成式的[]改爲(),就建立了一個generator:yield至關於把 .__iter__()和 .__next__()爲函數封裝好遵循迭代器的取值方式,obj.__next__(),觸發函數的執行,函數的暫停與再繼續狀態都有yield保存。用yield 生成斐波那契數據類型(100內)def foo(): a = b = 1 yield a yield b while 1: a,b = b,a+b yield bfor num in foo(): if num > 100: break print(num)foo() #1 1 2 3 5 8 13 21 34 55 89