python函数

阅读目录

一 函数知识体系

 

 

1 什么是函数?
2 为什么要用函数?
3 函数的分类:内置函数与自定义函数
4 如何自定义函数
  语法
  定义有参数函数,及有参函数的应用场景
  定义无参数函数,及无参函数的应用场景
  定义空函数,及空函数的应用场景

5 调用函数
    如何调用函数
    函数的返回值
    函数参数的应用:形参和实参,位置参数,关键字参数,默认参数,*args,**kwargs

6 高阶函数(函数对象)
7 函数嵌套
8 作用域与名称空间
9 装饰器
10 迭代器与生成器及协程函数
11 三元运算,列表解析、生成器表达式
12 函数的递归调用
13 内置函数
14 面向过程编程与函数式编程
 
可变长参数
*args

    #接收多出来的位置参数,组成元组

**kwargs

    #接收多出来的关键字参数,组成字典

 

函数对象
函数对象的概念:

    1.函数可被当做数据传递

    2.函数可以当做参数传给另外一个函数

    3.一个函数的返回值可以是一个函数
二 闭包函数

 

 

简介

定义:定义在函数内部的函数

特点:包含对外部作用域而不是对全局作用域名字的引用,该函数就称为闭包函数。

例题

需求:利用闭包函数爬网页

函数实现

def get(url):
    return urlopen(url).read()
print(get('http://www.baidu.com'))
print(get('http://www.baidu.com'))

闭包函数实现

def get(url):
    def inner():
        return urlopen(url).read()
    return inner
baidu=get('http://www.baidu.com')
print(baidu())
print(baidu())

解释:这样对比直接用函数方便。只要调用一次,传好参数,把里面那一层函数赋值给一个变量,里面层函数里面直接用第一层外面传好的参数,这样就打破层级限制在全局可以直接使用函数里面的函数。以后用直接写变量名加括号就可以了不需要重复传值。

 
闭包一些用法
__closure__(查看有几个闭包参数)
def get(url):
    def inner():
        return urlopen(url).read()
    return inner
baidu=get('http://www.baidu.com')
print(baidu.__closure__)
print(baidu.__closure__[0].cell_contents)

