python重要知识点总结二

目录
  • 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

你可能感兴趣的:(python重要知识点总结二)