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))

倒向打印:

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()

斐波那契数列:

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.178linux.com/96813

发表评论

登录后才能评论

This site uses Akismet to reduce spam. Learn how your comment data is processed.

联系我们

400-080-6560

在线咨询:点击这里给我发消息

邮件:1823388528@qq.com

工作时间:周一至周五,9:30-18:30,节假日同时也值班