python學習第七周個人總結

LEGB、遞歸函數、匿名函數、生成器函數、生成器應用、高階函數、柯里化、裝飾器、類型注解、functools.個人總結,加深印象。

1

1、LEGB的概念。

local,本地作用域、局部作用域的local命名空間。函數作用域的local命名空間。函數調用時創建,調用結束時候消亡。

Enclosing:嵌套函數外層函數的命名空間。

Global:全局作用域。一個模塊的命名空間.模塊被import時候創建,解釋器退出時消亡。

Bulid-in。內置模塊的命名空間,生命周期從python解釋器啟動時創建到解釋器退出時消亡。例如print(open)都是內置的變量。

 

所以一個名詞的查找順序就是LEGB。(從本級逐級向外找)。

2、遞歸函數。

1)函數執行流程。(調用函數,保存當前的內容,壓棧函數并創建棧幀。執行里面的語句)

全局幀中生成foo1、foo2、foo3、main的函數對象。(棧,先進后出,后進先出)。

main函數調用

main 中查找內建函數print壓棧,將常量字符串壓棧,調用函數,彈出棧頂。

main中全局函數foo1壓棧,將常量100,101壓棧,調用函數foo1,創建棧幀。Print函數壓棧,字符串和變量b、b1壓棧,調用函數,彈出棧幀,返回值。

Main中全局查找foo2函數壓棧,將常量200壓棧,調用foo2,創建棧幀。foo3函數壓棧,變量c引用壓棧,調用foo3,創建棧幀。Foo3完成print函數調用后返回。foo2回復調用,執行print后,返回值。Main中foo2調用結束彈出棧頂,main函數繼續執行print函數調用,彈出棧頂,main函數返回。

2)遞歸:函數直接或者間接調用自身就是遞歸。

遞歸需要有邊界條件、遞歸前進段、遞歸返回段。

遞歸一定要有邊界條件。

當邊界不滿足的時候遞歸前進。

當邊界條件滿足的時候,遞歸返回。

斐波那契數列:

pre = 0
cur = 1
print(pre,cur,end = ‘ ‘)
n=4
for i in range(n-1):
pre,cur = cur ,pre + cur
print(cur,end=’ ‘)

 

def fib(n):
return 1 if n<2 else fib(n-1)+fib(n-2)
for i in range(5):
print(fib(i),end=’ ‘)

3)遞歸要求

遞歸一定要有退出條件,遞歸調用一定要執行到這個退出條件,沒有退出條件的遞歸調用,就是無限調用。

遞歸調用的深度不宜過深。

Python中對遞歸調用的深度做了限制,以保護解釋器。

超過遞歸調用深度,會拋出異常的。

4)遞歸的性能

循環稍微復雜一些,但是只要不是死循環,可以多次迭代直至算到結果。

改進。左邊的fib函數和循環的思想類似。

參數n是邊界條件,用n來計數。

上一次的計算結果作為下一次結果的實參。

import datetime
n=35
start = datetime.datetime.now()
def fib(n):
return 1 if n<2 else fib(n-1)+fib(n-1)

for i in range(n):
print(fib(i),end=”)
delta = (datetime.datetime.now()-start).total_seconds()
print(delta)

效率比較低。

pre = 0
cur = 1
print(pre,cur,end=’ ‘)
def fib(n,pre=0,cur=1):
pre,cur = cur,pre + cur
print(cur,end=’ ‘)
if n == 2:
return
fib(n-1,pre,cur)
print(fib(5))

#斐波那契數列改進方式

左邊的fib函數和循環的思想類似

參數n是邊界條件,用n來計數。

上一次的計算結果作為函數的實參。

效率很高

和循環相比,性能相近,所以說遞歸效率不一定很低。但是深度有限。

5)間接遞歸

def ?foo1():

foo2()

 

def ?foo2():

f1oo()

foo1()

是通過別的函數調用了函數本身。

但是,如果構成了循環遞歸調用時非常危險。

6)遞歸總結

是一種很自然的表達,符合邏輯思維。

相對運行效率較低,每次調用函數都要開辟棧幀

遞歸有深度限制,如果遞歸層次太深,函數反復壓棧,棧內存很快就溢出了。

如果有限次數的遞歸,可以使用遞歸調用,或者使用循環替代,循環代碼稍微復雜,但是只要不是死循環,可以多次迭代直至算出結果。

絕大多數遞歸,都可以使用循環實現。

即使代碼很簡單,但是能不用則不用遞歸。

7)遞歸練習題

階乘:def fn1(n):
if n==1:
return 1
return n*fn1(n-1)
print(fn1(5))

倒向打?。?/p>

f1 = str(1234)
def fn4(n):
if n == -1:
return ”
else:
return f1[n]+fn4(n-1)
print(fn4(len(f1)-1))

解決猴子摘桃

def peach(day=9,sum=1):
sum = 2*(sum+1)
day -=1
if day ==0:
return sum
return peach(day,sum)
peach()

斐波那契數列:

def fib(n):
    if n<3:
        return 1
    else:
        return fib(n-1)+fib(n-1)
    
print(fib(5))

3、匿名函數

匿名:就是沒有名字。

匿名函數,即沒有名字的函數

沒有名字如何定義,如何調用,如果能調用,如何使用。

Python借助lambda表達式構建匿名函數。

格式:

Lambda參數列表:表達式

(Lambda x:x**2)(4)

#foo = lambda x:x+1

foo(1)

上下式等同:

#def foo(x):

return x+1

foo(1)

使用關鍵字來定義匿名函數:

參數列表不需要小括號

