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]
  • 相关阅读:
    wget 命令用法详解
    VI编辑器的使用方法
    Android APK反编译就这么简单 详解(附图)
    Nginx与X-Sendfile
    腾讯微博OAuth2.0认证介绍
    haporoxy的keeplaive ZZ
    如何监听非本地IP
    haproxy配置直接重定向url
    LVS与其他负载均衡软件的区别
    J2EE基础总结(4)——JSP
  • 原文地址:https://www.cnblogs.com/sidianok/p/12048885.html
Copyright © 2011-2022 走看看