目录
- 1、生成器
- 2、列表解析
- 3、函数式编程
- 4、描述器
- 5、迭代器
http://coolshell.cn/articles/10822.html
1、生成器
参考学习
为什么要使用生成器?很多时候,通过列表生成的可迭代对象占用内存会很大,而且若只是要访问前面几个元素,那后面的内存空间就会被浪费。若迭代器的构建是通过一个函数,也就是说有规律的,则可以使用生成器,在循环的过程中不断推算出后续的元素,这样就不必创建完整的list,从而节省大量的空间。
生成器的构造方式一
L = [x * x for x in range(10)]
print L
print type(L)
L = (x * x for x in range(10))
print L
print L.next()
print next(L)
print type(L)
for item in L:
print item
输出为:
[0, 1, 4, 9, 16, 25, 36, 49, 64, 81]
at 0x027F3A30>
0
1
4
9
16
25
36
49
64
81
上面可以看出,最大的不同就是[]和(),回忆(1,2,3)类型其实是一个元祖,元祖的特点就是元素不可变。但为什么这里却变成了一个生成器呢,深层次原理不考究,简单来说这里包含了元素计算的算法,生成器保存的实际上是该算法。注意,生成器也是可迭代对象,能进行for操作。
通过 yield关键字
def fib(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n = n + 1
print type(fib)
print fib(5)
gen = fib(5)
print gen.next()
for item in gen:
print item
输出结果:
1
1
2
3
5
如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator.
generator和函数的执行流程不一样。函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成generator的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。这也隐藏了一个问题,获取第一个元素的值也是要通过next调用。
生成器还有一些高级用法,如send()函数可以向生成器传入一个值。更多可以参考generator send
2、列表解析
列表解析式是将一个列表(实际上适用于任何可迭代对象(iterable))转换成另一个列表的工具。在转换过程中,可以指定元素必须符合一定的条件,才能添加至新的列表中,这样每个元素都可以按需要进行转换
new_things = []
for ITEM in old_things:
if condition_based_on(ITEM):
new_things.append("something with " + ITEM)
你可以将上面的for循环改写成这样的列表解析式:
new_things = ["something with " + ITEM for ITEM in old_things if condition_based_on(ITEM)]
再例如:
numbers = [1, 2, 3, 4, 5]
doubled_odds = []
for n in numbers:
if n % 2 == 1:
doubled_odds.append(n * 2)
转换成了这两行代码:
numbers = [1, 2, 3, 4, 5]
doubled_odds = [n * 2 for n in numbers if n % 2 == 1]
上面的其实也比较简单,需要自己有这个意识主动使用列表解析。而什么时候需要用到列表解析呢?在创建一个列表或其他的可迭代对象时,对其中的元素需要判断后再添加进去,或者判断后进行适当操作再添加,就需要用到列表解析。
再看几个其他的列表解析:
循环嵌套
flattened = []
for row in matrix:
for n in row:
flattened.append(n)
可以写为:
matrix = [(1,2,3),(4,5,6)]
# for row in matrix:
# for n in row:
# flattened.append(n)
flattened = [n for row in matrix for n in row]
print flattened
这样写是错误的,flattened = [n for n in row for row in matrix]
,for循环的顺序和之前的顺序是一样的。
注意可读性
长长的一行读起来很费力,可适当分行如下:
flattened = [
n
for row in matrix
for n in row
]
3、函数式编程基础 map filter
map
map(function,iterable),对iterable中每个元素应用function。
version_info = (4, 8)
version = '.'.join(map(str, version_info))
>>> map(lambda x: x + "bzz!",["I think","I'm good"])
['I thinkbzz!', "I'm goodbzz!"]
第一个,返回值是4.8,很简单,map将version_info中的整形转换为字符,然后各个字符之间用‘.’连接。
第二个要注意,Python2中返回的是一个列表,而在Python3中是返回可迭代的对象。
map在一定程度上也可以用列表解析替换如:
[x + 'bzz' for x in ["I think","I'm good"]]
filter
对所给的可迭代对象进行过滤
>>> def f(x): return x % 2 != 0 and x % 3 != 0
>>> filter(f, range(2, 25))
[5, 7, 11, 13, 17, 19, 23]
enumerate(iterable[,start])
该函数返回可迭代的enumerate对象,生成一个元组序列,每个元组包含一个整形索引(默认从0开始,也可以指定start)和迭代对象中对应的元素。
mylist = ['hello','how','are','you']
for i, item in enumerate(mylist):
print "Item %d: %s" % (i, item)
输出为:
Item 0: hello
Item 1: how
Item 2: are
Item 3: you
>>> mylist = [1,2,4]
>>> enumerate(mylist)
>>> list(enumerate(mylist))
[(0, 1), (1, 2), (2, 4)]
>>>
any(iterable) 和 all(iterable)
主要用来判断给定迭代对象是否满足相应条件,all表示要都满足,而any表示至少有一个满足时返回真。这两个函数只有一个输入,而且是对每个元素判断ture or faulse,因此常常和map函数配合使用。
等价于
def all(iterable):
for item in iterable:
if not item:
return False
return True
def any(iterable):
for item in iterable:
if item:
return True
return False
mylist = [0,1,2,3,-2]
print map(lambda x: x > 0, mylist)
if all(map(lambda x: x > 0, mylist)):
print 'all item grater than 0'
else:
print 'some less than 0'
输出为:
[False, True, True, True, False]
some less than 0
zip(iter1 [,iter2 [...]])
主要用于将一组键和一组值组合成字典。
print zip(['hello','world'],['hi','big'],['nice','night','right'])
[('hello', 'hi', 'nice'), ('world', 'big', 'night')]
该函数接收多个序列并将它们组合成元组,值得注意的是木桶效应,生成的列表(Python2中返回列表,3中为迭代对象)取决于最短的输入序列长度。
4、描述符
参考学习
一个描述符就是一个对象,该对象代表了一个属性的值。这就意味着如果一个账户对象有一个属性“name”,那么描述符就是另一个能够用来代表属性“name”持有值的对象。描述符协议中“定义了__get__”、“__set__”或”__delete__” 这些特殊方法,描述符是实现其中一个或多个方法的对象。
class Descri(object):
def __init__(self,name):
self.name = name
def __get__(self, instance, owner):
print '__get__'
return self.name
def __set__(self, instance, value):
print '__set__'
self.name = value
D = Descri('hello')
D.name = '123'
print D.name
D.name = 'yuan'
print D.name
class Foo(object):
name = Descri('dodo')
F = Foo()
print F.name
F.name = 'Jiessie'
输出为:
123
yuan
__get__
dodo
__set__
上面的程序很有意思,为什么直接调用Descri类没有进入__get__方法,而下面通过另一个类调用却会进入。首先要明确什么是描述符,顾名思义,在python中是用来描述一个属性的,一般来说,像上面Foo类name属性,回忆总结一中,当实例化后,在类的外面是可以随便修改属性值的,而描述符会对它所代表的类的属性执行类型检查等自定义操作,这也是描述符存在的价值。
当访问类Foo实例的任何属性时,描述符会调用它的__get__方法。需要注意的是,__get__方法的第一个参数是描述符代表的属性被引用的源对象。当属性被分配时,描述符会调用它的__set__方法。当没有定义__set__方法时就是只读变量。
描述符可以用来对变量值进行检查,但相对繁琐,提供了另一种相对简洁方式,property(fget=None, fset=None, fdel=None, doc=None)
,然后实现相关函数即可。
很容易想到property修饰符也表示了同样的工作,并且更简洁,因此,主要用@property这种方式,但描述符需要理解。
5、迭代器
可以直接作用于for循环的对象统称为可迭代对象Iterable。有以下几种:
一类是集合数据类型,如list、tuple、dict、set、str等;
一类是generator,包括生成器和带yield的generator function。
由之前知道,生成器不但可以作用于for循环,还可以被next()函数不断调用并返回下一个值,直到最后抛出StopIteration错误表示无法继续返回下一个值了。
于是,迭代器就是可以被next()函数调用并不断返回下一个值的对象。要分清楚可迭代对象和迭代器。
from collections import Iterable,Iterator
print isinstance([],Iterable)
print isinstance((),Iterable)
print isinstance({},Iterable)
print isinstance([],Iterator)
print isinstance((),Iterator)
print isinstance({},Iterator)
print isinstance((x*x for x in range(2,5)),Iterator)
输出为:
True
True
True
False
False
False
True