冒號是分隔參數和表達式的

不需要return ,表達式的值就是匿名函數的返回值。

只能寫在一行上面,被稱為單行函數。

用途:在高階函數傳參時候,使用lambda表達式,往往能簡化代碼.

 

print((lambda x,y=3:x+y)(5))

print((lambda :0)())

print((lambda x,y=3:x+y)(5,6))

print((lambda x,*,y=30:x+y)(5))#y 使用的是缺省值。

print((lambda *args:(x for x in args))(*range(5)))

print((lambda *args:[x+1 for x in args])(*range(5)))

[1, 2, 3, 4, 5]

print((lambda *args:{x+1 for x in args})(*range(5)))

[(1, (0, 1, 2, 3, 4)),

(2, (0, 1, 2, 3, 4)),

(3, (0, 1, 2, 3, 4)),

(4, (0, 1, 2, 3, 4)),

(5, (0, 1, 2, 3, 4))]

 

4、生成器函數。

1)生成器指的是生成器對象,可以有生成器表達式得到,也可以使用yield關鍵字得到一個生成器函數,調用這個函數得到一個生成器對象。(只要有yield就是生成器函數,調用的時候返回的就是生成器對象,一般都是放在for 循環后面的。) ??與之對應的 ???(預熱,預加載,一次性返回所有的值,緩存。)

生成器函數:

函數體內包括yield語句的函數,返回生成器對象,

生成器對象,是一個可迭代對象,是一個迭代器。

生成器對象,是延遲計算、惰性求值。

2)def inc():
for i in range(5):
yield i
print(type(inc()))
print(type(inc()))
x = inc()
print(type(inc()))
print(type(inc()))
for m in x:
print(m,’*’)
for m in x:
print(m,’*’)

普通函數的調用fn(),函數會立即執行完畢,但是生成器函數可以使用next函數進行多次執行。

生成器函數等價于生成器表達式,只不過生成器函數可以更加的復雜。

 

 

def gen():
print(‘line1’)
yield 1
print(‘line2’)
yield 2
print(‘line3′)
return ?3

next(gen())
next(gen())
g=gen()
print(next(g))
print(next(g))
print(next(g))
print(next(g))
print(next(g),’end’)

 

3)在生成器函數中,使用多個yield語句,執行一次后暫停執行,把yield表達式的值返回。

再次執行會執行下一個yield語句。

Return語句依然可以終止函數,單return語句的返回值不會被獲取的到。

Return會導致無法繼續獲取下一個值,拋出stopiteration異常。

如果函數沒有顯示的return語句,如果生產器函數執行到結尾,一樣會拋出異常的。

4)生成器函數總結:

包含yield語句的生成器函數生成生成器對象的時候,生成器函數不會立即被執行。

Next(generator)會從函數的當前位置向后執行到碰到的第一個yield語句,會彈出值,并暫停函數執行。

再次調用next函數,和上一條一樣的處理過程。

沒有多余的yield語句能被執行,繼續調用next函數,會拋出異常的。stopiteration

Generator生成器對象。

5、生成器應用;

1)def counter():
i = 0
while True:
i += 1
yield i
def inc(c):
return next(c)
c = counter()
print(inc(c))
print(inc(c))

返回結果為 1,2 因為給counter賦值c了,每次重新調用的時候提前使用

def counter():
i = 0
while True:
i ?+=1
yield i
def inc():
c= counter()
return next(c)
print(inc())
print(inc())
print(inc())

返回結果為1,1.print的時候是三個不同的對象。

def counter():
i = 0
while True:
i ?+=1
yield i
def inc():
c= counter()
return next(c)
print(inc())
print(inc())
print(inc())

def inn():
def counter():
i = 0
while True:
i +=1
yield i
c = counter()
return lambda:next(c)
foo = inc()
print(foo())
print(foo())

上式和下式是等同的。

2)協程coroutine

生成器的高級用法

比進程、線程輕量級。 ??是在用戶空間調度函數的一種實現。

Python3 asyncio就是協程實現,已經加入到標準庫。

Python3.5中使用async,await關鍵字職業原生支持協程。

協程調度器實現思路

有兩個生成器a和b

Next(A)后,A執行到了yield語句暫停,然后執行next(B),b執行到yield語句也暫停,然后再次調用next(a),在調用next(B),周而復始,就實現了調度的效果。

可以引用調度的策略來實現切換方式。

協程是一種非搶占式調度。

3)yield from:

For x in range(100)

Yield x

等價于

yield from range(100)

 

def inc():
for x??in range(1000):
yield x

foo = inc()
print(next(foo))
print(next(foo))
print(next(foo))

def inc():
yield from range(1000)

foo = inc()
print(next(foo))
print(next(foo))
print(next(foo))

yield from 是python3.3出現的新的語法。

yield from iterable是for item in iterable:yield item形式的語法糖。

從迭代對象中一個個拿元素。

def counter(n):

for x in range(n):

yield x

def inc(n)

6、高階函數、柯里化。

1)First class object

函數在python中是一等公民

函數是對象,可調用的對象。

函數可以作為普通變量、參數、返回值等。

2)高階函數

數學概念,y=g(f(x))

高階函數滿足條件;(1)接受一個或者多個函數作為參數.(2)輸出一個函數。

加括號和不加括號概念是完全不一樣的。

3)高階函數的事例

#def counter(base):

def inc(step=1):

nonlocal base

base +=step

return base

return inc

c=counter(10)

c1 = counter(10)

print(c())

print(c1())

高階函數。

如果 c 不等于c1 也不是 is ?and == ?為假。

