python生成器函数_Python高阶函数,生成器,装饰器

一 、 生成器

1 使用生成器的原因:

列表生成式打印元素时会占用更多的内存,而生成器是每次调用只产生一个值,不会过多占用内存的,生成器又叫惰性求值,延迟计算。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

2 生成器概念和读取方式

概念: 生成器是指生成器对象,可以由生成器表达式得到,也可以使用yield关键字得到一个函数,调用这个函数得到一个生成器对象。

生成器函数:函数体中包含yield语句的函数,返回生成器对象,生成器对象,是一个可迭代对象,是一个迭代器,生成器对象,是延迟计算,惰性求值。

A 使用X.next()进行读取

B 使用for循环进行读取(原因:生成器是可迭代对象)

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

3 生成器简单实例

使用生成器来获得斐波那契数列

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

4 yield :能够生成生成器对象的关键字

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

使得next()的指针在yield之前停止

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

在生成器函数中,使用多个yield语句,执行一次后会暂停执行,把yield表达式的值返回,再次执行会执行到下一个yield语句执行结束

return 语句依然可以终止函数运行,但return语句的返回值不能被获取到,return 会导致无法获取下一个值,抛出stopiteration异常

如果函数没有显示return语句,如果生成器函数执行到结尾,一样会抛出stopiteration异常

yield的核心是让出

包含yield语句的生成器函数生成生成器对象的时候,生成器函数的函数体不会立即执行

next(generator) 会从函数的当前位置向后执行到碰到的第一个yield语句,会弹出值,并暂停函数执行

再次调用next函数,和上一条一样的处理过程

没有多余的yield语句能被执行,继续调用next函数,会抛出异常

生成器应用:协程

内核在线程内做一个操作,在用户空间的操作,减少了空间切换的问题。线程和进程的调度是操作系统完成的,调度策略。

yield from

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

生成斐波那契数列

def x(n):

a,b=0,1

for i in range(n):

yield b

a,b=b,a+b

n=int(input("请输入数字:"))

a=x(n)

for i in a:

print (i)

如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

3 实战案例

1 购买图书的生成器模型

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

无缓冲器的定制和购买图书模型

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

如果中间有和缓冲区,则需要定义一个列表并将其情况加入列表中

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

2 迷你聊天机器人

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

二 、内置高阶函数

1 高阶函数

first class object

函数在python中式一等公民

函数也是对象,可调用的对象

函数可以作为普通变量,参数,返回值等

高阶函数

在数学和计算机科学中,高阶函数应当是至少满足下面一个条件的函数

1 接受一个或多个函数作为参数

2 输出一个函数

计数器

def x(base):

def y(step=1):

nonlocal base

base+=step

return base

return y

foo=x(10)

foo1=x(10)

print (foo())

print (foo())

print (foo==foo1)

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

上述两个counter 是一样的,但是其内部的inc 可以作为本地变量理解,因此其两个是不相同的,堆栈的变量的地址。

函数对象是在堆上生成,其是在栈上调用的。

在堆中做对象的创建,栈的空间是有效的部分,空间是有限的,堆是乱的

Foo1 和 foo 的地址是在堆上的,其地址的空间的指针在栈上。

2 map :

将函数func 作用域给定序列(s)的每一个元素,并且用一个列表提供返回值,如果func 为None ,func 表现为一个身份函数,返回一个含有每个序列中元素的集合的n个元组的列表。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

其函数传入值为一个,不能同时传入多个值

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

3 reduce:

将二元函数作用与seq 序列的元素,每次携带一对(先前的结果以及下一个序列元素)连续第将现有的结果和下一个值作用在获得后的结果上,最后减少我们的序列为一个单一的返回值,如果初始值init给定,第一个比较会是init和第一个序列元素而不是序列的头两个元素

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

其函数参数只能传入两个,而不能同时传入多个参数

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

4 filter:

调用一个布尔函数func 来迭代便利每个seq中的元素,返回一个使func返回值为True 的元素序列

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

5 sorted:

import random

l1=[ random.randint(100,200) for i in range(10) ]

