zoukankan      html  css  js  c++  java
  • 流畅的python,Fluent Python 第二章笔记

    2.1内置序列类型的概览

    Python标准库用C实现了丰富的序列类型

    容器序列

    list,tuple和collections.deque这些序列都能存放不同的数据类型。

    扁平序列

    str,bytes,bytearray,memoryview,array.array,这种序列只能容纳一种类型。

    容器徐蕾存放的是他们所包含的任意类型的对象的引用,而扁平序列里存放的是值而不是引用。

    不可变序列 (Sequence)

    tuple、str、bytes

    剩下的都是可变的 (MutableSequence)

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

    列表推导,我已经讲了多次,使用相对熟练,跳过。

    生成器表达式与列表表达式在使用中,一个最大的好处,假如被for循环遍历,如果数据很大,用生成器表达式可以省下生成列表的开销。

    2.3元祖不仅仅是不可变的列表

    主要介绍了元祖的拆包赋值,已经*的使用。

    In [88]: a,b,*rest = range(5)                                                                                           
    
    In [89]: a,b,rest                                                                                                       
    Out[89]: (0, 1, [2, 3, 4])
    
    In [90]: a,*body,c,d = range(10)                                                                                        
    
    In [91]: a,body,c,d                                                                                                     
    Out[91]: (0, [1, 2, 3, 4, 5, 6, 7], 8, 9)
    

     用起来还是很有意思的。

    nametuple前面我已经介绍过了,不写笔记了,多本书上介绍,看来还是蛮重要的。

    说明一点,nametuple构建的类的实例所消耗的内存跟元祖一样,因为不会用__dict__来存放这些实例的属性。

    2.4切片

    主要讲述了对对象进行切片,slice与切片赋值,切片赋值的对象一定要是可迭代对象

    invoice = '''
    0.....6...........................40...........52...55........
    1909  Pimoroni PiBrella          $17.50      3    $9.90
    1910  ok123   456  789           $ 454       2    $ 234
    1910  ok123   456  789           $ 454       2    $ 234
    1910  ok123   456  789           $ 454       2    $ 234
    '''
    ski = slice(0, 6)
    
    line_items = invoice.split('
    ')[2:]
    for item in line_items:
        print(item[ski])
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第二章/t2-4.py
    1909  
    1910  
    1910  
    1910  
    
    
    Process finished with exit code 0
    

     一个简单的slice的使用,平时一般我很少用。

    切片可以增删改查,该的话,务必给一个可迭代对象。

    Out[132]: [1, 2, 1, 2, 3, 4, 4, 5]
    
    In [133]: l = [1,2,3,4,5]                                                                                               
    
    In [134]: l = [0,1,2,3,4,5]                                                                                             
    
    In [135]: l[2:4] = 'hello'                                                                                              
    
    In [136]: l                                                                                                             
    Out[136]: [0, 1, 'h', 'e', 'l', 'l', 'o', 4, 5]
    
    In [137]: del l[5:]                                                                                                     
    
    In [138]: l                                                                                                             
    Out[138]: [0, 1, 'h', 'e', 'l']
    
    In [139]: l[2:4]=''                                                                                                     
    
    In [140]:                                                                                                               
    
    In [140]: l                                                                                                             
    Out[140]: [0, 1, 'l']
    

     上面就通过了简单的字符串切片赋值操作,很方便,由于字符串属于可迭代对象,可以在需要删除的部分直接赋值''空字符串既可。

    2.5 对序号使用+和*

    +和*都遵循这个规则,不修改原来的操作对象,而是构建一个全新的序列。

    In [147]: l + (1,2,3)                                                                                                   
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-147-720df1c2ab95> in <module>
    ----> 1 l + (1,2,3)
    
    TypeError: can only concatenate list (not "tuple") to list
    
    In [148]: l + [1,2,3]                                                                                                   
    Out[148]: [0, 1, 'l', 1, 2, 3]
    
    In [149]: l * 3                                                                                                         
    Out[149]: [0, 1, 'l', 0, 1, 'l', 0, 1, 'l']
    
    In [150]: l                                                                                                             
    Out[150]: [0, 1, 'l']
    

     只能跟同类型的相加。

    In [151]: l = [[1,2,3]]                                                                                                 
    
    In [152]: l1 = l * 3                                                                                                    
    
    In [153]: l1                                                                                                            
    Out[153]: [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
    
    In [154]: l1[0][0]= 'a'                                                                                                 
    
    In [155]: l1                                                                                                            
    Out[155]: [['a', 2, 3], ['a', 2, 3], ['a', 2, 3]]
    
    In [156]:      
    

     乘号要注意,里面的元素都将指向同一个变量。

    可以用列表生成式。

    In [172]: l = [[1,2,3] for i in range(3)]                                                                               
    
    In [173]: l                                                                                                             
    Out[173]: [[1, 2, 3], [1, 2, 3], [1, 2, 3]]
    
    In [174]: l[0][0] = 'a'                                                                                                 
    
    In [175]: l                                                                                                             
    Out[175]: [['a', 2, 3], [1, 2, 3], [1, 2, 3]]
    

     2.6一个+=的谜题

    In [177]: t = (1,2,[30,40])                                                                                             
    
    In [178]: t[2] += [50,60]                                                                                               
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-178-b483a1b4fe1d> in <module>
    ----> 1 t[2] += [50,60]
    
    TypeError: 'tuple' object does not support item assignment
    
    In [179]: t                                                                                                             
    Out[179]: (1, 2, [30, 40, 50, 60])
    

     对一个元祖内元素,是列表进行了+=操作。有意思的是,由于元祖内的元素不能修改,所以报错了

    但由于该元素是可变的,所以修改尽然又成功了,有意思。

    作者得到是三个教训:

    1、不要把可变对象放在元祖里面。

    2、增量赋值不是一个原子操作。它虽然跑出了异常,但还是完成了操作。

    3、我就不说了,他说查看那字节码很简单。。

    2.8用bisect来管理已排序的序列。

    这个有一个国内的链接使用记录:https://www.cnblogs.com/beiluowuzheng/p/8452671.html

    bisect对于大型的已经排好序的元祖或者列表查找索引非常快,底层都用了2分查找的方法。

    import bisect
    import sys
    import random
    
    HAYSTACK = [1, 3, 8, 13, 14, 15, 19, 21, 22, 24, 24, 25, 27, 28, 29]
    NEEDLES = [0, 1, 6, 9, 15, 21, 23, 25, 27, 28, 29, 30]
    
    ROW_FMT = '{0:2d} @ {1:2d}    {2}{0:<2d}'  # 利用了.foramt的格式化对齐<2左对齐
    
    def demo(bisect_fn):
        for needle in reversed(NEEDLES):
            position = bisect_fn(HAYSTACK, needle)
            offset = position * '  |'         # 通过索引划线条
            print(ROW_FMT.format(needle, position, offset))
    
    
    if __name__ == '__main__':
    
        if sys.argv[-1] == 'left':
            bisect_fn = bisect.bisect_left     # 索引在相同数的前面,就是左边
        else:
            bisect_fn = bisect.bisect       # 索引在相同数的右边,就是后面
        print('Demo:', bisect_fn.__name__)
        print('haystack ->', ' '.join('%2d' % n for n in HAYSTACK))
        demo(bisect_fn)
    
    shijianzhongdeMacBook-Pro:第二章 shijianzhong$ python3 t2-8.py
    Demo: bisect_right
    haystack ->  1  3  8 13 14 15 19 21 22 24 24 25 27 28 29
    30 @ 15      |  |  |  |  |  |  |  |  |  |  |  |  |  |  |30
    29 @ 15      |  |  |  |  |  |  |  |  |  |  |  |  |  |  |29
    28 @ 14      |  |  |  |  |  |  |  |  |  |  |  |  |  |28
    27 @ 13      |  |  |  |  |  |  |  |  |  |  |  |  |27
    25 @ 12      |  |  |  |  |  |  |  |  |  |  |  |25
    23 @  9      |  |  |  |  |  |  |  |  |23
    21 @  8      |  |  |  |  |  |  |  |21
    15 @  6      |  |  |  |  |  |15
     9 @  3      |  |  |9 
     6 @  2      |  |6 
     1 @  1      |1 
     0 @  0    0 
    shijianzhongdeMacBook-Pro:第二章 shijianzhong$ python3 t2-8.py left
    Demo: bisect_left
    haystack ->  1  3  8 13 14 15 19 21 22 24 24 25 27 28 29
    30 @ 15      |  |  |  |  |  |  |  |  |  |  |  |  |  |  |30
    29 @ 14      |  |  |  |  |  |  |  |  |  |  |  |  |  |29
    28 @ 13      |  |  |  |  |  |  |  |  |  |  |  |  |28
    27 @ 12      |  |  |  |  |  |  |  |  |  |  |  |27
    25 @ 11      |  |  |  |  |  |  |  |  |  |  |25
    23 @  9      |  |  |  |  |  |  |  |  |23
    21 @  7      |  |  |  |  |  |  |21
    15 @  5      |  |  |  |  |15
     9 @  3      |  |  |9 
     6 @  2      |  |6 
     1 @  0    1 
     0 @  0    0 
    

     作者写的很漂亮的测试代码,逻辑清楚,格式化输出完整,大神就是大神。膜拜。

    从1,15,21可以看出他们的索引其实是不一样的。

    import bisect
    
    def grade(score, breakpoints=[60, 70, 80, 90], grades='FDCBA'):
        i = bisect.bisect(breakpoints, score)
        return grades[i]
    
    print([grade(score) for score in [50, 73, 80, 63, 97, 93, 98, 67, 68, 41]])
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第二章/t2-8-1.py
    ['F', 'C', 'B', 'D', 'A', 'A', 'A', 'D', 'D', 'F']
    
    Process finished with exit code 0
    

     这是一个通过索引,获取分数等级的代码,我就奇怪,这个bisect用在大量数据的时候比较好,为什么书上的实例都是小数据。

    import bisect
    import random
    
    
    SIZE = 7
    
    random.seed(1729)
    
    my_list = []
    for i in range(SIZE):
        new_item = random.randrange(SIZE*2)
        bisect.insort(my_list, new_item)     # 每次插入数据
        print('%2d ->' %  new_item, my_list)
    
    /usr/local/bin/python3.7 /Users/shijianzhong/study/Fluent_Python/第二章/t2-8-2.py
    10 -> [10]
     0 -> [0, 10]
     6 -> [0, 6, 10]
     8 -> [0, 6, 8, 10]
     7 -> [0, 6, 7, 8, 10]
     2 -> [0, 2, 6, 7, 8, 10]
    10 -> [0, 2, 6, 7, 8, 10, 10]
    
    Process finished with exit code 0
    

     同样他也有bisect.insort_left

    刚刚今天我用timeit做了一个测试,惊人。bisect的速度在大数据下跟index的速度简直是天壤之别。

     l = [uuid.uuid4().int  for i in range(10**7)]                                                                 
    
    In [426]: l                                                                                                             
    Out[426]: 
    [130961955267366331713228587612281413150,
     101168619383506800871032067138007901687,
     230268143853013387044664472262098300983,
     271666116513559436401883400443324603744,
     277109119499575815471579530262910533167,
     26384477908852324007838280384798436465,
     68709297594091959530488974322976028713,
     261120941994541284427462176415989676859,
     126772591179259287818187381605544117637,
     304890299811831813661392454800720055539,
     179882082628802583283994601746567577335,
     255412216037852743580789243320988572922,
     70403601837210562193281460378323700626,
     62530470385539227292309277933722797828,
     69582615430669922150817782274899291254,
     100414497399552519366731353674646246067,
     93739563539916107574427723914796801231,
     ...]
    
    In [427]: l.sort()                                                                                                      
    
    In [428]: l[-1]                                                                                                         
    Out[428]: 340282358066129026880946537805653405206
    
    In [429]: def run1(l): 
         ...:     return l.index(340282358066129026880946537805653405206) 
         ...:                                                                                                               
    
    In [430]: def run2(l): 
         ...:     return bisect.bisect(l, 340282358066129026880946537805653405206) 
         ...:                                                                                                               
    
    In [431]: timeit.timeit(repr(run1(l)), setup='from __main__ import run1, l', number=1)                                  
    Out[431]: 4.849862307310104e-07
    
    In [432]: timeit.timeit(repr(run1(l)), setup='from __main__ import run1, l', number=100)                                
    Out[432]: 1.2320233508944511e-06
    
    In [433]: timeit.timeit(repr(run1(l)), setup='from __main__ import run1, l', number=1000)                               
    Out[433]: 7.741968147456646e-06
    
    In [434]: timeit.timeit(repr(run1(l)), setup='from __main__ import run1, l', number=10000)                              
    Out[434]: 7.341301534324884e-05
    
    In [435]: timeit.timeit(repr(run2(l)), setup='from __main__ import run2, l', number=10000)                              
    Out[435]: 7.15720234438777e-05
    
    In [436]:                                                                                                               
    

     无聊做了一个整数的,感觉差不多速度,但浮点数就完全不一样了,下面上代码。

    l = [random() for i in range(10**7)]
    
    In [449]: l.sort()                                                                                                      
    
    In [450]: l[-1]                                                                                                         
    Out[450]: 0.9999999538530189
    
    
    In [462]: def f_run1(l): 
         ...:     return l.index(0.9999999538530189) 
         ...:           
    
    In [464]: def f_run2(l): 
         ...:     return bisect.bisect(l, 0.9999999538530189)
    
    In [458]: timeit.timeit(repr(f_run2(l)), setup='from __main__ import f_run2, l', number=1)                              
    Out[458]: 3.3492688089609146e-07
    
    In [459]: timeit.timeit(repr(f_run1(l)), setup='from __main__ import f_run1, l', number=1)                              
    Out[459]: 5.069887265563011e-07
    
    In [460]: f_run1                                                                                                        
    Out[460]: <function __main__.f_run1(l)>
    

     速度差了一半左右,前面有一次运行要1秒多,这次少了很多,具体有空在测试了

    2.9当列表不是首选时

    主要讲述了数组array以及memoryviewe的使用,最后还有NUMpy和Scipyd的介绍。

    In [304]: from array import array                                                                                       
    
    In [305]: from random import random                                                                                     
    
    In [306]: floats = array('d', (random() for i in range(10**7)))                                                         
    
    In [307]: floats[-1]                                                                                                    
    Out[307]: 0.81302883691505
    
    In [308]: fp = open('floats.bin', 'wb')                                                                                 
    
    In [309]: floats.tofile(fp)                                                                                             
    
    In [310]: fp.close()                                                                                                    
    
    In [311]: floats2 = array('d')                                                                                          
    
    In [312]: fp = open('floats.bin', 'rb')                                                                                 
    
    In [313]: floats2.fromfile(fp, 10**7)                                                                                   
    
    In [314]: fp.close()                                                                                                    
    
    In [315]: floats2[-1]                                                                                                   
    Out[315]: 0.81302883691505
    
    In [316]: floats == floats2                                                                                             
    Out[316]: True
    
    In [317]:  
    

     这个是通过数组操作1000组数据,无论从速度还是数据量都比操作文本文件要快多了。

    pickle.dump处理浮点数组跟array.tofile速度一样非常快。

    2.9.2内存视图

    具体介绍了memoryview内置类的使用。

    Python memoryview() 函数返回给定参数的内存查看对象(Momory view)。

    所谓内存查看对象,是指对支持缓冲区协议的数据进行包装,在不需要复制对象基础上允许Python代码访问。

    In [317]: numbers = array('h', [-2, -1, 0, 1, 2])                                                                       
    
    In [318]: menv = memoryview(numbers)                                                                                    
    
    In [319]: menv.tolist()                                                                                                 
    Out[319]: [-2, -1, 0, 1, 2]
    
    In [320]: menv[-1]                                                                                                      
    Out[320]: 2
    
    In [321]: menv_oct = menv.cast('B')                                                                                     
    
    In [322]: menv_oct.tolist()                                                                                             
    Out[322]: [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]
    
    In [323]: numbers                                                                                                       
    Out[323]: array('h', [-2, -1, 0, 1, 2])
    
    In [324]: menv_oct[5] = 4                                                                                               
    
    In [325]: numbers                                                                                                       
    Out[325]: array('h', [-2, -1, 1024, 1, 2])
    

     menoryview.cast会把同一块内存里的内容打包成一个全新的memoryview对象给你,进行内存操作后,数据直接改了。

    2.9.3NumPy与SciPy

    这个数据分析的两个库,SciPy是基于NumPy的另一个类,他提供了很多跟科学计算相关的算法,专为限行代数,数值积分和统计学设计。

    2.9.4双向队列和其他形式的队列。

    collections.deque

    from collections import deque                                                                                 
    
    In [327]: dq = deque(range(10), maxlen=10)                                                                              
    
    In [328]: dq.rotate(3)                                                                                                  
    
    In [329]: dq                                                                                                            
    Out[329]: deque([7, 8, 9, 0, 1, 2, 3, 4, 5, 6])
    
    In [330]: dq[2:5] = []                                                                                                  
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-330-18b8655c1ded> in <module>
    ----> 1 dq[2:5] = []
    
    TypeError: sequence index must be integer, not 'slice'
    
    In [331]: dq.appendleft(-1)                                                                                             
    
    In [332]: dq                                                                                                            
    Out[332]: deque([-1, 7, 8, 9, 0, 1, 2, 3, 4, 5])
    
    In [333]: dq[0] = 0                                                                                                     
    
    In [334]: dq                                                                                                            
    Out[334]: deque([0, 7, 8, 9, 0, 1, 2, 3, 4, 5])
    
    In [335]: dq[0]=6                                                                                                       
    
    In [336]: dq.rotate(-4)                                                                                                 
    
    In [337]: dq                                                                                                            
    Out[337]: deque([0, 1, 2, 3, 4, 5, 6, 7, 8, 9])
    
    In [338]: dq.extendleft('555')                                                                                          
    
    In [339]: dq                                                                                                            
    Out[339]: deque(['5', '5', '5', 0, 1, 2, 3, 4, 5, 6])
    

     测试了不能切片赋值,另外的炒作比列表要方便,同时也是容器序列,里面啥都可以装。

    除了collections.deque还有其他对列的实现

    queuq.Queue  LifoQueue 和 PriorityQueue,不同线程可以通过操作队列实现线程安全。

    multiprocessing.Queue用于线程间的通讯,同时还有multiprocessing.JoinableQueue类型,让任务管理更加方便。

    asynio有Queue,LifoQueue,PrioityQueue和JoinableQueue这个为异步编程里的任务管理提供了专门的遍历。

    heapq跟上面的三个模块不同,没有队列类,而是提供了heappush和heappop的方法让用户可以把可变序列当做堆队列或者优先队列来使用。

    In [357]: l                                                                                                             
    Out[357]: [2, 4, 3, 6, 5.0, 5, 5.0]
    
    In [358]: heapq.heappop(l)                                                                                              
    Out[358]: 2
    
    In [359]: l                                                                                                             
    Out[359]: [3, 4, 5, 6, 5.0, 5.0]
    
    In [360]: heapq.heappush(l,3)                                                                                           
    
    In [361]: l                                                                                                             
    Out[361]: [3, 4, 3, 6, 5.0, 5.0, 5]
    
    In [59]: import random                                                          

    In [60]: l = [random.random() for i in range(20)]                               

    In [61]: import heapq                                                           

    In [62]: heapq.nlargest(3,l)      # 取出最大的3个                                               
    Out[62]: [0.9413791060745906, 0.9313454160253535, 0.9059834980630659]

    In [63]: heapq.nsmallest(4,l)                                                   
    Out[63]:
    [0.048011217492305636,
     0.07982525918463046,
     0.16502599283546138,
     0.1879030977165872]

    In [64]: heapq.nsmallest(2,l,key=lambda x:len(str(x)))   取出长度最小的两个                       
    Out[64]: [0.5813214897578903, 0.3945711068440729]
  • 相关阅读:
    轻量级数据库sqlite的使用
    Integer引发的思考
    css限制显示行数
    数据库 chapter 17 数据仓库与联机分析处理技术
    数据库 chapter 15 对象关系数据库系统
    数据库 chapter 16 XML数据库
    数据库 chapter 14 分布式数据库系统
    数据库 chapter 11 并发控制
    数据库 chapter 12 数据库管理系统
    数据库 chapter 13 数据库技术新发展
  • 原文地址:https://www.cnblogs.com/sidianok/p/12048885.html
Copyright © 2011-2022 走看看