如果 c()等于c1() ???is and ?== 是真值。

 

c is c1

False

 

c ==c1

False

 

c() is c1()

True

 

c() ==c1()

True

4)自定義sort函數。

#初步代碼

def sort(iterable,reverse = False,key = None ):

lst = []

for x in iterable:

for i,y in enumerate(lst):

if x>y:

lst.insert(i,x)

break

else:

lst.append(x)

return lst

 

sort([1,5,6,7,4,3,2,9,8,0])

#out

[9, 8, 7, 6, 5, 4, 3, 2, 1, 0]

#第二步優化:

def sort(iterable,reverse = False,key = lambda x,y:x>y ):

lst = []

for x in iterable:

for i,y in enumerate(lst):

#flag = True if x>y else False

if key(x,y):

lst.insert(i,x)

break

else:

lst.append(x)

return lst

 

sort([1,5,6,7,4,3,2,9,8,0])

#第三步

def sort(iterable,reverse = False,key = lambda x,y:x>y ):

lst = []

for x in iterable:

for i,y in enumerate(lst):

#flag = True if x>y else False

if key(x,y):

lst.insert(i,x)

break

else:

lst.append(x)

return lst

 

sort([1,5,6,7,4,3,2,9,8,0],key = lambda x,y:x<y)

#第四步

def sort(iterable,reverse = False,key = lambda x,y:x>y ):

lst = []

for x in iterable:

for i,y in enumerate(lst):

flag = key(x,y) if not reverse else not key(x,y)

if flag:

lst.insert(i,x)

break

else:

lst.append(x)

return lst

 

sort([1,5,6,7,4,3,2,9,8,0],key = lambda x,y:x<y)

利用索引,i,x這些。。

5)內建函數-高階函數。

sorted(iterable[,key][,reverse])

排序

(1)sorted(iterable[,key][,reverse]) 返回一個新的列表,對一個可迭代對象的所有元素進行排序,按照key定義的函數。reverse表示是否反轉

Sorted(lst,key=lambda x: 6-1) 返回新的列表

List.sort(key=lambda x:6-1)就地修改。

(2)filter(function,iterable)

過濾函數

過濾可迭代對象的元素,返回一個迭代器

function一個具有一個參數的函數,返回bool。

#list(filter(lambda x: x%3==0,[1,9,55,150,-3,78,28,123]))

#out ??[9, 150, -3, 78, 123]

(3)map(func,*iterables)—map object

映射

對多個可迭代對象的元素按照指定的函數進行映射,返回一個迭代器。

#list(map(lambda x:2*x+1,range(5)))

#out [1, 3, 5, 7, 9]

#dict(map(lambda x:(x%5,x),range(500)))

#{0: 495, 1: 496, 2: 497, 3: 498, 4: 499}

(4)filter(function or None,iterable)

(5)map函數一個參數的函數。后面假定送進來的是一個元素的對象。

一般送進去的參數都是使用lambda。 ??lambda使用的是一個參數。

map后面的可迭代元素后面只能出現一個元素。

dict(map(lambda x,y: (x,y),zip(‘abcdfe’,range(5))))

這種表達式是錯誤的,前面的形參是兩個,但是后面的實參卻是二元組,條件不滿足。

7、柯里化:

1)柯里化:指的是將原來接受兩個元素的函數,變成接受一個參數的函數的過程,新的函數返回一個以原有第二個參數的函數。z=f(x,y) ?z=f(x)(y)

#例子

def add(x):

def _add(y):

return x+y

return _add

t=add(4)(5)

print(t)

 

 

通過嵌套函數就可以把函數轉換為柯里化函數。

8、裝飾器。

1)print打印語句耦合太高,輸出信息的功能,屬于非業務代碼。靈活度太差。

print在業務代碼中是屬于侵入式代碼。

2)裝飾器

#第一步代碼

def add(x,y):

return x+y

 

def logger(fn):

print(‘before’)

ret=fn(4,5)

print(‘end’)

return ret

 

print(logger(add))

#第二步多個參數優化

def add(x,y):

return x+y

 

def add1(x,y,*,z=6):

return x+y+z

 

def logger(fn,*args,**kwargs):

print(‘before’)

ret=fn(*args,**kwargs)

print(‘end’)

return ret

a=logger(add1,4,5,z=7)

print(a)

#第三步

def add(x,y):

return x+y

 

def add1(x,y,*,z=6):

return x+y+z

 

def logger(fn):

def _logger(*args,**kwargs):

print(‘before’)

ret=fn(*args,**kwargs) ??#調用add1

print(‘end’)

return ret

return _logger

add1=logger(add1) ????#內層函數_logger

a=add1(4,5,z=7) ????#logger(add1,4,5,z=7)

print(a)

#第四步

def add(x,y):

return x+y

 

def add1(x,y,*,z=6):

return x+y+z

 

def logger(fn):

def _logger(*args,**kwargs):

print(‘before’)

ret=fn(*args,**kwargs) ??#調用add1

print(‘end’)

return ret

return _logger

#add1=logger(add1) ????#內層函數_logger

a=logger(add1)(4,5,z=7) ????#logger(add1,4,5,z=7)

print(a)

#第五步

def add(x,y):

return x+y

 

def logger(fn):

def _logger(*args,**kwargs):

print(‘before’)

ret=fn(*args,**kwargs) ??#調用add1

print(‘end’)

return ret

return _logger

 

@logger

def add1(x,y,*,z=6): ??#add1 = logger(add1)

return x+y+z

 

#add1=logger(add1) ????#內層函數_logger