def sort1(iterable):

ret = []

for x in iterable: #[1] [2] [4] [3] [6]

for i,y in enumerate(ret): # [0] [1] [2,1] [4,2,1] [4,3,2,1]

if x>y: # 通过将x和y的每一个值进行比较来确定其大小关系,若 [4>2] [3<4] [2<3] [6>4]

ret.insert(i,x) #每次的小的值都会被弄到前面 [4,2,1] [4,3,2,1] [6,4,3,2,1]

break # 跳出本循环体的循环,

else: # 此处是for都不符合的结果,机y>x,则需要将x插入到指定地点

ret.append(x)

return ret

print (sort1(l1))

小于的方式进行排序

import random

l1=[ random.randint(100,200) for i in range(10) ]

def sort1(iterable):

ret = []

for x in iterable:

for i,y in enumerate(ret):

if x

ret.insert(i,x) #每次的小的值都会被弄到前面

break # 跳出本循环体的循环,

else: # 此处是for都不符合的结果,机y>x,则需要将x插入到指定地点

ret.append(x)

return ret

print (sort1(l1))

变形,通过外部传值进行处理

import random

l1=[ random.randint(100,200) for i in range(10) ]

def sort1(iterable,res):

ret = []

for x in iterable:

for i,y in enumerate(ret):

flag= xy # 此处通过对传入的值的情况进行相关的判断而影响其排序方式

if flag: # 通过将x和y的每一个值进行比较来确定其大小关系

ret.insert(i,x) #每次的小的值都会被弄到前面

break # 跳出本循环体的循环,

else: # 此处是for都不符合的结果,机y>x,则需要将x插入到指定地点

ret.append(x)

return ret

print (sort1(l1,False))

使用lambda 进行处理

import random

l1=[ random.randint(100,200) for i in range(10) ]

def sort1(iterable,a,b):

ret = []

flag = lambda a,b: a

for x in iterable:

for i,y in enumerate(ret):

if flag(x,y): # 通过调用lambda 函数来完成对函数的排序

ret.insert(i,x) #每次的小的值都会被弄到前面

break # 跳出本循环体的循环,

else: # 此处是for都不符合的结果,机y>x,则需要将x插入到指定地点

ret.append(x)

return ret

print (sort1(l1,1,2))

将lambda放置到函数参数处

import random

l1=[ random.randint(100,200) for i in range(10) ]

def sort1(iterable,flag= lambda a,b: a>b,res=True):

ret = []

flag = lambda a,b: a

for x in iterable:

for i,y in enumerate(ret):

key=flag(x,y) if res == True else flag(y,x)

if key: # 通过将x和y的每一个值进行比较来确定其大小关系

ret.insert(i,x) #每次的小的值都会被弄到前面

break # 跳出本循环体的循环,

else: # 此处是for都不符合的结果,机y>x,则需要将x插入到指定地点

ret.append(x)

return ret

print (sort1(l1,lambda a,b: a>b))

sorted 函数

用于排序

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

默认reverse=False,表示从小到大的顺序进行排列,若reverse=True 则表示其排列为从大到小的顺序排列

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

实例:不区分大小写的情况下对列表中的元素进行排序

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

三 、lambda函数(匿名函数)

使用lambda关键字来定义匿名函数

参数列表不需要小括号

冒号时用来分割参数列表和表达式的

不需要使用return,表达式的值,就是匿名函数的返回值

lambda 表达式只能写在一行,被称为单行函数

用途:在高阶函数传参时,使用lambda表达式,往往能简化代码

lambda 函数名:函数返回值

Lamdba 运算符

Lamdba args:expression (对参数进行调用并处理的)

args: 以逗号分割的参数列表

expression: 用到args 中个参数的表达式

Lamdba 语句定义的代码必须是合法的表达式,不能出现多条件语句(可使用if 的三元表达式)和其他非表达式语句,如for 和 while 等

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

lambda函数用于计算一个序列的平方并返回一个序列

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

lambda 函数联合reduce高阶函数进行对多重列表中的指定元素进行排序

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

四 、函数作为返回值,装饰器

