一、生成器补充

1.什么是生成器?

可以理解为一种数据类型,这种数据类型自动实现了迭代器协议(其他的数据类型需要调用自己内置的__iter__方法),所以生成器就是可迭代对象。

2.生成器分类

(1)生成器函数:常规函数定义,但是,使用yield语句而不是return语句返回结果。yield语句一次返回一个结果,在每个结果中间,挂起函数的状态,以便下次从它离开的地方继续执行。

(2)生成器表达式:类似于列表推导,但是,生成器返回按需产生结果的一个对象,而不是一次构建一个结果列表。

3.为何使用生成器之生成器的优点

Python使用生成器对延迟操作提供了支持。

所谓延迟操作,是指在需要的时候才产生结果,而不是立即产生结果。这也是生成器的主要好处。

4.生成器函数

def lay_eggs(num):
for egg in range(num):
res='蛋%s' %egg
yield res
print('下完一个蛋') laomuji=lay_eggs(10)
print(laomuji) #打印生成器在内存中的地址
print(laomuji.__next__()) #执行生成器函数
print(laomuji.__next__())
print(laomuji.__next__())
egg_l=list(laomuji)
print(egg_l)
运行结果:
<generator object lay_eggs at 0x0000000000A320F8>
蛋0
下完一个蛋
蛋1
下完一个蛋
蛋2
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
下完一个蛋
['蛋3', '蛋4', '蛋5', '蛋6', '蛋7', '蛋8', '蛋9']

5.生成器表达式和列表解析

egg_list=['鸡蛋%s' %i for i in range(10)]   #列表解析
laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式
print(laomuji)
print(next(laomuji)) #next本质就是调用__next__
print(laomuji.__next__())
print(next(laomuji))
运行结果:
<generator object <genexpr> at 0x0000000000D820A0>
鸡蛋0
鸡蛋1
鸡蛋2

总结:

(1)把列表解析的[]换成()得到的就是生成器表达式

(2)列表解析与生成器表达式都是一种便利的编程方式,只不过生成器表达式更节省内存

(3)Python不但使用迭代器协议,让for循环变得更加通用。大部分内置函数,也是使用迭代器协议访问对象的。

例如,sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,所以,我们可以直接这样计算一系列值的和:

sum(x ** 2 for x in xrange(4))

6.注意

人口信息.txt文件的内容

{'name':'北京','population':10}

{'name':'南京','population':100000}

{'name':'山东','population':10000}

{'name':'山西','population':19999}

def get_provice_population(filename):
with open(filename) as f:
for line in f:
p=eval(line) #将字符串转换成字典
yield p['population']
gen=get_provice_population('人口信息') all_population=sum(gen)
print(all_population)
for p in gen:
print(p/all_population)
运行结果:
130009

为什么只有1个运行结果呢?

执行上面这段代码,将不会有任何输出,这是因为,生成器只能遍历一次。在我们执行sum语句的时候,就遍历了我们的生成器,当我们再次遍历我们的生成器的时候,将不会有任何记录。所以,上面的代码不会有任何输出。

示例:

def test():
for i in range(4):
yield i g=test() g1=(i for i in g)
g2=(i for i in g1) print(list(g1))
print(list(g2))
运行结果:
[0, 1, 2, 3]
[]

7.协程函数

(1)协程函数就是使用了yield表达式形式的生成器

def eater(name):
print("%s eat food" %name)
while True:
food = yield
print("done") g = eater("gangdan")
print(g)
<generator object eater at 0x00000000011B80A0>

(2)协程函数赋值过程

要先运行next(),让函数初始化并停在yield,相当于初始化函数,然后再send(),send会给yield传一个值

next()和send()都是让函数在上次暂停的位置继续运行,

next是让函数初始化

send在触发下一次代码的执行时,会给yield赋值

def eater(name):
print('%s start to eat food' %name)
food_list=[]
while True:
food=yield food_list
print('%s get %s ,to start eat' %(name,food))
food_list.append(food) e=eater('钢蛋')
print(e)
print(next(e)) # 现在是运行函数,让函数初始化
print(e.send('包子')) #
print(e.send('韭菜馅包子'))
print(e.send('大蒜包子'))
运行结果:
<generator object eater at 0x00000000011C8150>
钢蛋 start to eat food
[]
钢蛋 get 包子 ,to start eat
['包子']
钢蛋 get 韭菜馅包子 ,to start eat
['包子', '韭菜馅包子']
钢蛋 get 大蒜包子 ,to start eat
['包子', '韭菜馅包子', '大蒜包子']