a=add1(4,5,z=7) ????#logger(add1,4,5,z=7)

print(a)

#第六步包裝函數wrapper

def logger(fn):
def wrapper(*args,**kwargs):
print(‘before’)
ret = fn(*args,**kwargs)
print(‘end’)
return ret
return wrapper
@logger
def add(x,y,*,z=6):
return x+y+z
@logger
def add1(x,y):???#add=logger(add)
return x+y

#b=logger(add)
a=add(4,5,z=10)
print(a)

 

 

 

 

 

 

 

3)@logger的語法格式。

裝飾器(無參)

他是一個函數,函數作為他的形參。

返回的值也是一個函數。

可以使用@function調用。

Wrapper表示被包裝函數。

Wrapperd表示包裝函數。

4)裝飾器和高階函數。

裝飾器是高階函數,但是裝飾器是對傳入的函數的功能進行裝飾,功能增強。

#裝飾器

增強函數,而不是非侵入式函數。把函數名作為其參數。

import datetime

import time

 

def logger(fn):

def wrapper(*args,**kwargs):

print(“args={},kwargs={}”.format(args,kwargs))

start = datetime.datetime.now()

ret = fn(*args,**kwargs)

duration = datetime.datetime.now() – start

print(“function{}took{}s.”.format(fn.__name__,duration.total_seconds()))

return ret

return wrapper

@logger ?#add = logger(add)

def add(x,y):

print(“===call add =======”)

time.sleep(2)

return x+y

 

print(add(4,y=1))

 

裝飾器:裝飾器函數,前置功能增強,被增強函數,后置功能增強。

#import datetime
import time

def logger(fn):
def wrapper(*args,**kwargs):
print(“{}{}”.format(args,kwargs))
start = datetime.datetime.now()
n = fn(*args,**kwargs)
duration=datetime.datetime.now() – start
print(“function{}took{}s”.format(fn.__name__,duration.total_seconds()))
return n
return wrapper

@logger
def add(x,y):
print(“====call add =======”)
time.sleep(2)
return x+y

b=add(3,y=4)
print(b)

9、文檔字符串。

1)Python是文檔字符串。Documentation Strings。

在函數語句塊的第一行,且習慣是多行的文本,所以多行使用三引號。

慣例是首字母大寫。第一行寫概述,空一行,第三行寫詳細描述,

可以使用特殊屬性__do__ 訪問這個文檔。

必須寫在第一行。

#

def add(x,y):

“””This is s function of addition””” ?????????????文檔字符串。

a=x+y

return x+y

print(“name={} \n doc={}”.format(add.__name__,add.__doc__))

print(help(add))

name=add

doc=This is s function of addition

Help on function add in module __main__:

 

add(x, y)

This is s function of addition

 

None

2)存在副作用,因為原函數對象的屬性都被替換了。

3)#第一次代碼

import datetime

import time

 

def copy_properties(src,dest):

dest.__name__ = src.__name__

dest.__doc__ = src.__doc__

def logger(fn):

def wrapper(*args,**kwargs):

print(“args={},kwargs={}”.format(args,kwargs))

start = datetime.datetime.now()

ret = fn(*args,**kwargs)

duration = datetime.datetime.now() – start

print(“function{}took{}s.”.format(fn.__name__,duration.total_seconds()))

return ret

copy_properties(fn,wrapper)

return wrapper

@logger ?#add = logger(add)

def add(x,y):

“””this is a add function”””

print(“===call add =======”)

time.sleep(2)

return x+y

 

print(add(4,y=1),add.__name__,add.__doc__)

#第二次代碼(帶參裝飾器)

import datetime

import time

 

def copy_properties(src):

def _inner(dest):

dest.__name__ = src.__name__

dest.__doc__ = src.__doc__

return dest

return _inner

 

def logger(fn):

@copy_properties(fn)

def wrapper(*args,**kwargs):

“””I am wrapper”””

print(“args={},kwargs={}”.format(args,kwargs))

start = datetime.datetime.now()

ret = fn(*args,**kwargs)

duration = datetime.datetime.now() – start

print(“function{}took{}s.”.format(fn.__name__,duration.total_seconds()))

return ret

# copy_properties(fn,wrapper)

return wrapper

@logger ?#add = logger(add)

def add(x,y):

“””this is a add function”””

print(“===call add =======”)

time.sleep(2)

return x+y

 

print(add(4,y=1),add.__name__,add.__doc__)

 

標示符和名稱并不是一一對應的。

3)帶參裝飾器

通過copy_properties函數將被包裝函數的屬性覆蓋掉包裝函數。

凡是被裝飾的函數都需要復制這些屬性,這個函數很通用。

可以將復制屬性的函數構建成裝飾器函數,帶參裝飾器。(帶參裝飾器進行柯里化)。

本質:裝飾器函數,裝飾別的函數,增強別的(業務函數)函數功能。

#帶參裝飾器代碼

import datetime

import time

 

def copy_properties(src):

def _inner(dest):

dest.__name__ = src.__name__

dest.__doc__ = src.__doc__

return dest

return _inner

def logger(durtion)

def _logger(fn):

@copy_properties(fn)

def wrapper(*args,**kwargs):

“””I am wrapper”””

print(“args={},kwargs={}”.format(args,kwargs))

start = datetime.datetime.now()

ret = fn(*args,**kwargs)

delta = (datetime.datetime.now() – start).total_seconds()

print(duration)

if delta>duration:

print(‘low’)

else:

print(‘fast’)

 

#print(“function{}took{}s.”.format(fn.__name__,duration.total_seconds()))