1 闭包

1 概念 :

1 自由变量: 未在本地作用域中定义变量,如定义在内存函数外的外层函数中的作用域中的变量

2 闭包:指的是内层函数引用到了外层函数的自由变量,就形成了闭包

2 实例

In [1]: def a():

...: x=1

...: def y():

...: x+=1

...: return x

...: return y

...:

In [2]: z=a()

In [3]: z()

---------------------------------------------------------------------------

UnboundLocalError Traceback (most recent call last)

in

----> 1 z()

in y()

2 x=1

3 def y():

----> 4 x+=1

5 return x

6 return y

UnboundLocalError: local variable 'x' referenced before assignment

和上一章的结果一样,是未定义就使用

使用global 结果如下

python 2 中的两种解决方式:

In [1]: def a():

...: x=1

...: def b():

...: global x

...: x+=1

...: return x

...: return b

...:

In [2]: z=a()

In [3]: z()

---------------------------------------------------------------------------

NameError Traceback (most recent call last)

in

----> 1 z()

in b()

3 def b():

4 global x

----> 5 x+=1

6 return x

7 return b

NameError: name 'x' is not defined

global定义作用域是本地函数和全局变量,和上一级没关系

In [1]: def a():

...: global x # 此时才可以

...: x=1

...: def b():

...: global x

...: x+=1

...: return x

...: return b

...:

In [2]: z=a()

In [3]: z()

Out[3]: 2

修改如下

In [1]: def a():

...: x=[1]

...: def b():

...: x[0]+=1

...: return x[0]

...: return b # 此处返回一个函数,因此下面要通过对象加括号来调用其中的值

...:

In [2]: z=a()

In [3]: z()

Out[3]: 2

In [4]: z() # 此时随着程序的执行,再次调用,其值会和原来的值相加进行调用

Out[4]: 3

In [5]: z()

Out[5]: 4

In [6]: z()

Out[6]: 5

python 3 中的解决方式

使用了nonlocal关键字,将上述变量标记为在上级局部变量中定义的变量,但不能是全局作用域中定义。其必须是其参数的上一级作用域是局部变量,全局变量则不行

In [1]: def a():

...: x=1

...: def b():

...: nonlocal x

...: x+=1

...: return x

...: return b

...:

In [2]: z=a()

In [3]: z()

Out[3]: 2

In [1]: x=10

In [2]: def a():

...: nonlocal x

File "", line 2

nonlocal x

^

SyntaxError: no binding for nonlocal 'x' found

2 函数(方法)装饰器

1 柯里化

概念: 原来接受两个参数的函数变成一个新的接受一个参数的函数的过程,新的函数返回一个以原有函数的第二个参数作为参数的函数,及就是z=f(x,y) 转换成z=f(x)(y),其中z的值是相同的。

实例如下

def add(x,y):

return x+y

print (add(4,5))

柯里化

def add1(x):

def _add2(y): #此处的函数拥有私有属性

return x+y

return _add2

x=add1(4)

print (x(5))

############### 上述的结果和下面的结果相同

print (add1(4)(5))

外层函数没有对具体的功能进行相关的实现,其实现是通过内层函数实现,但调用可以通过外层函数来获取到。

2 基本装饰器的形式

装饰器推导

1 要求返回加入相关打印信息和其他相关信息

def add(x,y):

print ("{},{}".format(x,y)) #通过此处的调用完成函数的装饰,返回加上打印传入的值

return x+y

print (add(4,5))

缺点:

1 打印语法耦合性太高,不能适用于其他函数相关操作的处理

2 加法函数属于业务功能,而输出信息的功能,属于非业务功能,不应该放置在加法器中。

2 通过外部调用的方式完成

def add(x,y):

#通过此处的调用完成函数的装饰,返回加上打印传入的值

return x+y

def de():

print ("login......")

ret = add(4,5) # 此处不使用return的作用是需要打印下面的内容,因此不能使用return

print ("logout......")

return ret

print (de())

此处功能过于单一,无相关的参数传入,只能针对此参数进行相关操作,适用性较差