#打印
(<cell at 0x0000000001E1C528: str object at 0x0000000001DB5C70>
http://www.baidu.com
总结

如果函数体内需要一个参数可以有两种方法:

  1.使用函数,但是函数每次调用都需要传值,麻烦一点

  2.使用闭包函数,只需要传一次参数,根据第一层的return拿到里面的函数直接调用即可,里面的函数的参数用的是上面一层外面传来的参数

三 装饰器

 

无参装饰器

简介

装饰器其实就是利用闭包来实现的。

1.为什么要用装饰器

  -开放封闭原则:对扩展是开放的,对修改是封闭的

2.什么是装饰器

  -用来装饰它人,装饰器本身可以是任意可调用对象,被装饰的对象也可以是任意可调用对象

-遵循的原则:

  1.不修改被装饰对象的源代码

  2.不修改被装饰对象的调用方式

-目标:

  在遵循原则1和2的前提,为被装饰对象添加新功能

 

实例
简单装饰器

现在被装饰对象没有带着参数

需求:将下面程序的运行时间统计出来,前提是不修改源代码和调用方式。

import time
def index():
    time.sleep(3)
    print('首页面')
#实现
def timer(func):
    def inner():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('耗时[%s]'%(start_time-stop_time))
    return inner
index=timer(index)
index()

#这样就不但没有修改源码以及调用方式就添加上了新功能:
#原因是直接把内部函数inner赋值给index,但是index上面还包了一层,inner就可以用上面那层的参数

针对上面的index=timer(index)python已经提供了一种语法就是做这个操作的。

import time
def timer(func):
    def inner():
        start_time=time.time()
        func()
        stop_time=time.time()
        print('耗时[%s]'%(stop_time-start_time))
    return inner

@timer
def index(): time.sleep(3) print('首页面') index()

解释:当程序运行到@time时python会把正下方index当做参数传给time函数,返回inner又赋给index,此时的index就已经是inner的内存地址了,可以加括号执行inner,但是inner上面还包了一层,inner函数可以直接用上面一层的参数。


装饰器修订

注:现在被装饰对象带着参数

import time
def timer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        func(*args,**kwargs)
        stop_time=time.time()
        print('耗时[%s]'%(stop_time-start_time))
    return inner

@timer
def home(name):
    time.sleep(2)
    print('欢迎%s回家'%name)
home('dick'

解释:

  很明显现在被装饰的函数带了一个参数,在home被调用的时候其实调用的是time下的inner,(解释:遇到@time装饰器的时候他会把下面的函数当做参数传给time装饰器,并返回inner函数的内存地址赋值给home)inner函数就必须接收,但是接受到的值不是这个函数用,他要原封不动的再传给真正被调用的函数就是func,func变量存的参数正在home函数的内存地址。

 
获取被装饰对象返回值

需求:拿到被装饰对象的返回值

import time
def timer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('耗时[%s]'%(stop_time-start_time))
        return res
    return inner
    
@timer
def home(name):
    time.sleep(2)
    print('欢迎%s回家'%name)
    return 'home'
res=home('dick')
print(res)

#打印 欢迎dick回家 耗时[2.0001144409179688] home

解释:

  要拿到被装饰对象的返回值得话必须在他被调用的地方获取返回值。如下咧:调用home并不是调用正在的home,而是再调inner,inner里面的func才是在调用真正的home,所以要在调用func时,func执行完之后把返回值返回,把返回值保存下来赋值给res,inner执行执行完之后,把func返回值在返回给inner的调用者(就是home),然后调用者接收,打印。

 
获取函数帮助信息

注:low版

直接在func执行完后,获取func的帮助信息并赋值给inner的帮助信息,这样你不管是哪个被装饰的对象都可以打印出帮助信息

import time
def timer(func):
    def inner(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('耗时[%s]'%(stop_time-start_time))
        return res
    inner.__doc__=func.__doc__
    return inner
    
@timer
def home(name):
    '''
    home的帮助文档
    :param name:
    :return:
    '''
    time.sleep(2)
    print('欢迎%s回家'%name)
    return 'home'
res=home('dick')
print(help(home))

#打印
欢迎dick回家
耗时[2.0001139640808105]
Help on function inner in module __main__:
inner(*args, **kwargs)
    home的帮助文档
    :param name:
    :return:
None
home

注:高雅版

可以使用wraps装饰器,他显示的就是func的所有信息,而且非常全

from functools import wraps
import time
def timer(func):
    @wraps(func)
    def inner(*args,**kwargs):
        start_time=time.time()
        res=func(*args,**kwargs)
        stop_time=time.time()
        print('耗时[%s]'%(stop_time-start_time))
        return res
    return inner
@timer
def home(name):
    '''
    home的帮助文档
    :param name:
    :return:
    '''
    time.sleep(2)
    print('欢迎%s回家'%name)
    return 'home'
res=home('dick')

有参装饰器

问题
low版(解释版)

现在装饰器需要一个参数,该怎么办?

下面一个装饰器就需要一个cmd参数,该往哪里给他传值呢?是在inner那里面传值吗?不是的因为inner接收的参数只是接收被装饰对象的参数,不肯再调用自己的函数时,还要给装饰器传参。还是在auth里面传值?不是的,因为auth里面只接收被装饰对象的内存地址。解决方法请看第二个代码块。

#有问题的代码

def auth(func):
        def inner(*args,**kwargs):
            if user['user']:
                res=func(*args,**kwargs)
                return res
            if cmd == 'file':
                name=input('username>>')
                pwd=input('password>>')
                if name =='admin' and pwd=='123':
                    user['user']=user
                    res=func(*args,**kwargs)
                    return res
            elif cmd == 'mysql':
                print('mysql auth')
            else:
                pass
        return inner

 

解决

可以在上面在定义一层函数,把装饰器需要的值包给它,这样装饰器就可以直接去调用这个参数,这个就称为有参装饰器。

def auth(cmd):
    def wrapper(func):
        def inner(*args,**kwargs):
            if user['user']:
                res=func(*args,**kwargs)
                return res
            if cmd == 'file':
                name=input('username>>')
                pwd=input('password>>')
                if name =='admin' and pwd=='123':
                    user['user']=user
                    res=func(*args,**kwargs)
                    return res
            elif cmd == 'mysql':
                print('mysql auth')
            else:
                pass
        return inner
    return warpper
    
def home(name):
    time.sleep(2)
    print('欢迎%s'%name)
    return 'home'

#调用的方法
wrapper=auth('file')
home=wrapper(home)
home('dick')

调用方法解释:

  先调用auth然后给装饰器传了一个参数(这个就是给装饰器用的参数),拿到返回值就是warpper函数的内存地址赋值给wrapper,这个函数就是用来接收被装饰对象的内存地址的,然后加括号执行wrapper函数把被装饰对象的内存地址传给他。返回来inner函数的内存地址赋值给home变量,inner函数就只专门接收被装饰对象的参数,现在被装饰对象需要一个name参数,在调用home(其实就是里面的inner函数),给传了一个dick实参,最后登录功能就给被装饰对象添加上了。

 

分级解释:

  auth函数是专门给装饰器用的参数

  wrapper函数是专门接收被装饰对象的内存地址的

  inner函数是专门接收被装饰对象的参数的

 
高雅版(使用python提供的方法版)
import time
user={'user':None}
def auth(cmd):
    def wrapper(func):
        def inner(*args,**kwargs):
            if user['user']:
                res=func(*args,**kwargs)
                return res
            if cmd == 'file':
                name=input('username>>')
                pwd=input('password>>')
                if name =='admin' and pwd=='123':
                    user['user']=user
                    res=func(*args,**kwargs)
                    return res
            elif cmd == 'mysql':
                print('mysql auth')
            else:
                pass
        return inner
    return wrapper
@auth('mysql') #函数执行完这个地方就是@wrapper装饰器了
def home(name):
    time.sleep(2)
    print('欢迎%s'%name)
    return 'home'

#调用的方法
home('dick') #其实就是在执行inner

解释:

  代码从上到下执行,碰见@auth(‘mysql’),这时看见名字加括号就下面先不用管直接调用auth,并把参数‘mysql’传给他,并返回wrapper的内存地址,这时@那个地方就是wrapper装饰器了(auth函数只不过是给装饰器传了参数)。这时@wrapper就把正下方的home函数内存地址当做参数传给wrapper自己,并返回inner函数的内存地址赋值给home,此时的home已经不是被装饰对象的被装饰对象已经刚刚被当做参数传给了wrapper,此时的home是最里层的inner函数,作用是接收被装饰对象的参数,然后执行func(这时的func就是被装饰对象),然后把inner接收的值在原封不动的传给func(被装饰对象)

添加多个装饰器

例题

需求:

  把被装饰对象即添加统计时间功能也添加认证功能

:统计时间的装饰器要在认证装饰器的下面,因为要试试在认证装饰器的上面的话会把认证装饰器运行的时间也给统计上,那样就不是被装饰对象的真正运行时间了

import time
user={'user':None}
def auth(cmd):
    def wrapper(func):
        def inner(*args,**kwargs):
            if user['user']:
                res=func(*args,**kwargs)
                return res
            if cmd == 'file':
                name=input('username>>')
                pwd=input('password>>')
                if name =='admin' and pwd=='123':
                    user['user']=user
                    res=func(*args,**kwargs)
                    return res
            elif cmd == 'mysql':
                print('mysql auth')
                res=func(*args,**kwargs)
                return res
            else:
                pass
        return inner
    return wrapper

def timer(func): def inner(*args,**kwargs): start_time=time.time() res=func(*args,**kwargs) stop_time=time.time() print('耗时[%s]'%(stop_time-start_time)) return res return inner @auth('file') @timer def home(name): time.sleep(2) print('欢迎%s'%name) return 'home' #调用的方法 home('dick')

解释:

  直接在被装饰对象的正上方,加上需要装饰的功能即可。在遇到@auth('file')装饰器时因为用的事mysql认证所有执行mysql的if分支,运行func函数(func函数就是被装饰对象函数的内存地址),但是在被装饰对象是发现还有一个装饰器@timer(这是一个无参装饰器)于是time就把被装饰对象的内存地址传给自己,然后又给添加上了统计时间功能。

四 函数的递归调用
 

简介

指的是在嵌套调用的基础之上(嵌套调用是在一个函数里面调用另外一个函数),指的是在调用自己本身。(递归调用是函数嵌套调用的一种特殊形式,函数在调用时,直接或间接调用了自身,就是函数的递归调用。)

 

递归分为两个重要的阶段:

  地推和回溯

 

:在别的语言里面可以给递归优化叫做尾递归优化,python没有递归优化这么一说

例子

需求:一共有五个人,问第五个人的年龄第五个人说比第四个人大两岁,问第四个人的年龄说比第三个人大两岁,以此类推第一个人18岁,利用递归求出第五给人的年龄。

def func(a):
    if a ==1:
        return 18
    return func(a-1)+2
print(func(5))

#打印
26

解释:

  定义了一个func的函数传一个参数就是那五个人的个数,先判断人的个数是不是已经到了第一个了如果到了就返回18,如果还没有到第一个人就继续减1加二知道取到第一个人返回十八接下来就是回溯阶段给18加上了4次2,每次调用在括号外面加2意思就是给返回值加二就是最后那个18

 

流程图

递归小练习

需求:把列表里面的元素都取出来

l=[1,2,[3,4,[5]]]
def func(l_list):
    for i in l_list:
        if type(i) == list:
            func(i)
        else:
            print(i)
func(l)

#打印
1
2
3
4
5

解释:

  直接使用递归调用,for循环这个列表判断每一个元素如果函数列表就再次调自己进行递归,再循环这个列表直到递归结束,如果不是列表了就直接打印元素

 
总结递归调用

1.进入下一次递归时,问题的规模必须降低。

2.递归调用必须有一个明确的接结束条件。

3.在python没有尾递归优化,递归调用的效率就是不高。

二分法(配合递归调用)

简介

二分法必须纯数字,而且数字必须是排这序的数字

 
实例

需求1:高效率判断列表里面有没有想要的值

l=[1,2,10,30,33,99,101,200,301,402]
def func(num,l):
    print(l)
    if l:
        mid=len(l)//2
        if num > l[mid]:
            l=l[mid+1:]
        elif num < l[mid]:
            l=l[:mid]
        else:
            print('ok')
            return
        func(num,l)
    else:
        print('no')
func(30,l)

#打印
[1, 2, 10, 30, 33, 99, 101, 200, 301, 402]
[1, 2, 10, 30, 33]
[30, 33]
[30]
ok

#没有值打印
[1, 2, 10, 30, 33, 99, 101, 200, 301, 402]
[1, 2, 10, 30, 33]
[30, 33]
[30]
[]
no
View Code

解释:

  定义一个函数利用递归调用,第一层if判断是为了想要的值没有在列表里面而递归到最后没有找到防止报错用的,列表都切空了都没有找到值说明里面就没有想要的值。如果列表里面还有值就切分唯二,利用二分法形式,判断用户传的值是大于还是小于中间这个值,判断完成后直接切分,把切分的那部分列表在递归下去再判断列表是不是空的,如果是空的就说明没有值,如果还有值还继续判断中间值是大于小于还是等于用户传来的值,以此类推。

 

需求2:如果有值打印它的索引

l=[1,2,10,30,33,99,101,200,301,402]
def func(num,l):
    if l:
        mid=len(l)//2
        if num > l[mid]:
            l=l[mid+1:]
        elif num < l[mid]:
            l=l[:mid]
        else:
            return num
        return func(num,l)
    else:
        print('no')
        return None
res=func(200,l)
if res:
    print(l.index(res))
    
#打印
7
View Code
五 匿名函数

 

简介

匿名函数即没有绑定名字的函数,没有绑定名字意味着,用一次就会被回收。

所以说匿名函数的应用场景就是某个功能只用一次就结束了。

例子

需求:比较下面字典里面values值,并返回最大values得key

:比较的是value但是但是返回的是字典的key。

salaries={
    'egon':3000,
    'alex':100000000,
    'wupeiqi':10000,
    'yuanhao':2000
}
#求最大值得函数
print(max(salaries,key=lambda n:salaries[n]))

#打印
alex

#按工资多少排序
print(sorted(salaries,key=lambda n:salaries[n]))

#打印
['yuanhao', 'egon', 'wupeiqi', 'alex']

解释:

  max函数就是求最大值得函数但是你要是直接把字典丢进去的话,那默认比较的是字典的key,max函数就是一个迭代器,他也是循环这个字典取出每一个key,但是max函数里面有一个key的参数,这个参数的作用就是你把取出来的值别做处理了交个我后面这个功能,此时key后面就是一个匿名函数了,匿名函数也有一个参数作用就是接收max函数循环字典取出的key,冒号后面就是匿名函数的函数体,匿名函数自带return,返回字典里面的value所以max的比较条件就不是字典里面的key了而是value。

内置函数

 

简介

内置函数就是python已经给我们提供好了的功能

常用的内置函数

max函数(取出最大的值)

详情看匿名函数

 

min函数(取出最小值)

详情看匿名函数

 

sorted函数(排序)

详情看匿名函数

 

map函数(映射)

需求:把列表里面的所有值得末尾添加一个_sb

name=['admin','root','dick']
res=map(lambda x:x+'_sb',name)
print(list(res))

#打印
['admin_sb', 'root_sb', 'dick_sb']

解释:

  这个方法是把可可迭代对象放在后面,功能放在前面,map函数不断循环列表的面值交给匿名函数,匿名函数就会把每个元素后面都加上了指定了_sb

 

例二

print(list(map(lambda x,y:x+y,[1,2,3],[1,2,3])))

#打印
[2, 4, 6]

 

reduce函数(合并)

需求:得出1到100的合

reduce() 函数会对参数序列中元素进行累积。

函数将一个数据集合(链表,元组等)中的所有数据进行下列操作:用传给 reduce 中的函数 function(有两个参数)先对集合中的第 1、2 个元素进行操作,得到的结果再与第三个数据用 function 函数运算,最后得到一个结果。

from functools import reduce
print(reduce(lambda x,y:x+y,range(1,101)))

#打印
5050

解释:

  和其他内置函数一样,也是for循环后面的range,这次是需要两个参数是在range里面取到两个数值后进行加法运算得出一个结果后,再到range里面取一个数值和刚才得出结果的数值在进行加法运算以此类推。

 

:还可以指定初始值,意思就是拿着这个初始值在去range里面取一个值进行运算,以此类推。

from functools import reduce
print(reduce(lambda x,y:x+y,range(1,101),100))

#打印
5150

例二

from functools import reduce
print(reduce(lambda x,y:x+y,[1,2,3]))

#打印
6

filter函数(过滤)

需求:把列表里面带末尾sb的元素取出来

name=['admin_sb', 'root_sb', 'dick_sb','meng']
res=filter(lambda name:name.endswith('sb'),name)
print(list(res))

#打印
['admin_sb', 'root_sb', 'dick_sb']

解释:

  他还是把功能放在前面,可迭代对象放在后面,内置还是用for循环,在可迭代对象里面取出一个值放到前面的匿名函数里面,判断看看这个元素的末尾是不是由sb关键字,有就返回留下来。

 
abs函数(负数变正数)
print(abs(-5))

#打印
5

 

all函数(判断可迭代对象的元素是不是都是True)

:如果可迭代对象里面有值但是有一个元素是空的情况下就返回false,如果所有元素都有值就返回true。但是如果all里面传了一个空的可迭代对象也返回true。

name=['admin_sb', 'root_sb', 'dick_sb','meng',]
print(all(name))

#打印
true

name=['admin_sb', 'root_sb', 'dick_sb','meng','']
print(all(name))

#打印
false

 

any函数(判断可跌打对象里的元素是不是都是false)

如果一个可迭代对象里面的但凡有一个元素是true的都返回true

注:如果传的可迭代对象本来就是空的话返回的是false

l=[[],'',0,1]
print(any(l))

#打印
true

print(any([]))
#打印
false

 

bin函数(十进制转二进制)

:0b开头的代表二进制

print(bin(10))

#打印
0b1010

 

oct函数(十进制转成八进制)

:0o代表八进制

print(oct(10))

#打印
0o12

 

hex函数(十进制转十六进制)

:0x表示十六进制

print(hex(10))

#打印
0xa

 

bytes(字节类型可以转换成bytes类型)
print(bytes('hello',encoding='utf8'))

#打印
b'hello'

 

callable(判断是否可被调用)
print(callable(map))

#打印
true

 

chr函数(按阿斯玛表把数字转成对应的字符)

:65-90(是大写字母A-Z) 97-122(是小写字母a-z)

print(chr(65))
print(chr(90))
print(chr(97))
print(chr(122))

#打印
A
Z
a
z

 

ord函数(按阿斯玛表把字符转成对应的数字)
print(ord('a'))
print(ord('z'))

#打印
97
122

 

dir函数(查看一个模块里面有哪些名字可用)

注:这就是查看time模块里面所有可以用的名字

import time
print(dir(time))

#打印
['altzone', 'asctime', 'clock', 'ctime', 'daylight'......]

 

divmode(算出两个数的商和余数)

注:算数商的余数都保存都元组里

print(divmod(10,3))

#打印
(3,1)

 

enumerate函数(可以去除带索引的索引和值)
l=['a','b','c']
for i,k in enumerate(l):
    print(i,k)
    
#打印
0 a
1 b
2 c

 

help(查看函数的帮助信息)
def func():
    '''
    帮助信息
    :return:
    '''
    pass
print(help(func))

#打印
Help on function func in module __main__:
func()
    帮助信息
    :return:
None

 

isinstance(判断数据类型)
print(isinstance(1,int))

#打印
true

 

pow(谁的几次方)

print(pow(10,2))

#打印
100

 

slice(保留切片状态)

如果多个列表使用一个切片的流程可以保留切片状态这样其他的列表直接调用就可以

l=['a','b','c','d','e','f']
l1=['a','b','c','d','e','f']
l2=['a','b','c','d','e','f']

res
=slice(1,6,2) print(l[res]) print(l1[res]) print(l2[res]) #打印 ['b', 'd', 'f'] ['b', 'd', 'f'] ['b', 'd', 'f']

 

sum函数(求和)

注:都是循环里面的元素拿出一个加一个

print(sum([1,2,3,4]))
print(sum(range(10)))

#打印
10
45

 

__import__函数(把字符串转成模块)

注:可以吧用户输入的字符串比如说time就可以转成真正time模块的功能了

res=__import__('time')
res.sleep(3)
迭代器

 

简介

1.什么是迭代器

是一个重复的过程,每一次重复都是基于上一次结果来的,每一次都成为一次迭代

小例题:

这就称之为迭代,每一次重复都是基于上一次的结果来的

l=['a','b','c']
count=0
while count < len(l):
    print(l[count])
    count+=1
 
2.为什么要用迭代器

就像上面的例子,while很有很有局限性,只能迭代有索引的,而对于没有所以得如,字典,文件。所以必须找出一种不依赖所以得取值方案这就是迭代器。

 

3.什么是可迭代对象

只要是对象内置有___iter__方法,就称为可迭代对象。

如:字符串,字典,列表,文件,集合,元组

 

4.什么是迭代器对象

对象即内置有__iter方法又内置有__next__方法

如:文件

 

总结:

只要是迭代器对象那肯定就是可迭代对象,可迭代对象不一定是迭代器对象

 
5.例题解释

解释:通过可迭代对象可以拿到迭代器对象(就是通过__iter__方法制作迭代器对象)

:那为什么可迭代对象和可迭代器对象都有__iter__方法呢?因为为了统一标准

l=['a','b','c']
count=0
while count < len(l):
    print(l[count])
    count+=1

 

6.迭代器对象的用处

就是取出一些不带索引类型的值,如字典等。

例题

引子

__next__一次就取出一个值,有一个内置方法next()和__next__一样,这个内置方法内部原理就是再调__next__,如果的值超出了存值的范围就会报一个结束信号。

l={'a':1,'b':2,'c':3}
l_dic=l.__iter__()
print(l_dic.__next__())
print(next(l_dic))
print(next(l_dic))
print(next(l_dic))

#打印
a
b
c
StopIteration

 

迭代文件

with open('test','r') as f:
    print(next(f),end='')
    print(next(f),end='')
    print(next(f),end='')
    
#打印
1111
2222
3333

循环迭代,因为取到字典里面有值得情况下还取还报终止信号,所有用try捕捉一下异常、

l={'a':1,'b':2,'c':3}
l_doc=l.__iter__()
while True:
    try:
        print(next(l_doc))
    except StopIteration:
        break

#打印
a
b
c

 

for循环(实现机制)

如上面的例子,有没有什么方法可以帮我自动把可迭代对象变成迭代器对象还可以帮我自动捕捉异常呢?有,这就是for循环。

dic={'a':1,'b':2,'c':3}
for i in dic:
    print(i)
    
#打印
a
b
c

解释:

  in后面的类型必须是可迭代对象(那他是不是迭代器对象呢?不一定),如果后面的类型就是迭代器对象也会执行他的__iter__方法只不过还是它本身。它会帮我们把可迭代对象,做成迭代器对象,遍历完所有值还会自动捕捉异常。

总结

迭代器的优点:

  1.提供了一种统一的迭代取值方式,这种方式不再依赖索引

  2.更加的节省内存,同时在内存里面只有一个值(文件就是一行值)。

迭代器的缺点:

  1.next一次取一个值,不能取指定的一个位置的值,一次性取值只往前走,不能后退。

  2.无法统计长度

补充

解释:下面的两行代码是判断字符串是不是可迭代对象和是不是迭代器对象的方法。

from collections import Iterator,Iterable
print(isinstance('aaa',Iterable))
print(isinstance('aaa',Iterator))

#打印
True
False
八 生成器

 

简介

来源:

  迭代器的一种应用形式起名叫生成器

定义:

  只要函数内部出现yield关键字,那么在调用该函数,将不会立即执行函数体代码,会得到一个结果,该结果就是生成器对象。

功能:

  1.为我们提供了一种自定义迭代器的方式,生成器把函数做成迭代器提供给我们用

  2.对比return可以返回多个值,挂起函数的运行状态,遇到yield就会挂起,next一次就会把这次的结果返回,当又遇到下一个yield是再次挂起运行状态。

 

例题解1:

def func():
    print('aaa')
    yield 1
    print('bbb')
    yield 2
    print('ccc')
    yield 3
res=func()

print(res)
res.__next__()
res.__iter__()

#打印
<generator object func at 0x00000000021FF4C0>

解释:

  执行了func函数按理说是直接打印里面的内容可是现在却没有反应,打印它返回的结果是一个生成器对象内存地址。

 

例题解2:

 但是他还有iter和next方法,这说明生成器本质就是迭代器。利用yield关键字把函数里面的代码做成生成器,功能就是往出造值的。迭代器可以调next方法next一次出一行值,yield后面的值就是返回值,也可以一次返回多个值,已元组的方式返回。

def func():
    print('aaa')
    yield 1,2,3
    print('bbb')
    yield 2
    print('ccc')
    yield 3
res=func()

print(res.__next__())
print(res.__next__())
print(res.__next__())

#打印
aaa
(1,2,3)
bbb
2
ccc
3

 

例题解3:

只要是迭代器就可以被for循环

def func():
    print('aaa')
    yield 1
    print('bbb')
    yield 2
    print('ccc')
    yield 3
res=func()
for i in res:
    print(i)
    
#打印
aaa
1
bbb
2
ccc
3

 

练习

需求1:利用生成器造一个range功能

def my_range(start,stop,set=1):
    while start <= stop:
        yield start
        start+=set
for i in my_range(1,5):
    print(i)

#打印
1
2
3
4
5
需求2:写一个tail -f test.log |grep 404

先执行log函数传了一个文件路径,log函数打开这个文件,直接把光标移动到末尾,然后循环取值如果有内容就yield 挂起返回生成器对象,然后生成器当做参数传给grep函数,另一个参数是过滤的关键字,当grep里面for循环迭代生成器对象时判断这行数据有没有我想要的有就打印没有就不打印,for循环完处理完这个(就是next完)之后继续返回log函数判断文件最后有没有值,有的话在yield住返回一个生成器对象,

解释:就是刚yield住就被for循环取走for处理完后返回log函数继续判断有没有值有值的话在让for取走。就是造一个值取走一个,造一个值取走一个。

import time
def log(log):
    with open(log,'r') as f:
        f.seek(0,2)
        while True:
            line=f.read()
            if line:
                yield line
            else:
                time.sleep(0.2)

def grep(line,keywords):
    for i in line:
        if keywords in i:
            print(i)

grep(log('test'),'404')

修订版

import time
def log(log):
    with open(log,'r') as f:
        f.seek(0,2)
        while True:
            line=f.read()
            if line:
                yield line
            else:
                time.sleep(0.2)

def grep(line,keywords):
    for i in line:
        if keywords in i:
            yield i

res=grep(log('test'),'404')
for i in res:
    print(i)

yield表达式形式应用

简介

send可以给yield传值也可以下一个,和next功能一样但是多了一个传值的功能。

功能就是可以不断的往函数里面传值。

def eater(name):
    print('%s 开动啦'%name)
    while True:
        food=yield 123
        print('%s 开始吃 %s'%(name,food))
g=eater('admin')
g.send(None)
g.send('骨头')
next(g) 
g.send('')

#打印
admin 开动啦
admin 开始吃 骨头
admin 开始吃 None
admin 开始吃 肉

解释:

  第一个调用给yield传了一个none,只要给yield传值就必须先传一个none,表示初始化,next(g)也是初始化二选一

  第二个又给yield send一个值因为send也有next功能,表示先传值再往下next

  第三次用的是next(g) 却打印了‘admin 开始吃 None’,按理不是说还是吃骨头吗,不是的因为next也会传一个值,只不过是none

  第四次又在yield那卡住了,又send一个肉给yield

 

yield的返回值

def eater(name):
    food_list=[]
    print('%s 开动啦'%name)
    while True:
        food=yield food_list
        print('%s 开始吃 %s'%(name,food))
        food_list.append(food)

g=eater('admin')
g.send(None)
g.send('骨头')
print(g.send(''))

#打印
admin 开动啦
admin 开始吃 骨头
admin 开始吃 肉
['骨头', '']

 

明了实例
def a():
    while True:
        x=yield
        print(x)
b=a()
b.send(None)
b.send(1)
b.send(2)
b.send(3)

#打印
1
2
3

 

应用场景

简介

1.面向过程觉得不是用函数编程那么简单。

2.面向过程的编程思想:核心是过程二字,过程及解决问题的步骤,即先干什么在干什么,基于该思想去编写程序,就好比再设计一条流水线,是一个机械式的编程思想。

 

优点:

  复杂的问题流程化,进而简单化

缺点:

  可扩展性差

 

实现代码

需求:把指定目录下所有的文件和子目录下所有文件里面只要含有关键字的就打印出路径

import os

def init(func):
    def inner(*args,**kwargs):
        g=func(*args,**kwargs)
        next(g)
        return g
    return inner

def seach(path_file,oppen_yield):
    g = os.walk(path_file)
    for path,_,file in g:
        for files in file:
            oppen_yield.send(r"%s\%s"%(path,files))

@init
def oppen(cat_yield):
    while True:
        path_file=yield
        with open(path_file,'rb') as f:
            cat_yield.send((f,path_file))
@init
def cat(grep_yield):
    while True:
        file_open,file_path=yield
        for line in file_open:
            res=grep_yield.send((line,file_path))
            if res:
                break
@init
def grep(keywords,pinrter_yield):
    keywords=keywords.encode('utf8')
    while True:
        file_cat,file_path=yield
        if keywords in file_cat:
            yield True
            pinrter_yield.send(file_path)

@init
def pinrter():
    while True:
        pinrter_yield=yield
        print(pinrter_yield)

seach(r'D:\PycharmProj\fhm\老男孩', oppen(cat(grep('你好',pinrter()))))
View Code
九 三元表达式,生成器表达式,列表解析

 

三元表达式

简介

处理简单的if分支,给他们精简到一行去。

示例

if判断

把成功的结果放到左面,把失败的结果放到右面。

name=input('>>')
print('nb' if name=='dick' else 'sb')

#打印
>>dick
nb

列表解析

示例

需求:把egg1到egg5放到列表里面

解释:本身就在列表里面,所有运行的结果直接在列表里面

print(['egg%s'%i for i in range(5)])

#打印
['egg0', 'egg1', 'egg2', 'egg3', 'egg4']

扩展需求

把大于等于3的存到列表

print(['egg%s'%i for i in range(5) if i >=3])

#打印
['egg3', 'egg4']

 

练习题

把a去掉

g=['a','b','c']
print([i.upper() for i in g if not i=='a'])

#打印
['B', 'C']

生成器表达式

示例

这样就可以变成一个生成器,生成器详解看迭代器与生成器

g=('egg%s'%i for i in range(5) if i >= 3)
print(next(g))

#打印
egg3

 

posted on 2018-06-12 17:02  冯海猛  阅读(284)  评论(0编辑  收藏  举报

导航