美文网首页《Fluent Python》读书笔记
《Fluent Python》读书笔记-An Array of

《Fluent Python》读书笔记-An Array of

作者: 雨天独行 | 来源:发表于2019-01-17 18:22 被阅读0次

    概览

        python标准库提供了一系列的序列类型可供选择。可以从两种视角进行分类。
        一种分法如下:
    Container sequences
        list, tuple, and collections.deque can hold items of different types.
    Flat sequences
        str, bytes, bytearray, memoryview, and array.array hold items of one type.
        
        Container sequences持有的是包含对象的引用,可以是各种类型的,而Flat sequences是物理上将每一个对象的值存储在自身的内存空间里。因此Flat sequences更压缩,但是限制只能存储基本类型,如characters, bytes, numbers。
        另一种分法是按照序列的可修改性来划分:
    Mutable sequences
        list, bytearray, array.array, collections.deque, and memoryview
    Immutable sequences
        tuple;, ;str;, and ;bytes;

    List Comprehensions and Generator Expressions

        Comprehensions(推倒式,又叫解析式)是python的一种特殊语法,可以从一个数据序列构建另一个新的数据序列结构体。
        Generator Expressions(生成器表达式)开始看和推倒式没什么区别,从语法上看只是'[]'和'()'的差别,但是实际上,生成器表达式返回的并不是一个新的序列,而是一个iterator。使用生成器表达式最大的好处就是在构建tuple,array等序列时不需要先去完整的构建一个list,而是一次生成一个元素,节省内存空间。

    >>> list_comp = [x ** 2 for x in range(10) if x % 2 == 0]
    >>> list_comp
    [0, 4, 16, 36, 64]
    >>> gen_exp = (x ** 2 for x in range(10) if x % 2 == 0)
    >>> gen_exp
    <generator object <genexpr> at 0x10773f550>
    >>> t = tuple(gen_exp)
    >>> t
    (0, 4, 16, 36, 64)
    

        python2和python3在使用推倒式时,对于外部变量的处理方式不一样。
        python2:

    >>> x = 'my precious'
    >>> dummy = [x for x in 'ABC']
    >>> x
    'C'
    

        python3:

    >>> x = 'my precious'
    >>> dummy = [x for x in 'ABC']
    >>> x
    'my precious'
    

    Tuples Are Not Just Immutable Lists

        在作者看来,tuples应该包含两个作用:
    1.immutable lists
    2.records with no field names
        第二种用法产生的主要原因在于tuple unpacking mechanism(元组拆包机制)。

    >>> latitude, longitude = (33.9425, -118.408056)
    >>> latitude
    33.9425
    >>> longitude
    -118.408056
    >>> name, cc, pop, (latitude, longitude) = ('Tokyo','JP',36.933,(35.689722,139.691667))
    >>> name, latitude, longitude
    ('Tokyo', 35.689722, 139.691667)
    

        使用元组拆包比较优雅的交换值,不需要临时变量。

    >>> a = 1
    >>> b = 2
    >>> a, b = b, a
    >>> a
    2
    >>> b
    1
    

        可以通过元组拆包给函数传参,也可以返回多个值。

    >>> t=(20,8)
    >>> divmod(*t)
    (2, 4)
    >>> quotient, remainder = divmod(*t)
    >>> quotient, remainder
    (2, 4)
    

        占位符_和*

    >>> _, a, _ = (1, 2, 3)
    >>> a
    2
    >>> a, b, *rest = range(5)
    >>> a, b, rest
    (0, 1, [2, 3, 4])
    >>> a, *b, rest = range(5)
    >>> a, b, rest
    (0, [1, 2, 3], 4)
    

        collections.namedtuple是tuple的增强版本,有字段名和类名。

    >>> from collections import namedtuple
    >>> City = namedtuple('City', 'name country population coordinates')
    >>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))
    >>> tokyo
    City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
    >>> tokyo.population
    36.933
    >>> City._fields
    ('name', 'country', 'population', 'coordinates')
    >>> tokyo._asdict()
    OrderedDict([('name', 'Tokyo'), ('country', 'JP'), ('population', 36.933), ('coordinates', (35.689722, 139.691667))])
    

    Slicing

        作者在这一节一开篇讲了为什么SliceRange都不包括最后一个元素,讲了三个原因:
    1.当只给stop的位置时,可以直接看出序列的长度,如range(3)和my_list[:3]
    2.当同时给出start和stop位置时,序列的长度就是stop - start
    3.在任意位置x切分序列时,两段不会重叠

    >>> l=[10,20,30,40,50,60]
    >>> l[:2]
    [10, 20]
    >>> l[2:]
    [30, 40, 50, 60]
    

        对于mutable sequences可以支持切片修改。

    >>> l = list(range(10))
    >>> l[2:5] = [20, 30]
    >>> l
    [0, 1, 20, 30, 5, 6, 7, 8, 9]
    >>> del l[5:7]
    >>> l
    [0, 1, 20, 30, 5, 8, 9]
    

    Using + and * with Sequences

    >>>l=[1,2,3]
    >>>l*5
    [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
    

    一些特殊的注意案例

    >>> board = [['_'] * 3 for i in range(3)]
    >>> board
    [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
    >>> board[1][2] = 'X'
    >>> board
    [['_', '_', '_'], ['_', '_', 'X'], ['_', '_', '_']]
    
    >>> weird_board = [['_'] * 3] * 3
    >>> weird_board
    [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
    >>> weird_board[1][2] = 'O'
    >>> weird_board
    [['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]
    

        对于第二段代码,相当于把同一个对象复制三遍,所以修改其中一个,也会影响其他的。

    >>> l=[1,2,3]
    >>> id(l)
    4553226824
    >>> l*=2
    >>> l
    [1, 2, 3, 1, 2, 3]
    >>> id(l)
    4553226824
    >>> t=(1,2,3)
    >>> id(t)
    4553229944
    >>> t*=2
    >>> id(t)
    4552912424
    

        区别在于list是mutable的,而tuple是immutable,本质在于是否实现了__iadd____imul__这两个特殊方法。对于没有实现的,a += b实质等于a = a + b。

    >>> t=(1,2,[30,40])
    >>> t[2] += [50, 60]
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    TypeError: 'tuple' object does not support item assignment
    >>> t
    (1, 2, [30, 40, 50, 60])
    

        从这个例子可以看到tuple中的可变序列的增加是分两步的,第一步先增加,第二步会赋值回原来的元组。

    list.sort and the sorted Built-In Function

        list.sort返回None,不会创建新的队列,在原来的队列里变化。
        sorted创建新的队列,原始队列不变。

    >>> fruits = ['grape', 'raspberry', 'apple', 'banana']
    >>> sorted(fruits, key=len, reverse=True)
    ['raspberry', 'banana', 'grape', 'apple']
    >>> fruits
    ['grape', 'raspberry', 'apple', 'banana']
    >>> fruits.sort()
    >>> fruits
    ['apple', 'banana', 'grape', 'raspberry']
    

    bisect和 bisect.insort

        对于已经排序的队列,可以使用bisect来查找新元素的插入位置,也可以使用bisect.insort进行插入,默认插入在右边,也可以使用bisect_left和bisect.insort_left在左侧插入。给了一个很巧妙的例子。

    >>> import bisect
    >>> def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
    ...     i = bisect.bisect(breakpoints, score)
    ...     return grades[i]
    ... 
    >>> [grade(score) for score in [33, 99, 77, 70, 89, 90, 100]]
    ['F', 'A', 'C', 'C', 'B', 'A', 'A']
    

    其他类型的sequence

    • array:支持所有list的操作,存储空间利用率高,支持快速的从文件中读写,只支持几种基础类型;
    • memorview:可用于在数据结构之间共享数据,而不需要进行数据拷贝;
    • deque:从队列的左端移除数据会导致整个队列的移动,比较耗费资源,deque可以支持快速的从队列双端插入和删除数据,并且是线程安全的。deque还支持设置保存元素的最大数量。

    相关文章

      网友评论

        本文标题:《Fluent Python》读书笔记-An Array of

        本文链接:https://www.haomeiwen.com/subject/jlrvdqtx.html