3 通过函数传递参数的方式实现调用

def add(x,y):

#通过此处的调用完成函数的装饰,返回加上打印传入的值

return x+y

def de(x,y):

print ("login......")

ret = add(x,y) # 此处不使用return的作用是需要打印下面的内容,因此不能使用return

print ("logout......")

return ret

print (de(4,5))

此处的缺点是若进行多个参数的处理,其处理方式则不能满足相关的需求

4 传递多个参数的处理方式

def add(x,y):

#通过此处的调用完成函数的装饰,返回加上打印传入的值

return x+y

def add1(x,y,z):

return x+y+z

def de(x,*args,**kwargs): #通过此处的设置其可传递多个参数进入函数内部,可完成多种形式的处理

print ("login......")

ret = add1(x,*args,**kwargs) # 此处不使用return的作用是需要打印下面的内容,因此不能使用return, 此处的含义是解包,通过上述的传入可对对应的函数参数进行解包处理

print ("logout......")

return ret

print (de(4,y=5,z=6))

此处的缺点是在进行运算时需要不断的更换函数

5 通过传递函数的方式进行更加灵活的操作

def add(x,y):

#通过此处的调用完成函数的装饰,返回加上打印传入的值

return x+y

def add1(x,y,z):

return x+y+z

# 通过此种方式传入函数的方式来完成对其的处理和修改

def de(fn,x,*args,**kwargs): #通过此处的设置其可传递多个参数进入函数内部,可完成多种形式的处理

print ("login......")

ret = fn(x,*args,**kwargs) # 此处不使用return的作用是需要打印下面的内容,因此不能使用return, 此处的含义是解包,通过上述的传入可对对应的函数参数进行解包处理

print ("logout......")

return ret

x=d(add)

print (x(4,5))

此处函数和传入的参数在一起,不利于相关运算

6 对此函数进行柯里化操作:

def add(x,y):

#通过此处的调用完成函数的装饰,返回加上打印传入的值

return x+y

def add1(x,y,z):

return x+y+z

def d(fn): # 通过此种方式传入函数的方式来完成对其的处理和修改

def de(x,*args,**kwargs): #通过此处的设置其可传递多个参数进入函数内部,可完成多种形式的处理

print ("login......")

ret = fn(x,*args,**kwargs) # 此处不使用return的作用是需要打印下面的内容,因此不能使用return, 此处的含义是解包,通过上述的传入可对对应的函数参数进行解包处理

print ("logout......")

return ret

return de

x=d(add)

print (x(4,5))

print (d(add1)(1,2,3))

7 进行相关调用处理

def add(x,y):

#通过此处的调用完成函数的装饰,返回加上打印传入的值

return x+y

def add1(x,y,z):

return x+y+z

def d(fn): # 通过此种方式传入函数的方式来完成对其的处理和修改

def de(x,*args,**kwargs): #通过此处的设置其可传递多个参数进入函数内部,可完成多种形式的处理

print ("login......")

ret = fn(x,*args,**kwargs) # 此处不使用return的作用是需要打印下面的内容,因此不能使用return, 此处的含义是解包,通过上述的传入可对对应的函数参数进行解包处理

print ("logout......")

return ret

return de

add1=d(add1) #此处被调用的add1赋值后是对应的ret的返回,及就是内层函数,而不再是原来的add1,由于上述中的fn在内层函数调用,因此其形成了闭包,此时的调用的外层函数add1被保存。

print (add1(1,2,3))

注意 :上述是由于闭包的产生导致内层函数记录了外部传输函数的情况,进而进行了相关的操作

验证如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

8 装饰器

def logger(fn):

def _logger(*args,**kwargs):

print ("login......")

ret=fn(*args,**kwargs)

print ("logout....")

return ret

return _logger

@logger # 此处的@logger等价于 add=logger(add),此处及就是语法糖,通过某些方式实现了对函数调用的简单话化

def add(x,y,z):

return x+y+z

print (add(1,2,3))

装饰器背后的主要动机是源自python面向对象编程,装饰器是在函数调用之上的修饰,这些修饰仅是当声明一个函数或者方法的时候,才会应用的额外调用