用装饰器修饰协程函数

def deco(func):
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
next(res)
return res
return wrapper @deco #g=deco(g)
def eater(name):
print('%s ready to eat' %name)
food_list=[]
while True:
food=yield food_list
food_list.append(food)
print('%s start to eat %s' %(name,food)) g=eater('alex') #wrapper('alex')
print(g)
next(g) #等同于 g.send(None) g.send('手指头')
g.send('脚指头')
g.send('别人的手指头')
g.send('别人的脚指头') print(g)
print(g.send('脚趾头1'))
print(g.send('脚趾头2'))
print(g.send('脚趾头3'))
运行结果:
alex ready to eat
<generator object eater at 0x0000000000A12258>
alex start to eat None
alex start to eat 手指头
alex start to eat 脚指头
alex start to eat 别人的手指头
alex start to eat 别人的脚指头
<generator object eater at 0x0000000000A12258>
alex start to eat 脚趾头1
[None, '手指头', '脚指头', '别人的手指头', '别人的脚指头', '脚趾头1']
alex start to eat 脚趾头2
[None, '手指头', '脚指头', '别人的手指头', '别人的脚指头', '脚趾头1', '脚趾头2']
alex start to eat 脚趾头3
[None, '手指头', '脚指头', '别人的手指头', '别人的脚指头', '脚趾头1', '脚趾头2', '脚趾头3']

应用场景

grep -rl 'python' /root

import os

def send_none(func):
'''
装饰器函数:初始化表达式yield的生成器函数(send一个None)
'''
def wrapper(*args,**kwargs):
res = func(*args,**kwargs)
res.send(None) # next(res)
return res
return wrapper @send_none
def get_abs_path(open_file):
'''
获取目录下所有文件的绝对路径函数,并send给open_file
'''
while True:
walk_path = yield
w = os.walk(walk_path)
for path,_,files in w:
for file in files:
file_abs_path = r'%s\%s' % (path, file)
open_file.send(file_abs_path) # 将得到的绝对路径通过send方式传给open_file这个生成器函数 @send_none
def open_file(match_line):
'''
获得文件句柄函数,并将文件句柄和文件路径以元祖的形式send给match_line
'''
while True:
file_abs_path = yield
with open(file_abs_path,encoding='utf-8') as f:
match_line.send((file_abs_path,f)) # send可以将多个参数打包成元祖传递给生成器函数 @send_none
def match_line(word):
'''
遍历通过yield获取到的文件里的每一行,找到关键字是否存在于改行,存在关键字的文件,打印输出
(使用标签位位,避免重复打印)
'''
while True:
file_abs_path,f = yield
Flag = False # 定义一个标志位
for line in f:
if Flag: # 如果标志位被更改为True则跳出循环(为了防止一个文件有多行关键字,print时存在重复)
break
if word in line :
Flag = True # 当匹配到关键字所在的行,将标志位改为Ture
print(file_abs_path) walk_path = r'E:\s17\day05\a'
g = get_abs_path(open_file(match_line('python'))) # 获得get_abs_path()生成器对象
g.send(walk_path)
运行结果:
E:\s17\day05\a\a1
E:\s17\day05\a\b\c\c1
E:\s17\day05\a\b\c\d\d1

总结:

面向过程的程序设计:是一种流水线式的编程思路,是机械式

优点:

程序的结构清晰,可以把复杂的问题简单

缺点:

扩展性差

应用场景:

linux内核,git,httpd

二、递归

1.递归调用

在函数调用过程中,直接或间接地调用了函数本身,这就是函数的递归调用。

def f1():
print('f1')
f2() def f2():
f1() f1()

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。

使用以下方式查询:

import sys
print(sys.getrecursionlimit())
运行结果:
1000

2.尾递归

尾递归是指,在函数返回的时候,调用自身本身,并且,return语句不能包含表达式。这样,编译器或者解释器就可以把尾递归做优化,使递归本身无论调用多少次,都只占用一个栈帧,不会出现栈溢出的情况。

尾递归调用时,如果做了优化,栈不会增长,因此,无论多少次调用也不会导致栈溢出。

遗憾的是,大多数编程语言没有针对尾递归做优化,Python解释器也没有做优化。

3.应用场景:二分法

逻辑是将列表取中间值做比较,然后取左边或右边再比较

l = [1, 2, 10,33,53,71,73,75,77,85,101,201,202,999,11111]

