目录
- 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]
<type 'list'>
<generator object <genexpr> at 0x027F3A30>
0
1
<type 'generator'>
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
输出结果:
<type 'function'>
<generator object fib at 0x01FD3A30>
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)
<enumerate object at 0x020838C8>
>>> 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
注:生成器和迭代器区别
对任何对象,只要实现了__iter__和next方法就是一个迭代器,迭代器是一个可迭代对象。生成器是一个特殊的迭代器,可以使用生成式表达式和包含yield的函数生成。 迭代器是一个更抽象的概念,任何对象,如果它的类有next方法(next python3)和__iter__方法返回自己本身。 每个生成器都是一个迭代器,但是反过来不行。通常生成器是通过调用一个或多个yield表达式构成的函数s生成的。同时满足迭代器的定义。 当你需要一个类除了有生成器的特性之外还要有一些自定义的方法时,可以使用自定义的迭代器,一般来说生成器更方便,更简单。 当你需要一个类除了有生成器的特性之外还要有一些自定义的方法时,可以使用自定义的迭代器,一般来说生成器更方便,更简单。
def squares(start, stop):
for i in xrange(start, stop):
yield i*i
等同于生成器表达式:
(i*i for i in xrange(start, stop))
列表推倒式是:
[i*i for i in xrange(start, stop)]
如果是构建一个自定义的迭代器:
class Squares(object):
def __init__(self, start, stop):
self.start = start
self.stop = stop
def __iter__(self):
return self
def next(self):
if self.start >= self.stop:
raise StopIteration
current = self.start * self.start
self.start += 1
return current
此时,你还可以定义自己的方法如:
def current(self):
return self.start
两者的相同点:对象迭代完后就不能重写迭代了。