小待

  博客园 :: 首页 :: 新随笔 :: 联系 :: 订阅 :: 管理 ::

Python学习第八篇:Python基础之函数(三)- 函数总结

本节内容在后半部分:

一、表达式形式的yield
二、 表达式形式yield的应用与面向过程编程
三、匿名函数与zip,max,min,sorted
四、map,reduce,filter
五、递归与二分法

 

                                                                                                               上节复习:

1 函数对象(第一类对象)
函数可以被当成数据来传递
def func():
pass

函数的特征:
1.可以被引用
2.可以当做参数传递给另外一个函数
3.可以作为函数的返回值
4.可以当做容器类型的元素
当作容器类型元素:
        def func1():
            print('func1')

        def func2():
            print('func2')

        dic={
            'func1':func1,
            'func2':func2,
        }
执行方法: dic['func1']()

2 函数的嵌套
函数的嵌套调用:在调用函数的过程中又调用了其他的函数 --- 比如球最大值收获
函数的嵌套定义:在定义函数时,又使用def关键字定义了其他函数
def f1():
def f2():
def f3():
print(x)




3 名称空间与作用域
1 内置名称空间
2 全局名称空间
3 局部名称空间

1和2属于全局范围--->全局作用域:全局有效,在任何位置都能被访问到,除非被删除,否则存活到文件执行结束
3属于局部范围--->局部作用域:局部有效,只能在函数内部被访问到,在函数执行结束后,就释放了





4 闭包
1. 定义在函数内部的函数
2. 该内部函数包含对外部作用域而不是对全局作用域的引用

def f1():
x=1
def f2():
print(x)

return f2
f=f1()
x=100000
f()


5 装饰器
为什么要有装饰器:
开放封闭原则,对扩展是开放的,对修改是封闭

什么是装饰器:
装饰器本身-》可以是任意可调用对象
被装饰的对象-》也是任意可调用对象

装饰器要遵循的原则:
1. 不修改被装饰器对象的源代码
2. 不修改被装饰对象的调用方式

@deco #func=deco(func)
def func():
pass

@deco1
@deco2
def func():
pass



@deco1(x) #@res #func=res(func)
def func():
pass



6 迭代器
迭代:重复上一次过程,每一迭代都基于上一次执行地结果而继续进行
可迭代对象?
obj.__iter__

迭代器对象:
obj.__iter__
obj.__next__

为何要有迭代器:
提供一种不依赖于索引的迭代方式


for i in obj: #obj.__iter__()
pass


迭代器的优缺点:
优点:
1.提供一种不依赖于索引的迭代方式
2. 节省内存

缺点:
1.无法获取长度
2.一次性



7 生成器
函数体内含有yield关键字,那该函数的执行结果是生成器对象
生成器对象本质就是迭代器,所以yield的功能是
1. 把函数的执行结果做成迭代器
2. 可以返回多次值,而return只能返回一次值
3. 可以挂起函数的执行



9 内置函数:
...

 

                                                                                                         本节内容:

一、yield --->表达式形式的yield:

两种用法(形式):

 

1.yield的语句形式: yield 1 (就是简单的暂停)
2.yield的表达式形式: x=yield

 

#协程函数

 

------------表达式形式的yield----------------

 简单用法:

def eater(name):
    print('%s ready to eat' %name)
    while True:
        food=yield
        print('%s start to eat %s' %(name,food))


g=eater('alex')

#next(g)  
#表达式形式的生成器开始的时候必须传空效果等同于next(),使其到达一个初始化的位置,以后才能send值(next和send都行)
g.send(None)
#表达式形式的传值,初始化之后才可以传值
g.send('手指头1')
g.send('手指头2')

结果:
   alex ready to eat
   alex start to eat 手指头1
   alex start to eat 手指头2

 

表达式形式的生成器开始的时候必须传空效果等同于next(),使其到达一个初始化的位置,以后才能send值(next和send都行)
--->所以可以加个装饰器,让其最开始一执行拿到的就是初始化过的生成器3

扩展:  