def search(find_num,seq):
if len(seq) == 0:
print('not exists')
return
mid_index=len(seq)//2
mid_num=seq[mid_index]
print(seq,mid_num)
if find_num > mid_num:
#in the right
seq=seq[mid_index+1:]
search(find_num,seq)
elif find_num < mid_num:
#in the left
seq=seq[:mid_index]
search(find_num,seq)
else:
print('find it') search(77,l)
search(72,l)
search(-100000,l)
运行结果:
[1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
[77, 85, 101, 201, 202, 999, 11111] 201
[77, 85, 101] 85
[77] 77
find it
[1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
[1, 2, 10, 33, 53, 71, 73] 33
[53, 71, 73] 71
[73] 73
not exists
[1, 2, 10, 33, 53, 71, 73, 75, 77, 85, 101, 201, 202, 999, 11111] 75
[1, 2, 10, 33, 53, 71, 73] 33
[1, 2, 10] 2
[1] 1
not exists

三、匿名函数

1.lambda

当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便。

f=lambda x,y:x+y
print(f) print(f(1,2))
运行结果:
<function <lambda> at 0x0000000000B40268>
3
map(lambda x: x * x, [1, 2, 3, 4, 5, 6, 7, 8, 9])
运行结果:
[1, 4, 9, 16, 25, 36, 49, 64, 81]

匿名函数有个限制,就是只能有一个表达式,不用写return,返回值就是该表达式的结果。

2.lambda用法

(1)把匿名函数赋值给一个变量,再利用变量来调用该函数

f = lambda x: x * x
print(f)
f(5)
运行结果:
<function <lambda> at 0x10453d7d0>
25

(2)匿名函数作为返回值返回

def build(x, y):
return lambda: x * x + y * y
t=build(4,3)
print(t())

(3)max,min,zip,sorted的用法

salaries={
'egon':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
} print(max(salaries)) #只根据字典的key进行比较
res=zip(salaries.values(),salaries.keys()) #接收任意个参数,返回一个元组列表
print(res)
print(list(res))
#匿名函数用法
print(max(salaries,key=lambda k:salaries[k]))
print(min(salaries,key=lambda k:salaries[k]))
print(sorted(salaries)) #默认的排序结果是从小到到
print(sorted(salaries,key=lambda x:salaries[x])) #默认的排序结果是从小到大
print(sorted(salaries,key=lambda x:salaries[x],reverse=True)) #默认的排序结果是从小到大
运行结果:
yuanhao
<zip object at 0x0000000000A17A88>
[(3000, 'egon'), (100000000, 'alex'), (2000, 'yuanhao'), (10000, 'wupeiqi')] alex
yuanhao
['alex', 'egon', 'wupeiqi', 'yuanhao']
['yuanhao', 'egon', 'wupeiqi', 'alex']
['alex', 'wupeiqi', 'egon', 'yuanhao']

3.global用法

如果你想要为一个定义在函数外的变量赋值,那么你就得告诉Python这个变量名不是局部的,而是全局的。

我们使用global语句完成这一功能。没有global语句,是不可能为定义在函数外的变量赋值的。

注意:应该尽量避免这样做,因为这使得程序的读者会不清楚这个变量是在哪里定义的。

x=1000
def f1():
x=0 #只在局部生效 f1()
print(x) #打印的是全局变量x
运行结果:
1000
x=1000
def f1():
global x #调用全局变量x
x=0 f1()
print(x)
运行结果:
0

4.内置函数

  • map函数

    Map接受一个方法和一个集合作为参数。它创建一个新的空集合,以每一个集合中的元素作为参数调用这个传入的方法,然后把返回值插入到新创建的集合中。
l=['alex','wupeiqi','yuanhao']
res=map(lambda x:x+'_SB',l)
print(res)
print(list(res))
运行结果:
<map object at 0x00000000007FB9B0>
['alex_SB', 'wupeiqi_SB', 'yuanhao_SB']
nums=(2,4,9,10)
res1=map(lambda x:x**2,nums)
print(list(res1))
运行结果:
[4, 16, 81, 100]
  • reduce函数

    reduce()传入的函数必须接收两个参数,reduce()对list的每个元素反复调用函数,并返回最终结果值。
from functools import reduce

l=[1,2,3,4,5]
print(reduce(lambda x,y:x+y,l,10)) #reduce(f,[1,2,3,4],10) #1+2+3+4+10
运行结果:
25
  • filter函数

    对每个元素进行判断,返回True或False,filter()根据判断结果自动过滤掉不符合条件的元素,返回由符合条件元素组成的新list。
l=['alex_SB','wupeiqi_SB','yuanhao_SB','egon']

res=filter(lambda x:x.endswith('SB'),l)
print(res)
print(list(res))
运行结果:
<filter object at 0x000000000108BA58>
['alex_SB', 'wupeiqi_SB', 'yuanhao_SB']

四、面向过程编程与函数编程

1.概念

函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。

而函数式编程(请注意多了一个“式”字)——Functional Programming,虽然也可以归结到面向过程的程序设计,但其思想更接近数学计算。

我们首先要搞明白计算机(Computer)和计算(Compute)的概念。

在计算机的层次上,CPU执行的是加减乘除的指令代码,以及各种条件判断和跳转指令,所以,汇编语言是最贴近计算机的语言。

而计算则指数学意义上的计算,越是抽象的计算,离计算机硬件越远。

对应到编程语言,就是越低级的语言,越贴近计算机,抽象程度低,执行效率高,比如C语言;越高级的语言,越贴近计算,抽象程度高,执行效率低,比如Lisp语言。

函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,因此,这种函数是有副作用的。

函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数!

Python对函数式编程提供部分支持。由于Python允许使用变量,因此,Python不是纯函数式编程语言。

2.总结

面向过程解释:

函数的参数传入,是函数吃进去的食物,而函数return的返回值,是函数拉出来的结果,面向过程的思路就是,把程序的执行当做一串首尾相连的函数,一个函数吃,拉出的东西给另外一个函数吃,另外一个函数吃了再继续拉给下一个函数吃。。。

例如:

用户登录流程:前端接收处理用户请求-》将用户信息传给逻辑层,逻辑词处理用户信息-》将用户信息写入数据库

验证用户登录流程:数据库查询/处理用户信息-》交给逻辑层,逻辑层处理用户信息-》用户信息交给前端,前端显示用户信息

3.高阶函数

  • map函数应用
array=[1,3,4,71,2]

ret=[]
for i in array:
ret.append(i**2)
print(ret) #如果我们有一万个列表,那么你只能把上面的逻辑定义成函数
def map_test(array):
ret=[]
for i in array:
ret.append(i**2)
return ret print(map_test(array)) #如果我们的需求变了,不是把列表中每个元素都平方,还有加1,减一,那么可以这样
def add_num(x):
return x+1
def map_test(func,array):
ret=[]
for i in array:
ret.append(func(i))
return ret print(map_test(add_num,array))
#可以使用匿名函数
print(map_test(lambda x:x-1,array)) #上面就是map函数的功能,map得到的结果是可迭代对象
print(map(lambda x:x-1,range(5)))
运行结果:
[1, 9, 16, 5041, 4]
[1, 9, 16, 5041, 4]
[2, 4, 5, 72, 3]
[0, 2, 3, 70, 1]
<map object at 0x0000000001503A20>
  • reduce函数应用
from functools import reduce
#合并,得一个合并的结果
array_test=[1,2,3,4,5,6,7]
array=range(100) #报错啊,res没有指定初始值
def reduce_test(func,array):
l=list(array)
for i in l:
res=func(res,i)
return res # print(reduce_test(lambda x,y:x+y,array)) #可以从列表左边弹出第一个值
def reduce_test(func,array):
l=list(array)
res=l.pop(0)
for i in l:
res=func(res,i)
return res print(reduce_test(lambda x,y:x+y,array)) #我们应该支持用户自己传入初始值
def reduce_test(func,array,init=None):
l=list(array)
if init is None:
res=l.pop(0)
else:
res=init
for i in l:
res=func(res,i)
return res # print(reduce_test(lambda x,y:x+y,array))
print(reduce_test(lambda x,y:x+y,array,50))
运行结果:
4950
5000
  • filter函数应用
movie_people=['alex','wupeiqi','yuanhao','sb_alex','sb_wupeiqi','sb_yuanhao']

def tell_sb(x):
return x.startswith('sb') def filter_test(func,array):
ret=[]
for i in array:
if func(i):
ret.append(i)
return ret print(filter_test(tell_sb,movie_people)) #函数filter,返回可迭代对象
print(filter(lambda x:x.startswith('sb'),movie_people))
运行结果:
['sb_alex', 'sb_wupeiqi', 'sb_yuanhao']
<filter object at 0x0000000001487C18>

Python编程-函数进阶二的更多相关文章

  1. Python编程-函数进阶

    一.函数对象 函数是第一类对象,即函数可以当作数据传递 1 可以被引用 2 可以当作参数传递 3 返回值可以是函数 4 可以当作容器类型的元素 def foo(): print('from foo') ...

  2. 【转】Python之函数进阶

    [转]Python之函数进阶 本节内容 上一篇中介绍了Python中函数的定义.函数的调用.函数的参数以及变量的作用域等内容,现在来说下函数的一些高级特性: 递归函数 嵌套函数与闭包 匿名函数 高阶函 ...

  3. Python之函数进阶

    本节内容 上一篇中介绍了Python中函数的定义.函数的调用.函数的参数以及变量的作用域等内容,现在来说下函数的一些高级特性: 递归函数 嵌套函数与闭包 匿名函数 高阶函数 内置函数 总结 一.递归函 ...

  4. 《Python》 函数进阶和名称空间作用域

    函数进阶: 一.动态参数:*args  **kwargs *args是元祖形式,接收除去键值对以外的所有参数 # args可以换成任意变量名,约定俗成用args **kwargs接收的只是键值对的参数 ...

  5. 10.Python初窥门径(函数进阶)

    Python(函数进阶) 一.函数的传参(接上期) 形参角度(一共四种,后两种) 动态参数(万能参数)* # 定义一个函数时,*所有的位置参数聚合到一个元组中 def func(*args): # * ...

  6. python开发函数进阶:生成器表达式&各种推导式

    一,生成器表达式 #生成器表达式比列表解析更省内存,因为惰性运算 #!/usr/bin/env python #_*_coding:utf-8_*_ new_2 = (i*i for i in ran ...

  7. Python入门-函数进阶

    昨天我们简单的了解了函数的定义,调用,以及传参,其实还有一个更重要的传参:动态传参,让我们继续昨天没有说完的,以及今天我要分享的东西. 一.动态传参 之前我们说过了传参,如果我们需要给一个函数传参,而 ...

  8. python学习——函数进阶

    首先来看下面这个函数. def func(x,y): bigger = x if x > y else y return bigger ret = func(10,20) print(ret) ...

  9. python开发函数进阶:装饰器

    一,装饰器本质 闭包函数 功能:就是在不改变原函数调用方式的情况下,在这个函数前后加上扩展功能 作用:解耦,尽量的让代码分离,小功能之前的分离. 解耦目的,提高代码的重用性 二,设计模式 开放封闭原则 ...

随机推荐

  1. jq和thinkphp经常使用的几种ajax

    第一种方法   第二种方法 jquery方法: MessageAction.class.php <?php class MessageAction extends Action{ functio ...

  2. Win10中配置jdk之后javac无法运行

    环境变量(environment variables)一般是指在操作系统中用来指定操作系统运行环境的一些参数,如:临时文件夹位置和系统文件夹位置等. 环境变量是在操作系统中一个具有特定名字的对象,它包 ...

  3. Vue实现远程获取路由与页面刷新导致404错误的解决

    一.背景 先简单介绍一下现在项目情况:前后端分离,后端服务是Java写的,前端是Vue+ElementUI. 最近的一个需求是:通过后端Api去获取前端路由表,原因是每个登录角色对应的前端路由表可能是 ...

  4. CImg的使用,入门

    CImg的使用: const char *imageIN="image.jpg" const char *imageOUT="imgeout.jpg" CImg ...

  5. SpringAOP和AspectJ

    SpringAOP和AspectJ的关系 问题:之前对 SpringAOP和AspectJ的关系感到疑惑,因为曾经在书上看过SpringAOP集成了AspectJ,那么SpringAOP是直接使用了A ...

  6. linux终端常用命令

    常用的信息显示命令 命令#pwd 用于在屏幕上输出当前的工作目录. 命令#stat 用于显示指定文件的相关信息. 命令#uname -a 用于显示操作系统信息. 命令#hostname 用于显示当前本 ...

  7. mysql的体系架构和存储引擎

    定义数据库和实例 数据库:物理操作系统的文件或其他形式文件类型的集合.在mysql数据库中,数据库文件可以是frm.MYD.MYI.ibd结尾的文件. 实例:MySQL数据库由后台线程以及一个共享内存 ...

  8. docker学习笔记(1)概述、原理学习、常用命令

    一.Docker概述 Docker是基于Go语言实现的云开源项目,诞生于2013年初,目前主流的Linux操作系统已支持Docker,如Redhat RHEL6.5/CentOS6.5.Ubuntu ...

  9. inner join和out join的区别

    inner join(又叫join) out join包括left join,right join和full join(也就是left+right)

  10. es6数组的一些函数方法使用

    数组函数forEach().map().filter().find().every().some().reduce()等 数组函数(这里的回调函数中的index和arr都可以省略,回调函数后有参数是设 ...