一、生成器补充

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. address-already in use 以及查看端口

    https://stackoverflow.com/questions/19071512/socket-error-errno-48-address-already-in-use

  2. jq中写PHP

    var id="$defaultId"; if(!id){ $("#tag_url").addClass("div_display_none" ...

  3. codeforces(559C)--C. Gerald and Giant Chess(组合数学)

    C. Gerald and Giant Chess time limit per test 2 seconds memory limit per test 256 megabytes input st ...

  4. linux下Java运行时so文件的附加

    将路径加入至 etc/ld.so.conf 中

  5. Android无线测试之—UiAutomator工程建立

    一.环境要求: 已经搭建好了Android UiAotomator测试环境 二.新建测试工程: 1.打开eclipse,首次打开指定一个工作空间 2.新建一个Java Project,名叫:Demo1 ...

  6. iOS学习笔记(七)——UI基础UIButton

    前面写了UIWindow.UIViewController,那些都是一些框架,框架需要填充上具体的view才能组成我们的应用,移动应用开发中UI占了很大一部分,最基础的UI实现是使用系统提供的各种控件 ...

  7. convex hull trick CF344.E

    类似于斜率优化的东西,果真CF的E以后才会考点算法啊. 感觉这种优化应该很常见,但这题直线只有第一象限的,但是插入,和查找操作是不变的,按极角排序后就可以直接用这个模板了. #include < ...

  8. 七、Dockerfile案例三(Mysql安装)

    七.Dockerfile案例三(Mysql安装) *特别提醒:新版的mysql:5.7数据库下的user表中已经没有Password字段了(5.5的user表还有) 一.查看docker hub上的版 ...

  9. linux磁盘清理

    一.背景: 1.由于linux系统空间是由挂载磁盘得来的,但有时装系统时挂载/根目录空间不大,现仅清除用户下载的大文件 二.方法: 1.输入命令df -h显示当前磁盘挂载(包含剩余空间)情况这里写图片 ...

  10. 巨蟒python全栈开发linux之cento9

    1.docker入门学习 查看机器中已经启动的所有的进程. ps -ef 2.docker常用命令学习 3.docker学习3 4.dockerfile与镜像 5.docker私有仓库 6.rabbi ...