装饰器的语法是以@开头,接着是装饰器函数的名字和可选参数,紧跟着装饰器声明的是被修饰的函数,和装饰函数的可选参数。

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

1 函数中传入默认参数和必须参数的装饰器的使用

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

2 导入模块time进行计算函数的执行时间的装饰器的使用

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

3 函数中传入可变参数和关键字参数的装饰器的使用

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

4 装饰器传入值的处理

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

5 装饰器中传入多个不同类型的值的装饰器的创建

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

9 字符串文档

python 的文档

在函数语句块的第一行,且习惯是多行文本,所以使用三引号

习惯首字母是大写,第一行写概述,空一行,第三行写详细描述信息

可以使用特殊属性doc访问这个文档

def add(x,y):

''' this is function of addition'''

a= x+y

return a

print ("name={}\ndoc={}".format(add.__name__,add.__doc__))

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

其中name输出调用函数的名称,doc输出函数的说明文档。其help(add)和doc结果相同

#!/usr/bin/poython3.6

#conding:utf-8

def logger(fn):

'''

this is logger

'''

def wapper(*args,**kwargs):

''' this is wapper'''

print ("login......")

ret = fn(*args,**kwargs)

print ("logout......")

return ret

return wapper

@logger

def add(x,y):

'''

this is add sum jisuna

'''

ret = x+y

return ret

print (add(4,5),add.__name__,add.__doc__,sep='\n') #其__doc__返回的是装饰器的文档,但我们真实需要的是实际函数的文档,因此需要通过下述方式实现

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

我们真实希望的是其值返回时真实调用的函数的相关信息,而不是其内层装饰器的信息,修改如下:

#!/usr/bin/poython3.6

#conding:utf-8

def copyst(dst,src): # 通过此函数将wapper的信息调换为真实的add的相关信息

dst.__name__=src.__name__

dst.__doc__=src.__doc__

def logger(fn):

'''

this is logger

'''

def wapper(*args,**kwargs):

''' this is wapper'''

print ("login......")

ret = fn(*args,**kwargs)

print ("logout......")

return ret

copyst(wapper,fn) # 在此处调用,其中fn已经传入,且wapper已经替代fn,因此在此处进行相关处理最合适

return wapper

@logger

def add(x,y):

'''

this is add sum jisuna

'''

ret = x+y

return ret

print (add(4,5),add.__name__,add.__doc__,sep='\n')

结果如下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

此处的缺点是每次都需要进行相关的修改,且其参数在不断的叠加

带参数的装饰器

对上述copy函数进行柯里化:

#!/usr/bin/poython3.6

#conding:utf-8

def copyst(src): #进行柯里化,因为dst才是真正需要的数据

def _wapper(dst):

dst.__name__=src.__name__

dst.__doc__=src.__doc__

return dst

return _wapper

def logger(fn):

@copyst(fn) #@coypst._wapper,而@copyst.wapper=copyst.wapper(logger.waooer)其能够接受dst的参数,其相当于对底下的logger.wapper的值进行调用,其对其进行了一次覆盖后返回,wapper传入后进行重新复制计算

def wapper(*args,**kwargs):

''' this is wapper'''

print ("login......")

ret = fn(*args,**kwargs)

print ("logout......")

return ret

return wapper

@logger

def add(x,y):

'''this is add sum jisuna'''

ret = x+y

return ret

print (add(4,5),add.__name__,add.__doc__,sep='\n')

结果如下:

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

五 类型注解

1 函数定义的弊端

python 是动态语言,变量随时可以被赋值,且能被赋值为不同的类型,但某些时候由于只有在运行时才能显示出其相关的弊端,因此其变得不可捉摸,又因为其是强类型语言,因此其不能实现不同类型的运算,某些时候便表现出在运行时才出现的问题的情况

2 解决方式一

增加文档documentation string

def add(x,y):

'''

:Param x:int

:Param y:int

:return:int

'''

return x+y

print (add.__doc__)

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

3 解决方式,增加函数注解,