return ret

# copy_properties(fn,wrapper)

return wrapper

return _logger

@logger(5) ?#add = logger(add)

def add(x,y):

“””this is a add function”””

print(“===call add =======”)

time.sleep(2)

return x+y

 

print(add(4,y=1),add.__name__,add.__doc__)

  • 帶參裝飾器:是一個函數。函數作為他的形參,返回值是一個不帶參的裝飾器函數。

使用@functionname(參數列表)方式調用,

可以看做是在裝飾器外層又加了一層函數。

函數作為形參。

10、functools模塊。

Functools.update_wrapper(wrapper,wrappered,assigned=WRAPPER_ASSIGNMENTS,updated=WRAPPER_WPDATES)

類似copy_properties功能

Wrapper包裝函數、被更新者,wrapper被包裝函數、數據源。

元組WRAPPER_ASSIGNMENTS中是要被覆蓋的屬性。

‘__module__’, ‘__name__’, ‘__qualname__’, ‘__doc__’, ‘__annotations__’

模塊名、名稱、限定名、文檔、參數注解

元組WRAPPER_UPDATES中是要被更新的屬性,__dict__屬性字典。

增加一個__wrapped__屬性,保留著wrapped函數。

#代碼塊

import functools,time,datetime

def logger(durtion,func=lambda neme,durtion:print(‘{}took{}s’.format(name,durtion))):

def _logger(fn):

def wrapper(*args,**kwargs):

start = datetime.datetime.now()

n = fn(*args,**kwargs)

delta = (datetime.datetime.now() – start).total_seconds()

if delta > durtion:

func(fn.__name__,duration)

return n

return functools.update_wrapper(wrapper,fn)

return _logger

@logger(5) ??#add = logger(5)(add)

def add(x,y):

time.sleep(1)

return x+y

print(add(5,6),add.__name__,add.__wrapped__,add.__dict__,sep=’\n’)

11、類型注解

1)函數定義的弊端。

Python是動態語言,變量可以被賦值,且賦值為不同類型。

難發現,由于不做任何類型檢查,直到運行期問題才顯現出來,或者線上運行才可以發現。

難使用:函數的使用者看到這個函數的時候,并不知道其函數設計,并不知道應該傳入什么類型的參數。

2)如何解決弊端

(1)增加文檔注釋。(弊端:函數定義更新了,文檔未必同步更新).使用雙的三引號。

(2)函數注解:只是一個提示性文件。對函數參數進行類型注解。對函數參數做一個輔助的說明,并不對函數參數進行類型檢查。第三方工具,做代碼分析,發現隱藏的bug。

.__annotations__。

 

def add(x,y):
“””
????:param?x:
????:param?y:
????:return: int
????“””
????return x+y
print(help(add))

 

 

(3)3.6.3進行的變量注解。I:int = 3

3)業務應用;函數參數類型檢查

(1)函數參數檢查,一定是在函數外。

函數應該作為參數,傳入到檢查函數中。

檢查函數拿到函數傳入的實際參數。與形參聲明進行對比。

__annotations__屬性是一個字典,其中包括返回值類型的聲明使用inspect模塊。

  • inspect。提供獲取對象信息的函數,可以檢查函數的類、類型檢查。

import inspect
def add(x:int,y:int,*args,**kwargs):
return x+y
sig = inspect.signature(add)
print(sig,type(sig))
print(‘params:’,sig.parameters)
print(‘return:’,sig.return_annotation)
print(sig.parameters[‘y’],type(sig.parameters[‘y’]))
print(sig.parameters[‘x’])
print(sig.parameters[‘args’])
print(sig.parameters[‘args’].annotation)
print(sig.parameters[‘kwargs’])
print(sig.parameters[‘kwargs’].annotation)

#第一個print:(x:int, y:int, *args, **kwargs) <class ‘inspect.Signature’> ???sig表現出來的是對象,對象是signature類型

#第二個print:params: OrderedDict([(‘x’, <Parameter “x:int”>), (‘y’, <Parameter “y:int”>), (‘args’, <Parameter “*args”>), (‘kwargs’, <Parameter “**kwargs”>)]) ?類型是映射代理 ,返回的是有序的字典。Orderdict。

#第三個print:return: <class ‘inspect._empty’> ?通過__annotation查看的是聲明的類型。

#第四個print:y:int <class ‘inspect.Parameter’> ??返回的是一個類

#第五個print:x:int ?返回的是一個類

#第六個print:*args ??返回的是一個類

#第七個print:<class ‘inspect._empty’> ?表示聲明的類型,未聲明表示空

#第八個print:**kwargs ???返回的是一個類

#第九個print:<class ‘inspect._empty’> ?表示聲明的類型,未聲明表示空

4)模塊提供的信息:

Signature(callable)獲取簽名,(函數簽名包含了一個函數的信息包括函數名、參數類型,所在的類和名稱空間及其其他信息)

Def add(x,y): ??add(x,y)就是函數的簽名:

  • signature(callable,*,follow_wrapped=True)
  • Params=sig.parameters()

 

 

可變類型收集的就是不同類型數據的,所有后面沒有必要加類型注解。

5)模塊提供的信息

inspect.isfunction(add)?????#是否是函數
inspect.ismethod(add)????????????#是否是類方法
inspect.isgenerator(add)?????????#是否是生成器對象
inspect.isgeneratorfunction(add)????#是否是生成器函數
inspect.isclass(add)????????????#是否是類
inspect.ismodule(inspect)???????#是否是模塊
inspect.isbuiltin(print)????????#是否是內建對象

 

 