def deco(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        next(res)
        return res             #被装饰函数有返回值,这儿需要return,不然不是个迭代器?
    return wrapper
 
@deco
def eater(name):
    print('%s ready to eat' %name)
    food_list=[]   #设置一个初始为空的列表
    while True:
        food=yield food_list             #food_list是yield的返回值
        food_list.append(food)           #向列表里添加值
        print('%s start to eat %s' %(name,food))
 
 
g=eater('alex')
# print(g)
# next(g) #等同于 g.send(None)
 
# g.send('手指头')
# g.send('脚指头')
 
print(g.send('脚趾头1'))     #得到执行后的返回值,而不是print结果
print(g.send('脚趾头2'))
print(g.send('脚趾头3'))

表达式形式的yield(x=yield)功能:

g.send('1111'),先把1111传给yield,由yield赋值给x,然后再往下执行,直到再次碰到yield,然后把yield后的返回值返回

 

二、 表达式形式yield的应用与面向过程编程

模拟一个grep -rl 'python' /root 的功能:

1.递归显示文件:

import os

def search(search_path):
    g=os.walk(search_path)                                     #os.walk() 方法用于通过在目录树种游走输出在目录中的文件名,向上或者向下。
    print(g)
    for par_dir, _, files in g:                                # _替代的是子目录,不需要     
        for file in files:
            file_abs_path = r'%s\%s' % (par_dir, file)         #r'%s\%s' % (par_dir, file)是拼出的路径
            print(file_abs_path)

search(r'E:\PycharmProjects\learn\day05\课程\a')               #加r表示用原生字符串,不加r那么用\\代替\

  结果:

<generator object walk at 0x000001CD5D983E60>
E:\PycharmProjects\learn\day05\课程\a\a.txt
E:\PycharmProjects\learn\day05\课程\a\a1.txt
E:\PycharmProjects\learn\day05\课程\a\a2.txt

  

 2.可以多次循环输入目录的递归显示文件:

import os

def init(func):                        #加一个装饰器,省掉初始化next()
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        next(res)
        return res
    return wrapper

@init
def search():
    while True:                       #加一个循环,这样可以多次循环yield,下一次会从上次yield停的地方继续
        search_path=yield             #给yield send一个值,再传给search_path
        g=os.walk(search_path)
        for par_dir,_,files in g:
            for file in files:
                file_abs_path=r'%s\%s' %(par_dir,file)
                print(file_abs_path)
g=search()

g.send(r'E:\PycharmProjects\learn\day01')

结果:

E:\PycharmProjects\learn\day01\11.py
E:\PycharmProjects\learn\day01\db
E:\PycharmProjects\learn\day01\s1-zy-.py
E:\PycharmProjects\learn\day01\s1-zy.py

3.添加打开文件功能,得到文件路径并打开文件

import os
def init(func):
    def wrapper(*args,**kwargs):
        res=func(*args,**kwargs)
        next(res)
        return res
    return wrapper

@init
def search(opener_path):
    while True:
        search_path=yield
        g=os.walk(search_path)
        for par_dir,_,files in g:
            for file in files:
                file_abs_path=r'%s\%s' %(par_dir,file)
                opener_path.send(file_abs_path) # ===>opener().send(file_abs_path),这里就调用了opener函数来运行
@init
def opener():
    while True:
        file_abs_path=yield
        print(file_abs_path)
        # with open(file_abs_path,'r',encoding='utf-8') as f:
        #     pass

# o=opener()
# o.send('E:\PycharmProjects\learn\day0') #单独执行opener函数
s=search(opener()) #把opener的执行结果-->opener生成器当参数传给opener_path s.send(r'E:\PycharmProjects\learn\day01') #search得到的结果本生就是生成器,不会直接执行,所以需要send
...

4.1 添加过滤功能并输出路径:

#grep -rl 'python' /root

import os def init(func): #加一个装饰器,省掉初始化next() def wrapper(*args,**kwargs): res=func(*args,**kwargs) next(res) return res return wrapper @init def search(opener_target): #这样便可以在执行的时候把下面的生成器opener()当参数传进来 while True: #可以循环执行yield search_path=yield #yield send得到值再传值给search_path g=os.walk(search_path) for par_dir,_,files in g: for file in files: file_abs_path=r'%s\%s' %(par_dir,file) opener_target.send(file_abs_path) #等同于opener().send(file_abs_path),这里调用了opener函数来运行 @init def opener(grep_target): while True: file_abs_path=yield with open(file_abs_path,'r',encoding='utf-8') as f: open_file=f.read() grep_target.send((file_abs_path,open_file)) #等同于grep().send((file_abs_path,open_file)) ,这里调用了grep函数来运行 #这里除了把打开了的文件open_file传给grep,还需要把文件路径file_abs_path传下去,这样文件用于判断路径用于输出 #yield生成器 send时候只能send一个值,但是这一个值却可以用元组的形式表达-->(file_abs_path,open_file) @init def grep(pattern): while True: file_abs_path,cat_file=yield #send过来一个元组,然后需要分别接受--->file_abs_path,cat_file=(file_abs_path,open_file) if pattern in cat_file: print(file_abs_path) x=r'E:\PycharmProjects\learn\day05\课程' #x是路径 p="pythond" #p是过滤字符 s=search(opener(grep(p))) #把grep的执行结果(生成器)当参数传给opener函数的grep_target,又把opener的执行结果(生成器)当参数传给search函数的opener_target s.send(x) #search得到的结果本生就是生成器,不会直接执行,所以需要send

结果:

D:\python35\python.exe E:/PycharmProjects/learn/day05/222.py
E:\PycharmProjects\learn\day05\课程\ceshi1.py
E:\PycharmProjects\learn\day05\课程\a\b\c\d\d.txt

 

4.2 添加过滤功能并输出路径: 

#grep -rl 'python' /root

import os

def init(func): #加一个装饰器,省掉初始化next()
def wrapper(*args,**kwargs):
res=func(*args,**kwargs)
next(res)
return res
return wrapper

@init
def search(target): #这样便可以在执行的时候把下面的生成器opener()当参数传进来
while True: #可以循环执行yield
search_path=yield #yield send得到值再传值给search_path
g=os.walk(search_path)
for par_dir,_,files in g:
for file in files:
file_abs_path=r'%s\%s' %(par_dir,file)
target.send(file_abs_path) #等同于opener().send(file_abs_path),这里调用了opener函数来运行
@init
def opener(target):
while True:
file_abs_path=yield
with open(file_abs_path,encoding='utf-8') as f:
target.send((file_abs_path,f)) #等同于cat().send(file_abs_path,f),这里调用了cat函数来运行
@init
def cat(target):
while True:
file_abs_path,f=yield #(file_abs_path,f),通过元组传进去
for line in f:
tag=target.send((file_abs_path,line)) #等同于grep().send((file_abs_path,line)) ,这里调用了grep函数来运行
#这里除了把文件内容line传给grep,还需要把文件路径file_abs_path传下去,这样文件用于判断路径用于输出
#yield生成器 send时候只能send一个值,但是这一个值却可以用元组的形式表达-->(file_abs_path,line)
if tag:
break
@init
def grep(target,pattern): #这里除了在执行的时候把下面的生成器printer()当参数,还把需要过滤的字符传进来
tag=False #只有第一次初始化的时候才会执行到这,以后都停在yield
while True:
file_abs_path,line=yield tag #send过来一个元组,然后需要分别接受--->file_abs_path,line=(file_abs_path,line) #后面的tag是用来给yield返回
tag=False #上次执行成功后,到另一个文件时设置一个初始tag,不然tag会一直是True
if pattern in line:
tag=True #成功为True,让上面break
target.send(file_abs_path) #等同于printer().send(file_abs_path),这里调用了printer函数来运行,同时传入file_abs_path
@init
def printer():
while True: #可以循环执行yield
file_abs_path=yield #运行时便把传过来的file_abs_path交给前面的file_abs_path
print(file_abs_path)


x=r'E:\PycharmProjects\learn\day05\课程'
g=search(opener(cat(grep(printer(),'python'))))
g.send(x)

结果如上面那个

 

上面的事例:------>

面向过程的程序设计:是一种流水线式的编程思路,是机械式      (一个阶段的结果产生下一阶段,下一阶段再产生下一阶段)
优点:
1.程序的结构清晰,可以把复杂的问题简单(流程化)。

缺点:
1 扩展性差 --->一个流水线就能做一件事,改一个点其他点也需要改;中间环节出问题只能一层一层找问题点

面向过程的程序设计应用场景:
1 linux内核,git,httpd

 

三、匿名函数(lambda) 与zip,max,min,sorted

 

有名函数:使用:名字 +() ,用def定义    

匿名函数:使用:不需要括号,用lambda定义 ,定义时候由于没有名字就相当于定义了一个值但引用计数为0,没有人直接被回收;用于取代简单的函数又不需要名字

 

匿名函数定义:

lambda x,y:x+y              (不取名字,函数体内有返回值,:后的就是返回值,  x+y相当于return x+y)

名字:没有名字就不会被引用

 

跟匿名函数有关的内置函数:

max,min,zip,sorted的用法:

salaries={
'egon':3000,
'alex':100000000,
'wupeiqi':10000,
'yuanhao':2000
}

若是想对上面的排序,可以使用max,min等内置的key参数,这样的话需要定义一个函数func:
def func(k): return salaries[k]

print (max(salaries,key=func)) #意思是把salaries按照函数func得出的结果当作来排序


这里函数func使用了一次就没用了,所以可以换成匿名函数代替:
print(max(salaries,key=lambda k:salaries[k])) #lambda k:salaries[k]--> lambda是关键词,k是参数(两个参数就x,y),:后面直接是函数体(需要有返回值) print(min(salaries,key=lambda k:salaries[k])) print(sorted(salaries,key=lambda x:salaries[x])) print(sorted(salaries,key=lambda x:salaries[x],reverse=True)) #reverse 是否翻转

 (zip函数后变成迭代器)

 

四、map,reduce,filter

高阶函数:如果函数里传入的参数是一个函数或者返回值是一个函数,那这个函数是高阶函数

def func(f):
    return f

res=func(max)
print(res)

结果:
<built-in function max>

  

map:

#把列表内每个元素后面加SB:

l=['alex','wupeiqi','yuanhao'] res=map(lambda x:x+'_SB',l) #map后产生迭代器,使用匿名函数 print(list(res)) 结果: ['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:

需要先导入functools模块

from functools import reduce

 

#reduce:做一个求和运算

from
functools import reduce l=[1,2,3,4,5] print(reduce(lambda x,y:x+y,l)) #reduce里如果不设置初始值就把序列里第一个元素弹出来作为初始值,在开始迭代后面的。作完运算再继续 输出结果: 15 #可以加初始值(比如初始值10): #有初始值就直接开始迭代 from functools import reduce l=[1,2,3,4,5] print(reduce(lambda x,y:x+y,l,10)) 输出结果: 25

 

filter:

#过滤出以SB结尾的元素

l=['alex_SB','wupeiqi_SB','yuanhao_SB','egon'] res=filter(lambda x:x.endswith('SB'),l) #匿名函数是判断是否以xxx结尾,得到bool值--是就True不是Flase,filter就是利用这个bool值--filter会依次迭代l进行匿名函数运算,结果是True就被过滤出来了 print(list(res)) 输出内容: ['alex_SB', 'wupeiqi_SB', 'yuanhao_SB']

 

五、递归与二分法

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

直接递归了:
def f1(): print('from f1') f1() f1()

 

import sys
print(sys.getrecursionlimit())        #看可以递归多少层
print(sys.setrecursionlimit(1000000)) #设置层数

  

递归:问年龄,每个人比前一个人大两岁,第五个人18
# age(5)=age(4)+2
# age(4)=age(3)+2
# age(3)=age(2)+2
# age(2)=age(1)+2
# age(1)=18
##---->得到规律:
# age(n)=age(n-1)+2 n>1
# age(n)=18 n=1
def age(n):
    if n == 1:
        return 18
     return age(n-1)+2
print(age(5))
 
结果:
26

递归形式:先递推,后面回溯得到结果

 

注意点:

递归效率低,需要在进入下一次递归时保留当前的状态,见51cto博客
解决方法是尾递归,即在函数的最后一步(而非最后一行)调用自己
但是python又没有尾递归,且对递归层级做了限制。

分开来讲 什么情况下再进行一次递归:

1. 必须有一个明确的结束条件

2. 每次进入更深一层递归时,问题规模相比上次递归都应有所减少

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

 

尾递归优化:http://egon09.blog.51cto.com/9161406/1842475

>>> sys.getrecursionlimit()
1000


>>> n=1
>>> def test():
... global n
... n+=1
... print(n)
... test()
... 
>>> test()

 

>>> sys.setrecursionlimit(10000)
>>> test() #可以递归10000层了

虽然可以设置,但是因为不是尾递归,仍然要保存栈,内存大小一定,不可能无限递归

 

二分法

切割对比,只对比一边的,减少一半以上的比较

#找到指定的数字

def search(find_num,seq):
seq=sorted(seq)
if len(seq) == 0: #判断列表是否为空,为空了就不能切分,递归终止 print('not exists') return mid_index=len(seq)//2 #切分得到中间长度(下标),若是值少则会取右边的(比如[0,1]会取1) 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: seq=seq[:mid_index] search(find_num,seq) else: print('find it')
search(
77,l) search(72,l) search(-100000,l) 结果: find it not exists not exists

 

posted on 2017-06-10 11:07  小待  阅读(144)  评论(0)    收藏  举报