通过annotations 的方式进行调用

资源注解: python3.5引入

对函数的参数进行类型注解

对函数的返回这进行类型注解

支队函数参数做一个辅助说明,并不能对函数进行类型检查

提供给第三方工具,做代码分析,发现隐藏bug

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

在pycharm中,则能够起到提示的作用

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

return x+y

print (add.__annotations__)

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

3 inspet 模块

1 获取基本信息

提供获取对象信息的函数,可以检查函数和类,类型检查

import inspect

def add(x:int,y:int,*args,**kwargs) ->int:

return x+y

sig=inspect.signature(add) #获取签名,函数签名包含了一个函数的信息,包括函数名,它的参数类型,它所在的类和名称空间及其他信息

print (sig)

print ("params: ",sig.parameters) # 返回一个有序的字典,其值

print ("return: ",sig.return_annotation) # 打印返回值的注解

print (sig.parameters['y'])

print (sig.parameters['x'].annotation) # 返回指定字典键的注解

print (sig.parameters['args'])

print (sig.parameters['args'].annotation)# 若无资源注解,则返回inspect._empty

print (sig.parameters['kwargs'])

print (sig.parameters['kwargs'].annotation)# 若无资源注解,则返回inspect._empty

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

2 inspect 模块判断

import inspect

def a(x:int,y:int,*args,**kwargs) ->int:

return x+y

print (inspect.isfunction(a)) # 判断其是否是一个函数

print (inspect.ismethod(a)) # 是否是一个类方法

print (inspect.isgenerator(a)) # 是否是一个生成器对象

print (inspect.isgeneratorfunction(a)) # 是否是一个生成器函数

print (inspect.isclass(a)) # 是否是一个类

print (inspect.ismodule(inspect)) # 是否是一个模块

print (inspect.isbuiltin(print)) #是否是一个内建模块

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

3 parameter 对象

保存在元组中,其是只读的

name,获取参数的名字

annotation ,获取参数的注解,可能没有

default,参数的缺省值,可能没有定义

empty, 特殊的类,用来标记default属性或者注释annotation书信的空值。

kind,实参如何绑定到形参,就是形参的类型(默认参数,关键字参数,可变参数)等

POSITIONAL_ONLY,值必须是位置参数提供,在python中没有实现,因为python中可通过赋值和直接传入相对位置两种方式进行

POSITIONAL_OR_KEYWORD,值可做为关键字参数或位置参数提供

VAR_POSITIONAL: 可变位置参数,对应*args

KEYWORD_ONLY: keyword-only参数,对应或*args之后出现的非可变关键字参数

VAR_KEYWORD:对应**kwargs

import inspect

def a(x,y:int=7,*args,z,t=10,**kwargs) ->int:

return x+y

sig=inspect.signature(a)

for i,(name,param) in enumerate(sig.parameters.items()): #通过遍历有序字典的键和值,其中name表示其变量名param.annotation表示该变量的资源注解

print (i+1,name,param.annotation,param.kind,param.default,sep='\n')

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

4 通过资源注解来确定其类型,然后进行判断来取出

In [1]: import inspect

In [2]: def add(x:int,y:int=5)->int:

...: return x+y

...:

In [3]: sig=inspect.signature(add)

In [4]: param=sig.parameters

In [5]: param #返回一个有序字典

Out[5]: mappingproxy({'x': , 'y': })

In [6]: param['x'] # 通过字典的键获取其值

Out[6]:

In [7]: a=param['x'] # 赋值其值

In [8]: a.annotation # 查看其参数的资源类型

Out[8]: int

In [9]: a.kind # 查看其位置属性

Out[9]: <_ParameterKind.POSITIONAL_OR_KEYWORD: 1>

In [10]: a.name # 查看其变量名称

Out[10]: 'x'

In [11]: a.default # 查看其默认参数

Out[11]: inspect._empty

In [17]: x=param.keys() # 获取其键

In [18]: tuple(x) #进行类型转换

Out[18]: ('x', 'y')

In [19]: list(x)

Out[19]: ['x', 'y']

实例