6)Parameter對象

保存在元組中,只是只讀的。

name參數的名字。

annotaion參數的注解,可能沒有定義。

Default參數的缺省值,可能沒有定義。

empty,特殊得類,用來標記default屬性或者注釋annotation屬性的空值。

Kind實參如何綁定到形參,就是形參的類型。

POSITIONAL_ONLY ,值必須是位置參數提供

POSITIONAL_OR_KEYWORD,值可以作為關鍵字或者位置參數提供。

VAR_POSITIONAL,可變位置參數,對應*args

KEYWORD_ONLY,keyword-noly參數,對應*或者*args之后出現的非可變關鍵字參數。

VAR_KEYWORD,可變關鍵字參數,對應**kwargs。

7)#課堂例子:

import inspect
def add(x,y:int=1,*args,z,t=10,**kwargs):
return x+y
sig = inspect.signature(add)
print(sig)
print(‘params:’,sig.parameters)
print(‘return:’,sig.return_annotation)
print(‘~~~~~~’)
for i,item in enumerate(sig.parameters.items()):
name,param = item
print(i+1,name,param.annotation,param.kind,param.default)
print(param.default is param.empty,end=’\n\n’)

(x, y:int=1, *args, z, t=10, **kwargs)

params: OrderedDict([(‘x’, <Parameter “x”>), (‘y’, <Parameter “y:int=1”>), (‘args’, <Parameter “*args”>), (‘z’, <Parameter “z”>), (‘t’, <Parameter “t=10”>), (‘kwargs’, <Parameter “**kwargs”>)])

return: <class ‘inspect._empty’>

~~~~~~

1 x <class ‘inspect._empty’> POSITIONAL_OR_KEYWORD <class ‘inspect._empty’>

True

 

2 y <class ‘int’> POSITIONAL_OR_KEYWORD 1

False

 

3 args <class ‘inspect._empty’> VAR_POSITIONAL <class ‘inspect._empty’>

True

 

4 z <class ‘inspect._empty’> KEYWORD_ONLY <class ‘inspect._empty’>

True

 

5 t <class ‘inspect._empty’> KEYWORD_ONLY 10

False

 

6 kwargs <class ‘inspect._empty’> VAR_KEYWORD <class ‘inspect._empty’>

True

8)業務應用

有函數如下

def add(x, y:int=7) -> int:

return x + y

請檢查用戶輸入是否符合參數注解的要求

#第一步代碼:解決位置參數(傳入的參數為位置參數)

思路:1,為了不侵入原來代碼,所以使用,裝飾器。

2,導入inspect模塊。

3,利用sig獲取函數簽名(add)

4,利用sig.paramters(fn)獲取對象,是一個有序的字典。

5,字典里面的k放在字典里面,為list(params.keys())。

6,字典里面的v值放在字典里面,作為找一個列表,list(paramts.values())

7,用戶輸入的值利用迭代器取出,判斷用戶輸入的values是否和定義的類型一樣。

8,如果為真,打印。

 

import inspect

def check(fn):
#@funtools.wraps(fn)
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters ??#有序字典
#keys = [x for x in params.keys()]
keys = list(params.keys())
values = list(params.values()) ??#參數對象列表
for i,val in enumerate(args):
if isinstance(val,values[i].annotation):
print(keys[i],’==’,val)
#n = fn(*args,**kwargs)
return fn(*args,**kwargs)
return wrapper

@check
def add(x:int,y:int=7)->int:
return x + y
print(add(4,5))

#第二步代碼,解決關鍵詞參數傳參。(傳入的實參采用關鍵字)

思路:1,迭代,k,v inkwargs.Items()迭代傳參后的字典。

2,判斷類型v的類型,與簽名得到的有序字典的類型判斷是否一致。

3,類型一致就進行打印。

def check(fn):
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters ????#有序字典
keys = list(params.keys()) ??#定義的
values = list(params.values())# 定義的
for i,val in enumerate(args): ??#形參和實參聲明一致
if isinstance(val,values[i].annotation):
print(keys[i],’==’,val)
for k,v in kwargs.items():
if isinstance(v,params[k].annotation):
print(k,’====’,v)
return fn (*args,**kwargs)
return wrapper

@check
def add(x:int,y:int=1):
return x+y
print(add(3,y=4))

#第三步,解決傳參為位置參數和關鍵字參數混合傳參。

import inspect
def check(fn):
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters
keys = list(params.keys())
values = list(params.values())
for i,val in enumerate(args):
if isinstance(val,values[i].annotation):
print(keys[i],’==’,val)
for k,v in kwargs.items(): ???#迭代定義的傳參后的字典,
if isinstance(v,params[k].annotation): ?#判斷傳參后的v與定義的比較。
print(k,’===’,v)
return fn(*args,**kwargs)
return wrapper

@check
def add(x:int,y:int=1):
return x+y
print(add(3,y=4))

#第四步;解決沒有注解的不進行比較的問題:

思路:采用短路與解決,如果不是定義類型的,就不會進行下一步比較、

import inspect
def check(fn):
def wrapper(*args,**kwargs):
sig = inspect.signature(fn)
params = sig.parameters
keys = list(params.keys())
values = list(params.values())
for i,val in enumerate(args):
if values[i].annotation is not inspect._empty ??and isinstance(val,values[i].annotation):
print(keys[i],’==’,val)
for k,v in kwargs.items(): ???#迭代定義的傳參后的字典,
if params[k].annotation is not params[k].empty ?and ??isinstance(v,params[k].annotation): ?#判斷傳參后的v與定義的比較。
print(k,’===’,v)
return fn(*args,**kwargs)
return wrapper

