周末班:Python基础之函数进阶

迭代器和生成器

迭代和可迭代

什么是迭代(iteration)?
若是给定一个list或tuple,咱们要想访问其中的某个元素,咱们能够经过下标来,若是咱们想要访问全部的元素,那咱们能够用for循环来遍历这个list或者tuple,而这种遍历咱们就叫作迭代。 html

可迭代(iterable)? python

其实你已经知道,不是全部的数据类型都是可迭代的。那么可迭代的数据类型都有什么特色呢? git

print(dir([1,2]))
print(dir((2,3)))
print(dir({1:2}))
print(dir({1,2}))
['__add__', '__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__iadd__', '__imul__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__reversed__', '__rmul__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 'remove', 'reverse', 'sort']
['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'count', 'index']
['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
['__and__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__gt__', '__hash__', '__iand__', '__init__', '__ior__', '__isub__', '__iter__', '__ixor__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__or__', '__rand__', '__reduce__', '__reduce_ex__', '__repr__', '__ror__', '__rsub__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__xor__', 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 'union', 'update']
结果

总结一下咱们如今所知道的:能够被for循环的都是可迭代的,要想可迭代,内部必须有一个__iter__方法。迭代器协议 面试

from collections import Iterable

l = [1,2,3,4]
t = (1,2,3,4)
d = {1:2,3:4}
s = {1,2,3,4}

print(isinstance(l,Iterable))
print('__iter__' in dir([l]))
print(isinstance(t,Iterable))
print('__iter__' in dir([t]))
print(isinstance(d,Iterable))
print('__iter__' in dir([d]))
print(isinstance(s,Iterable))
print('__iter__' in dir([s]))
判断是否可迭代

接着分析,__iter__方法作了什么事情呢?算法

print([1,2].__iter__())

结果
<list_iterator object at 0x1024784a8>

执行了list([1,2])的__iter__方法,咱们好像获得了一个list_iterator,如今咱们又获得了一个新名词——iterator。编程

iterator,这里给咱们标出来了,是一个计算机中的专属名词,叫作迭代器。api

迭代器

咱们调用可迭代对象的__iter__方法以后,获得了一个新东西--迭代器。app

那迭代器是什么呢?请继续日后阅读吧。ssh

迭代器协议

既什么叫“可迭代”以后,又一个历史新难题,什么叫“迭代器”?ide

虽然咱们不知道什么叫迭代器,可是咱们如今已经有一个迭代器了,这个迭代器是一个列表的迭代器。

咱们来看看这个列表的迭代器比起列表来讲实现了哪些新方法,这样就能揭开迭代器的神秘面纱了吧?

'''
dir([1,2].__iter__())是列表迭代器中实现的全部方法,dir([1,2])是列表中实现的全部方法,都是以列表的形式返回给咱们的,为了看的更清楚,咱们分别把他们转换成集合,
而后取差集。
''' #print(dir([1,2].__iter__())) #print(dir([1,2])) print(set(dir([1,2].__iter__()))-set(dir([1,2]))) 结果: {'__length_hint__', '__next__', '__setstate__'}

咱们看到在列表迭代器中多了三个方法,那么这三个方法都分别作了什么事呢?

iter_l = [1,2,3,4,5,6].__iter__()
#获取迭代器中元素的长度
print(iter_l.__length_hint__())
#根据索引值指定从哪里开始迭代
print('*',iter_l.__setstate__(4))
#一个一个的取值
print('**',iter_l.__next__())
print('***',iter_l.__next__())

这三个方法中,能让咱们一个一个取值的神奇方法是谁?

没错!就是__next__

在for循环中,就是在内部调用了__next__方法才能取到一个一个的值。

那接下来咱们就用迭代器的next方法来写一个不依赖for的遍历。

l = [1,2,3,4]
l_iter = l.__iter__()
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)
item = l_iter.__next__()
print(item)

这是一段会报错的代码,若是咱们一直取next取到迭代器里已经没有元素了,就会抛出一个异常StopIteration,告诉咱们,列表中已经没有有效的元素了。

这个时候,咱们就要使用异常处理机制来把这个异常处理掉。

l = [1,2,3,4]
l_iter = l.__iter__()
while True:
    try:
        item = l_iter.__next__()
        print(item)
    except StopIteration:
        break

那如今咱们就使用while循环实现了本来for循环作的事情,咱们是从谁那儿获取一个一个的值呀?是否是就是l_iter?好了,这个l_iter就是一个迭代器。

迭代器遵循 迭代器协议 :必须拥有__iter__方法和__next__方法。

还帐:next和iter方法

如此一来,关于迭代器和生成器的方法咱们就还清了两个,最后咱们来看看 range()是个啥。首先,它确定是一个可迭代的对象,可是它是不是一个迭代器?咱们来测试一下

print('__next__' in dir(range(12)))  #查看'__next__'是否是在range()方法执行以后内部是否有__next__
print('__iter__' in dir(range(12)))  #查看'__next__'是否是在range()方法执行以后内部是否有__next__

from collections import Iterator
print(isinstance(range(100000000),Iterator))  #验证range执行以后获得的结果不是一个迭代器
range函数的返回值是一个可迭代对象

 

 

为何要有for循环

基于上面讲的列表这一大堆遍历方式,聪明的你立马看除了端倪,因而你不知死活大声喊道,你这不逗我玩呢么,有了下标的访问方式,我能够这样遍历一个列表啊

l=[1,2,3]

index=0
while index < len(l):
    print(l[index])
    index+=1

#要毛线for循环,要毛线可迭代,要毛线迭代器

没错,序列类型字符串,列表,元组都有下标,你用上述的方式访问,perfect!可是你可曾想过非序列类型像字典,集合,文件对象的感觉,因此嘛,年轻人,for循环就是基于迭代器协议提供了一个统一的能够遍历全部对象的方法,即在遍历以前,先调用对象的__iter__方法将其转换成一个迭代器,而后使用迭代器协议去实现循环访问,这样全部的对象就均可以经过for循环来遍历了,并且你看到的效果也确实如此,这就是无所不能的for循环,觉悟吧,年轻人

生成器

初识生成器

咱们知道的迭代器有两种:一种是调用方法直接返回的,一种是可迭代对象经过执行 iter方法获得的,迭代器有的好处是能够节省内存。

若是在某些状况下,咱们也须要节省内存,就只能本身写。咱们本身写的这个能实现迭代器功能的东西就叫生成器。

 

Python 中提供的 生成器:

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

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

 

生成器Generator:

本质:迭代器 ( 因此自带了 __iter__方法和 __next__方法,不须要咱们去实现)

特色:惰性运算,开发者自定义

生成器函数

一个包含yield关键字的函数就是一个生成器函数。

yield和return同样能够从函数中返回值,可是yield又不一样于return,return的执行意味着程序的结束,只能返回一次,yield能够返回屡次。

调用生成器函数不会获得返回的具体的值,而是获得一个生成器对象。

每一次从这个可迭代对象获取值,就能推进函数的执行,获取新的返回值。直到函数执行结束(yield像是拥有可以让函数暂停的魔力)。

 

def my_range():
    print('我是一个生成器函数')
    n = 0
    while 1:
        yield n
        n += 1

生成器有什么好处呢?就是不会一会儿在内存中生成太多数据

接下来,咱们在这个函数的基础上来写一个咱们本身的range函数,实现开始和结束

def my_range2(start, stop):
    n = start
    while n < stop:
        yield n
        n += 1

再进一步,实现步长:

def my_range3(start, stop, step):
    n = start
    while n < stop:
        yield n
        n += step

生成器本质上就是个迭代器,咱们根据本身的想法创造的迭代器,它固然也支持for循环:

for i in my_range3(1, 10, 2):
    print(i)

 

更多应用

import time


def tail(filename):
    f = open(filename)
    f.seek(0, 2) #从文件末尾算起
    while True:
        line = f.readline()  # 读取文件中新的文本行
        if not line:
            time.sleep(0.1)
            continue
        yield line

tail_g = tail('tmp')
for line in tail_g:
    print(line)
生成器监听文件输入的例子

send

yield能够返回值,也能够接收值。

经过生成器的send方法能够给yield传值。

def eat(name):
    print('%s要开始吃了!' % name)
    while 1:
        food = yield
        print('{}在吃{}'.format(name, food))


a = eat('alex')
a.__next__()  # 初始化,让函数暂停在yield处
a.send('包子')  # send两个做用:1.给yield传值 2.继续执行函数
a.send('饺子')

yield能够同时返回值和接收值。

def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
next(g_avg)
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
计算移动平均值(1)
def init(func):  #在调用被装饰生成器函数的时候首先用next激活生成器
    def inner(*args,**kwargs):
        g = func(*args,**kwargs)
        next(g)
        return g
    return inner

@init
def averager():
    total = 0.0
    count = 0
    average = None
    while True:
        term = yield average
        total += term
        count += 1
        average = total/count


g_avg = averager()
# next(g_avg)   在装饰器中执行了next方法
print(g_avg.send(10))
print(g_avg.send(30))
print(g_avg.send(5))
计算移动平均值(2)_预激协程的装饰器

yield from

在一个生成器中引用另一个生成器。

def gen1():
    for c in 'AB':
        yield c
    for i in range(3):
        yield i

print(list(gen1()))

def gen2():
    yield from 'AB'
    yield from range(3)

print(list(gen2()))
yield from

 

列表推导式和生成器表达式

#老男孩因为峰哥的强势加盟很快走上了上市之路,alex思来想去决定下几个鸡蛋来报答峰哥

egg_list=['鸡蛋%s' %i for i in range(10)] #列表解析

#峰哥瞅着alex下的一筐鸡蛋,捂住了鼻子,说了句:哥,你仍是给我只母鸡吧,我本身回家下

laomuji=('鸡蛋%s' %i for i in range(10))#生成器表达式
print(laomuji)
print(next(laomuji)) #next本质就是调用__next__
print(laomuji.__next__())
print(next(laomuji))
峰哥与alex的故事

总结:

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

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

3.Python不但使用迭代器协议,让for循环变得更加通用。大部份内置函数,也是使用迭代器协议访问对象的。例如, sum函数是Python的内置函数,该函数使用迭代器协议访问对象,而生成器实现了迭代器协议,因此,咱们能够直接这样计算一系列值的和:

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

而不用画蛇添足的先构造一个列表:

sum([x ** 2 for x in range(4)]) 

 

 

更多精彩请见——迭代器生成器专题:http://www.javashuo.com/article/p-ryyxmfxu-y.html

生成器相关的面试题

def demo():
    for i in range(4):
        yield i

g=demo()

g1=(i for i in g)
g2=(i for i in g1)

print(list(g1))
print(list(g2))
面试题1
def add(a, b):
    return a + b


def func():
    for i in range(4):
        yield i


g = func()
for n in [1, 10]:
    g = (add(n, i) for i in g)


print(list(g))
面试题2

内置函数

 

    Built-in Functions    
abs() dict() help() min() setattr()
all() dir() hex() next() slice()
any() divmod() id() object() sorted()
ascii() enumerate() input() oct() staticmethod()
bin() eval() int() open() str()
bool() exec() isinstance() ord() sum()
bytearray() filter() issubclass() pow() super()
bytes() float() iter() print() tuple()
callable() format() len() property() type()
chr() frozenset() list() range() vars()
classmethod() getattr() locals() repr() zip()
compile() globals() map() reversed() __import__()
complex() hasattr() max() round()  
delattr() hash() memoryview() set()  

abs/round/sum

>>> abs(1)
1
>>> abs(-1)         # 求绝对值
1
>>> round(1.234,2)
1.23
>>> round(1.269,2)  # 四舍五入
1.27
>>> sum([1,2,3,4])
10
>>> sum((1,3,5,7))  # 接收数字组成的元组/列表
16

callable/chr/dir

>>> def func():pass  
>>> callable(func) # 判断一个变量是否能够调用 函数能够被调用 True >>> a = 123 # 数字类型a不能被调用 >>> callable(a) False >>> chr(97) # 将一个数字转换成一个字母 'a' >>> chr(65) 'A' >>> dir(123) # 查看数字类型中含有哪些方法 ['__abs__', '__add__', '__and__', '__bool__', '__ceil__', '__class__', '__delattr__', '__dir__', '__divmod__', '__doc__', '__eq__', '__float__', '__floor__', '__floordiv__', '__format__', '__ge__', '__getattribute__', '__getnewargs__', '__gt__', '__hash__', '__index__', '__init__', '__init_subclass__', '__int__', '__invert__', '__le__', '__lshift__', '__lt__', '__mod__', '__mul__', '__ne__', '__neg__', '__new__', '__or__', '__pos__', '__pow__', '__radd__', '__rand__', '__rdivmod__', '__reduce__', '__reduce_ex__', '__repr__', '__rfloordiv__', '__rlshift__', '__rmod__', '__rmul__', '__ror__', '__round__', '__rpow__', '__rrshift__', '__rshift__', '__rsub__', '__rtruediv__', '__rxor__', '__setattr__', '__sizeof__', '__str__', '__sub__', '__subclasshook__', '__truediv__', '__trunc__', '__xor__', 'bit_length', 'conjugate', 'denominator', 'from_bytes', 'imag', 'numerator', 'real', 'to_bytes'] >>> dir('abc') ['__add__', '__class__', '__contains__', '__delattr__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__getnewargs__', '__gt__', '__hash__', '__init__', '__init_subclass__', '__iter__', '__le__', '__len__', '__lt__', '__mod__', '__mul__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__rmod__', '__rmul__', '__setattr__', '__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 'isascii', 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']

eval/exec

>>> eval('1+2-3*4/5')  # 执行字符串数据类型的代码而且将值返回
0.6000000000000001
>>> exec('print(123)') # 执行字符串数据类型的代码但没有返回值
123

enumerate

>>> enumerate(['apple','banana'],1)   # 会给列表中的每个元素拼接一个序号
<enumerate object at 0x113753fc0>
>>> list(enumerate(['apple','banana'],1))
[(1, 'apple'), (2, 'banana')]

max/min

>>> max(1,2,3,)     # 求最小值
3
>>> min(2,1,3)      # 求最大值
1

sorted

将给定的可迭代对象进行排序,并生成一个有序的可迭代对象。

>>> sorted([1, 4, 5, 12, 45, 67])  # 排序,并生成一个新的有序列表
[1, 4, 5, 12, 45, 67]

还接受一个key参数和reverse参数。

>>> sorted([1, 4, 5, 12, 45, 67], reverse=True)
[67, 45, 12, 5, 4, 1]
list1 = [
    {'name': 'Zoe', 'age': 30},
    {'name': 'Bob', 'age': 18},
    {'name': 'Tom', 'age': 22},
    {'name': 'Jack', 'age': 40},
]

ret = sorted(list1, key=lambda x: x['age'])
print(ret)

# [{'name': 'Bob', 'age': 18}, {'name': 'Tom', 'age': 22}, {'name': 'Zoe', 'age': 30}, {'name': 'Jack', 'age': 40}]

 

zip

zip函数接收一个或多个可迭代对象做为参数,最后返回一个迭代器:

>>> x = ["a", "b", "c"]
>>> y = [1, 2, 3]
>>> a = list(zip(x, y))  # 合包
>>> a
[('a', 1), ('b', 2), ('c', 3)]
>>> b =list(zip(*a))  # 解包
>>> b
[('a', 'b', 'c'), (1, 2, 3)]

zip(x, y) 会生成一个可返回元组 (m, n) 的迭代器,其中m来自x,n来自y。 一旦其中某个序列迭代结束,迭代就宣告结束。 所以迭代长度跟参数中最短的那个序列长度一致。

>>> x = [1, 3, 5, 7, 9]
>>> y = [2, 4, 6, 8]
>>> for m, n in zip(x, y):
...   print(m, n)
... 
1 2
3 4
5 6
7 8

若是上面不是你想要的效果,那么你还可使用 itertools.zip_longest() 函数来代替这个例子中的zip。

>>> from itertools import zip_longest
>>> x = [1, 3, 5, 7, 9]
>>> y = [2, 4, 6, 8]
>>> for m, n in zip_longest(x, y):
...   print(m, n)
... 
1 2
3 4
5 6
7 8
9 None

zip其余常见应用:

>>> keys = ["name", "age", "salary"]
>>> values = ["Andy", 18, 50]
>>> d = dict(zip(keys, values))
>>> d
{'name': 'Andy', 'age': 18, 'salary': 50}

map

map()接收两个参数func(函数)和seq(序列,例如list)。以下图:

map()将函数func应用于序列seq中的全部元素。在Python3以前,map()返回一个列表,列表中的每一个元素都是将列表或元组“seq”中的相应元素传入函数func返回的结果。Python 3中map()返回一个迭代器。

由于map()须要一个函数做为参数,因此能够搭配lambda表达式很方便的实现各类需求。

例子1:将一个列表里面的每一个数字都加100:

 

>>> l = [11, 22, 33, 44, 55]
>>> list(map(lambda x:x+100, l))
[111, 122, 133, 144, 155]

例子2:

使用map就至关于使用了一个for循环,咱们彻底能够本身定义一个my_map函数:

def my_map(func, seq):
    result = []
    for i in seq:
        result.append(func(i))
    return result

测试一下咱们本身的my_map函数:

>>> def my_map(func, seq):
...     result = []
...     for i in seq:
...         result.append(func(i))
...     return result
... 
>>> l = [11, 22, 33, 44, 55]
>>> list(my_map(lambda x:x+100, l))
[111, 122, 133, 144, 155]

咱们自定义的my_map函数的效果和内置的map函数同样。

固然在Python3中,map函数返回的是一个迭代器,因此咱们也须要让咱们的my_map函数返回一个迭代器:

def my_map(func, seq):
    for i in seq:
        yield func(i)

测试一下:

>>> def my_map(func, seq):
...     for i in seq:
...         yield func(i)
... 
>>> l = [11, 22, 33, 44, 55]
>>> list(my_map(lambda x:x+100, l))
[111, 122, 133, 144, 155]

与咱们本身定义的my_map函数相比,因为map是内置的所以它始终可用,而且始终以相同的方式工做。它也具备一些性能优点,一般会比手动编写的for循环更快。固然内置的map还有一些高级用法:

例如,能够给map函数传入多个序列参数,它将并行的序列做为不一样参数传入函数:

拿pow(arg1, arg2)函数举例,

>>> pow(2, 10)
1024
>>> pow(3, 11)
177147
>>> pow(4, 12)
16777216
>>> list(map(pow, [2, 3, 4], [10, 11, 12]))
[1024, 177147, 16777216]

filter

filter函数和map函数同样也是接收两个参数func(函数)和seq(序列,如list),以下图:

 

filter函数相似实现了一个过滤功能,它过滤序列中的全部元素,返回那些传入func后返回True的元素。也就是说filter函数的第一个参数func必须返回一个布尔值,即True或者False。

下面这个例子,是使用filter从一个列表中过滤出大于33的数:

>>> l = [30, 11, 77, 8, 25, 65, 4]
>>> list(filter(lambda x: x>33, l))
[77, 65]

利用filter()还能够用来判断两个列表的交集:

>>> x = [1, 2, 3, 5, 6]
>>> y = [2, 3, 4, 6, 7]
>>> list(filter(lambda a: a in y, x))
[2, 3, 6]

补充:reduce

reduce
注意:Python3中reduce移到了functools模块中,你能够用过from functools import reduce来使用它。

reduce一样是接收两个参数:func(函数)和seq(序列,如list),以下图:

reduce最后返回的不是一个迭代器,它返回一个值。

reduce首先将序列中的前两个元素,传入func中,再将获得的结果和第三个元素一块儿传入func,…,这样一直计算到最后,获得一个值,把它做为reduce的结果返回。

原理相似于下图:

看一下运行结果:

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

再来练习一下,使用reduce求1~100的和:

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

 lambda

lambda是匿名函数,也就是没有名字的函数。lambda的语法很是简单:

 

直白一点说:为了解决那些功能很简单的需求而设计的一句话函数

注意:

使用lambda表达式并不能提升代码的运行效率,它只能让你的代码看起来简洁一些。

 

#这段代码
def func(x, y):
    return x + y#换成匿名函数
lambda x, y:x+y

lambda表达式和定义一个普通函数的对比:

 

咱们能够将匿名函数赋值给一个变量而后像调用正常函数同样调用它。

匿名函数的调用和正常的调用也没有什么分别。 就是 函数名(参数) 就能够了~~~

练一练:

请把如下函数变成匿名函数
def func(x, y):
    return x + y

 

上面是匿名函数的函数用法。除此以外,匿名函数也不是浪得虚名,它真的能够匿名。在和其余功能函数合做的时候

l=[3,2,100,999,213,1111,31121,333]
print(max(l))

dic={'k1':10,'k2':100,'k3':30}


print(max(dic))
print(dic[max(dic,key=lambda k:dic[k])])
res = map(lambda x:x**2,[1,5,7,4,8])
for i in res:
    print(i)

输出
1
25
49
16
64
res = filter(lambda x:x>10,[5,8,11,9,15])
for i in res:
    print(i)

输出
11
15

面试题练一练

1.现有两个元组(('a'),('b')),(('c'),('d')),请使用python中匿名函数生成列表[{'a':'c'},{'b':'d'}]

#答案一
test = lambda t1,t2 :[{i:j} for i,j in zip(t1,t2)]
print(test(t1,t2))
#答案二
print(list(map(lambda t:{t[0]:t[1]},zip(t1,t2))))
#还能够这样写
print([{i:j} for i,j in zip(t1,t2)])
练习1-coding
1.下面程序的输出结果是:
d = lambda p:p*2
t = lambda p:p*3
x = 2
x = d(x)
x = t(x)
x = d(x)
print x

练习2

三元运算

三元运算(三目运算)在Python中也叫条件表达式。三元运算的语法很是简单,主要是基于True/False的判断。以下图:

使用它就能够用简单的一行快速判断,而再也不须要使用复杂的多行if语句。 大多数时候状况下使用三元运算可以让你的代码更清晰。

三元运算配合lambda表达式和reduce,求列表里面值最大的元素:

>>> from functools import reduce
>>> l = [30, 11, 77, 8, 25, 65, 4]
>>> reduce(lambda x,y: x if x > y else y, l)
77

再来一个,三元运算配合lambda表达式和map的例子:

将一个列表里面的奇数加100:

>>> l = [30, 11, 77, 8, 25, 65, 4]
>>> list(map(lambda x: x+100 if x%2 else x, l))
[30, 111, 177, 8, 125, 165, 4]

递归

递归是一种解决问题的思路。

在函数内部,能够调用其余函数。若是一个函数在内部调用自身自己,这个函数就是递归函数。

def story():
    s = """
    从前有个山,山里有座庙,庙里有个老和尚在讲故事,
    讲的什么呢?
    """
    print(s)
    story()
    
story()

初识递归

递归的定义—— 在一个函数里再调用这个函数自己

如今咱们已经大概知道刚刚讲的story函数作了什么,就是 在一个函数里再调用这个函数自己 ,这种魔性的使用函数的方式就叫作 递归

刚刚咱们就已经写了一个最简单的递归函数。

递归的最大深度——1000

正如大家刚刚看到的,递归函数若是不受到外力的阻止会一直执行下去。

可是咱们以前已经说过关于函数调用的问题,每一次函数调用都会产生一个属于它本身的名称空间,若是一直调用下去,就会形成名称空间占用太多内存的问题。

Python为了杜绝此类现象,强制的将递归层数控制在了1000 (你写代码测试可能只测出997或998)。

咱们能够经过下面的代码来查看此限制:

import sys
print(sys.getrecursionlimit())

1000是Python为了咱们程序的内存优化所设定的一个默认值,咱们固然还能够经过一些手段去修改它:

import sys
print(sys.setrecursionlimit(100000))
修改递归最大深度

咱们能够经过这种方式来修改递归的最大深度,刚刚咱们将Python容许的递归深度设置为了10w,至于实际能够达到的深度就取决于计算机的性能了。

不过咱们仍是很是不推荐修改这个默认的递归深度,由于若是用1000层递归都没有解决的问题要么是不适合使用递归来解决要么就是你代码写的太烂了~~~

江湖上流传这这样一句话叫作:人理解循环,神理解递归。

注意Python解释器不支持尾递归优化。

再谈递归

这里咱们又要举个例子来讲明递归能作的事情。

 

首先咱们须要记住构成递归需具有的条件:

1. 子问题须与原始问题为一样的事,且更为简单(问题相同,但规模在变小);

2. 不能无限制地调用自己,须有个出口,化简为非递归情况处理。

总结一下:

递归是用来解决那些问题能够简化为不少相同的规模小不少的子问题的场景。

就是把大问题分红小问题,小问题本质上合大问题是同样的问题。

好比:list1 = [1, [2, [3, [4, [5, [6, [7, [8, [9]]]]]]]]],把里面的每个数字都打印出来。

def tell(x):
    for i in x:
        if not isinstance(i, list):
            print(i)
        else:
            tell(i)
            
            
tell(list1)

递归函数的优势是定义简单,逻辑清晰。理论上,全部的递归函数均可以写成循环的方式,但循环的逻辑不如递归清晰。

再好比斐波那契数列,这种典型的可使用递归解决的问题,均可以清晰的分为回溯和递推两个阶段。

递归函数与二分查找算法

http://www.javashuo.com/article/p-skwepppm-y.html

相关文章
相关标签/搜索