import inspect

def logger(fn):

def _wapper(*args,**kwargs):

# 实参的检查

#print (*args,**kwargs) #此处后面的**kwargs不能这样写,因为其print(a=1) 此时在print函数中未定义a,因此不行

#print(args,kwargs)

sig=inspect.signature(fn)

param=sig.parameters #有序字典

param_list=tuple(param.keys()) #此处获取到的是形参的列表

for i,v in enumerate(args): # 此处通过遍历获取传入参数值和对应的索引

k = param_list[i] # 此处通过索引找寻对应的值对应的形参

if isinstance(v, param[k].annotation): # 通过对对应的形参和对应的传入的实参的比较来判断其结果正确与否

print("--", 'is', param[k].annotation)

else:

print("--", 'is not', param[k].annotation)

#关键字传递参数处理

for k,v in kwargs.items(): # 此处获取的结果是既有参数,也有对应的value,此处只需进行对value的类型和k进行比较来确定其传入值是否正确即可

if isinstance(v,param[k].annotation):

print ("--",'is ',param[k].annotation )

else:

print("--", 'is not', param[k].annotation)

ret = fn(*args,**kwargs)

return ret

return _wapper

@logger

def a(x:int,y:int=5) ->int:

return x+y

a(4,8)

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

抛出异常的处理

#!/usr/bin/poython3.6

#conding:utf-8

import inspect

import functools

def logger(fn):

@functools.wraps(fn)

def _wapper(*args,**kwargs):

# 构建形参列表

sig = inspect.signature(fn)

param=sig.parameters

param_list=list(param.keys())

# 构建实参可变参数列表

for k,v in enumerate(args):

if isinstance(v,param[param_list[k]].annotation):

print ("{} is {}".format(v,param[param_list[k]].annotation))

else:

raise TypeError("{} is not {}".format(v,param[param_list[k]].annotation))

#构建实参关键字参数

for k,v in kwargs:

if isinstance(v,param[k].annotation):

print("{} is {}".format(v, param[param_list[k]].annotation))

else:

raise TypeError("{} is not {}".format(v, param[param_list[k]].annotation))

ret = fn(*args,**kwargs)

return ret

return _wapper

@logger

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

return x+y

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

六 实战实例:

1 IP地址查询系统

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

2 求两个字符串的公共子串

思路:通过两个公共字符串构建一个len(str1)*len(str2)的矩阵,并通过获取第一个和第二个字符串之间的公共字符在矩阵上获取其值为1,若不相同,则其值为0,及str1='abcd',str2='bcde',则矩阵为

[[0,0,0,0],

[1,0,0,0],

[0,1,0,0],

[0,0,1,0],

]

由上述得知,上述字符串的公共子串为bcd,及就是str2[2-2:2],其中2为最终的列表的最大对角线对应值的位置的列表内层的值,2为斜线连续的1的数量。其斜线的连续数量可使用直接相加的方式得到,其起始坐标可通过计数器获取。

def getMaxCommonSubstr(s1, s2):

# 求两个字符串的最长公共子串

# 思想:建立一个二维数组,保存连续位相同与否的状态

len_s1 = len(s1)

len_s2 = len(s2)

# 生成0矩阵,为方便后续计算,多加了1行1列

# 行: (len_s1+1)

# 列: (len_s2+1)

record = [[0 for i in range(len_s2+1)] for j in range(len_s1+1)]

maxNum = 0 # 最长匹配长度

p = 0 # 字符串匹配的终止下标

for i in range(len_s1):

for j in range(len_s2):

if s1[i] == s2[j]:

# 相同则累加

record[i+1][j+1] = record[i][j] + 1

if record[i+1][j+1] > maxNum:

maxNum = record[i+1][j+1]

p = i # 匹配到下标i

# 返回 子串长度,子串

return maxNum,s1[p+1-maxNum : p+1]

s1='abcde'

s2='bcdef'

[lenMatch,strMatch] = getMaxCommonSubstr(s1,s2)

print('子串: ', strMatch)

print('子串长度: ',lenMatch)

结果如下

watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值