@check
def add(x:int,y:int=1):
return x+y
print(add(3,y=4))

 

 

 

 

Inpsect模塊主要是做檢查,檢查函數,缺省值等。

12、functools模塊

1)Partial方法

2)偏函數,把函數部分的參數固定下來,相當于為部分參數添加了一個固定的默認值,形成一個新的函數返回。

3)一定返回的是新的函數,原來的函數包在內了。

(1)例題

from functools import partial

def add(x,y):
return x +y

newadd = partial(add,4)
print(newadd(5))

例題,把x固定了。

from functools import partial
import inspect
def add(x,y):
return x +y

newadd = partial(add,x=4)
print(newadd(y=5))
print(inspect.signature(newadd))

#打印出的:

9

(*,x=4, y)

根據簽名得到,被固定的參數的前面增加,*,

#課堂例子:

import functools
def add(x,y):
return x+y

newadd = functools.partial(add,y=5)

print(newadd(7))
print(newadd(7,y=6))
print(newadd(y=10,x=6))

import inspect
print(inspect.signature((newadd)))

#12

13

16

(x, *, y=5)

被固定的是add的函數的y的值,所以簽名是(x,*,y=5),所以傳參的方式x使用位置或者關鍵字均可以,y可以使用關鍵字傳參,不傳參的話可以使用默認值。

例題:

import functools
def add(x,y,*args):
print(args)
return x+y

newadd = functools.partial(add,1,3,5,6)
print(newadd(7))
print(newadd(7,10))
print(newadd(9,10,x=2,y=4)) ??# ?一定不可以,因為x,y被固定了,被填充滿了,在添加值是不可以的。
print(newadd())

import inspect
print(inspect.signature(newadd))

4)partial函數的本質。(本質就是元組和字典合并。)

def partial(func,*args,**kwargs):
def newfunc(*fargs,**fkwargs):#包裝函數
newkwargs = kwargs.copy()??#關鍵字參數拷貝 ?(調用時候才會執行)
newkwargs.update(fkargs)??#關鍵詞傳參參數全部更新,(調用時候才會執行)
return func(*(args+fargs),**newkwargs)??#元素依次的合并,返回一個新的元組,不去重的。(調用時候才會執行)
newfunc.func = func ?#保留原來的函數
newfunc.args = args ??#保留原函數的位置參數????空元組
newfunc.kwargs = kwargs ?#保留原函數的關鍵字參數。??#字典。
return newfunc???#返回一個新的函數
def add(x,y):
return x+y
foo = partial(add,4)
foo(5)

 

 

 

 

 

 

5)functools.lru_cache(maxsize=128,typed=False)

lru 最近最少使用,cache緩存。

如果maxsize設置的是none,所以禁用了LRU功能,并且緩存可以無限制的增長,當maxsize是2的次冪的時候,LRU執行良好。

如果typed設置為True,則不同類型的函數參數將單獨緩存,例如;f(3)和f(3.0)視為不同結果的不同調用。

import functools
@functools.lru_chache()
def add(x,y,z=5):
time.sleep(z)
return x+y

print(add(4,5))
print(add(4.0,5))
print(add(4,6))
print(add(4,6,3))
print(add(6,4))
print(add(4,y=6))
print(add(x=4,y=6))
print(add(y=6,x=4))

總結:第一個print和第二個一樣。

第七個和第八個一樣。其余的不一樣。

緩存機制是什么:利用字典,結果放在value中,k為add的參數。

lru_cache裝飾器。

通過一個字典緩存被裝飾函數的調用和返回值。

#第一個類型

import functools

functools._make_key((4,6),{‘z’:3},False)

#打印[4, 6, <object at 0x7f98aa654090>, ‘z’, 3]

返回的是一個列表。前面的元素是args。。后面是kwargs。<object at 0x7f98aa654090>代表的是符號。

#第二個類型

import functools

functools._make_key((4,6,3),{},False)

生成的是列表。

#打印出[4, 6, 3]

#第三個類型

functools._make_key(tuple(),{‘z’:3,’x’:4,’y’:6},False)

#打印出[<object at 0x7f98aa654090>, ‘x’, 4, ‘y’, 6, ‘z’, 3]進行了排序。

#第四個類型

functools._make_key(tuple(),{‘z’:3,’x’:4,’y’:6},True)

#打印出[<object at 0x7f98aa654090>, ‘x’, 4, ‘y’, 6, ‘z’, 3, int, int, int]

總結:pyted改為True顯示類型。缺省值就是false。

Object把二元組里面的元素加進去。

_make_key..

順序,拆開k顯示的是不同的。

 

 

@functools.lru_cache()改造斐波那契數列。

import functools
@functools .lru_cache(maxsize=100)
def fib(n):
if n<3:
return 1
else:
return fib(n-1)+fib(n-2)
n=fib(5)
print(n)

import functools
@functools .lru_cache(maxsize=100)
def fib(n):
if n<3:
return 1
else:
return fib(n-1)+fib(n-2)
print([fib(x) for x in range(35)])

 

原因利用緩存:因為計算的結果下一項利用到了上一項。倒向運算。所有的計算所需的數據都來自緩存,無須再次計算啦。

6)總結:

使用前提,同樣的函數參數一定得到同樣的結果。

函數執行時間長,且要執行很多次。

本質上是函數調用函數 =》返回值。

緩存的缺點,不支持緩存過期,key無法過期,失效。不支持清除操作,不支持分布式,是一個單機的緩存。

使用場景,單機上需要空間換時間的地方,可以用緩存來將計算變成快速的查詢。

練習題:

  • 寫一個命令分發器

#第一次代碼

cmds = {}???#利用空字典收集命令
def default_func():???#定義默認函數
pass

def reg(cmd,fn):???#定義注冊函數
cmds[cmd] = fn

def py():???#定義命令函數
print(‘py’)
def lin():??#定義命令函數
print(‘lin’)

#注冊過程,名稱cmd和function之間的關系。
reg(‘py’,py)
reg(‘lin’,lin)

def dispatcher():????(用戶輸入過程)
while True:
cmd = input(‘>>>’).strip()
if cmd == ‘quit’:
break
cmds.get(cmd,default_func)()??調用
dispatcher()??調用

 

#第二步代碼,柯里化。

cmds = {}
def default_func():
pass
def reg(cmd):
def _reg(fn):
cmds[cmd] = fn
return fn
return _reg
def dispatcher():
while True:
cmd = input(‘>>>’).strip()
if cmd == ‘quit’:
break
cmds.get(cmd,default_func)()
@reg(‘py’)
def py():
print(‘py’)
@reg(‘lin’)
def lin():
print(‘lin’)
# reg(‘py’,py)
# reg(‘lin’,lin)
dispatcher()

#第三步:
ddef cmd_disapher():
cmds = {}
def default():
print(‘this is no command’)
def reg(cmd):
def _reg(fn):
cmds[cmd] = fn
return fn
return _reg

def disapher():
while True:
cmd = input(‘>>>’).strip()
if cmd == ‘quit’:
break
else:
cmds.get(cmd,default)()
return reg,disapher
reg,disapher = cmd_disapher()
@reg(‘py’)
def py():
print(‘python’)
@reg(‘lin’)
def lin():
print(‘linux’)
# reg(‘py’,py)
# reg(‘lin’,lin)

disapher()

  • 課堂習題2

import inspect
from collections import OrderedDict
#import datetime
import datetime
local_cache = {}

def cache(fn):
def wrapper(*args,**kwargs):
def make_key(fn):
#key = ()
sig = inspect.signature(fn)
params = sig.parameters
keys = list(params.keys())
#values = list(params.values())
params_dict = OrderedDict()
#位置參數
for i,val in enumerate(args):
k = keys[i]
params_dict[k] = val
#key += (kyes[i],val)
#關鍵字參數
for k,v in sorted(kwargs.items()):
#k,v = item
params_dict[k] = v
#默認值參數
for k,param in params.items():
if k not in params_dict.keys():
params_dict[k] = param.default

return tuple(params_dict.items())
key = make_key(fn)
# 查詢和緩存
if key not in local_cache.keys():
local_cache[key] = fn (*args,**kwargs)

return local_cache[key]
return wrapper

def logger(fn):
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now()- start).total_seconds()
print(fn.__name__,delta)
return ret
return wrapper

@logger
@cache
def add(x,y,z=6):
return x+y+z

add(2,3,z=4)
add(2,3,4)
add(4,5)##
add(x=2,y=3,z=4)

#第二步代碼

import inspect
from collections import OrderedDict
#import datetime
import datetime
local_cache = {}

def cache(fn):
def wrapper(*args,**kwargs):
def make_key(fn):
#key = ()
sig = inspect.signature(fn)
params = sig.parameters
keys = list(params.keys())
#values = list(params.values())
params_dict = OrderedDict()
#位置參數
for i,val in enumerate(args):
k = keys[i]
params_dict[k] = val
#key += (kyes[i],val)
#關鍵字參數
# for k,v in sorted(kwargs.items()):
# ????#k,v = item
# ????params_dict[k] = v
#默認值參數
for k,param in params.items():
if k not in params_dict.keys():
if k in kwargs.keys():
params_dict[k] = param.default ???#將上句循環改為一個循環。
else:
params_dict[k] = v.default

return tuple(params_dict.items())
key = make_key(fn)
# 查詢和緩存
if key not in local_cache.keys():
local_cache[key] = fn (*args,**kwargs)

return local_cache[key]
return wrapper

def logger(fn):
def wrapper(*args,**kwargs):
start = datetime.datetime.now()
ret = fn(*args,**kwargs)
delta = (datetime.datetime.now()- start).total_seconds()
print(fn.__name__,delta)
return ret
return wrapper

@logger
@cache
def add(x,y,z=6):
return x+y+z

add(2,3,z=4)
add(2,3,4)
add(4,5)##
add(x=2,y=3,z=4)

 

13、裝飾器的作用

1)裝飾器是AOP面向切面編程的思想體現。

面向對象往往需要通過繼承或者組合依賴等方式調用一些功能,這些功能的代碼往往可能在多個類中出現。比如logger,這樣造成代碼的重復,增加了耦合,logger改變影響所有使用他的類或者方法。

而AOP在需要的類或方法上切下,前后的切入點可以加入增強的功能,比如logger函數功能就是對 業務函數增加日志,而業務函數中應該把業務函數無關的日志功能剝離干凈。

  • 裝飾器的應用場景:

日志、監控、權限、設計、參數檢查、路由等處理:

這些功能與業務功能無關,很多業務都需要公共功能,所以適合獨立出來,需要的時候,對目標對象增強。

本文來自投稿,不代表Linux運維部落立場,如若轉載,請注明出處:http://www.www58058.com/96813

(0)
604603701@qq.com604603701@qq.com
上一篇 2018-04-22
下一篇 2018-04-22

相關推薦

欧美性久久久久