• 第3章 Python的数据结构、函数和文件


    元祖

    如果元组中的某个对象是可变的,比如列表,可以用append或者extend在原地进行修改:

    In [11]: tup[1].append(3)
    
    In [12]: tup
    Out[12]: ('foo', [1, 2, 3], True)

    Python最近新增了更多高级的元组拆分功能,允许从元组的开头“摘取”几个元素。它使用了特殊的语法*rest,这也用在函数签名中以抓取任意长度列表的位置参数:

    In [29]: values = 1, 2, 3, 4, 5
    
    In [30]: a, b, *rest = values
    
    In [31]: a, b
    Out[31]: (1, 2)
    
    In [32]: rest
    Out[32]: [3, 4, 5]
    

    rest的部分是想要舍弃的部分,rest的名字不重要。作为惯用写法,许多Python程序员会将不需要的变量使用下划线:

    In [33]: a, b, *_ = values

    二分搜索和维护已排序的列表

    bisect模块支持二分查找,和向已排序的列表插入值。bisect.bisect可以找到插入值后仍保证排序的位置,bisect.insort是向这个位置插入值:

    In [67]: import bisect
    
    In [68]: c = [1, 2, 2, 2, 3, 4, 7]
    
    In [69]: bisect.bisect(c, 2)
    Out[69]: 4
    
    In [70]: bisect.bisect(c, 5)
    Out[70]: 6
    
    In [71]: bisect.insort(c, 6)
    
    In [72]: c
    Out[72]: [1, 2, 2, 2, 3, 4, 6, 7]
    

    注意:bisect模块不会检查列表是否已排好序,进行检查的话会耗费大量计算。因此,对未排序的列表使用bisect不会产生错误,但结果不一定正确。

    enumerate函数

    迭代一个序列时,你可能想跟踪当前项的序号。手动的方法可能是下面这样:

    i = 0
    for value in collection:
       # do something with value
       i += 1
    

    因为这么做很常见,Python内建了一个enumerate函数,可以返回(i, value)元组序列:

    for i, value in enumerate(collection):
       # do something with value
    

    当你索引数据时,使用enumerate的一个好方法是计算序列(唯一的)dict映射到位置的值:

    In [83]: some_list = ['foo', 'bar', 'baz']
    
    In [84]: mapping = {}
    
    In [85]: for i, v in enumerate(some_list):
       ....:     mapping[v] = i
    
    In [86]: mapping
    Out[86]: {'bar': 1, 'baz': 2, 'foo': 0}

    zip函数

    zip可以将多个列表、元组或其它序列成对组合成一个元组列表:

    In [89]: seq1 = ['foo', 'bar', 'baz']
    
    In [90]: seq2 = ['one', 'two', 'three']
    
    In [91]: zipped = zip(seq1, seq2)
    
    In [92]: list(zipped)
    Out[92]: [('foo', 'one'), ('bar', 'two'), ('baz', 'three')]
    

    zip可以处理任意多的序列,元素的个数取决于最短的序列:

    In [93]: seq3 = [False, True]
    
    In [94]: list(zip(seq1, seq2, seq3))
    Out[94]: [('foo', 'one', False), ('bar', 'two', True)]
    

    zip的常见用法之一是同时迭代多个序列,可能结合enumerate使用:

    In [95]: for i, (a, b) in enumerate(zip(seq1, seq2)):
       ....:     print('{0}: {1}, {2}'.format(i, a, b))
       ....:
    0: foo, one
    1: bar, two
    2: baz, three
    

    给出一个“被压缩的”序列,zip可以被用来解压序列。也可以当作把行的列表转换为列的列表。这个方法看起来有点神奇:

    In [96]: pitchers = [('Nolan', 'Ryan'), ('Roger', 'Clemens'),
       ....:             ('Schilling', 'Curt')]
    
    In [97]: first_names, last_names = zip(*pitchers)
    
    In [98]: first_names
    Out[98]: ('Nolan', 'Roger', 'Schilling')
    
    In [99]: last_names
    Out[99]: ('Ryan', 'Clemens', 'Curt')

    reversed函数

    reversed可以从后向前迭代一个序列:

    In [100]: list(reversed(range(10)))
    Out[100]: [9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
    

    要记住reversed是一个生成器(后面详细介绍),只有实体化(即列表或for循环)之后才能创建翻转的序列。

    字典

    keysvalues是字典的键和值的迭代器方法。虽然键值对没有顺序,这两个方法可以用相同的顺序输出键和值:

    In [117]: list(d1.keys())
    Out[117]: ['a', 'b', 7]
    
    In [118]: list(d1.values())
    Out[118]: ['some value', [1, 2, 3, 4], 'an integer']
    

    update方法可以将一个字典与另一个融合:

    In [119]: d1.update({'b' : 'foo', 'c' : 12})
    
    In [120]: d1
    Out[120]: {'a': 'some value', 'b': 'foo', 7: 'an integer', 'c': 12}
    

    update方法是原地改变字典,因此任何传递给update的键的旧的值都会被舍弃。

    用序列创建字典

    常常,你可能想将两个序列配对组合成字典。下面是一种写法:

    mapping = {}
    for key, value in zip(key_list, value_list):
        mapping[key] = value
    

    因为字典本质上是2元元组的集合,dict可以接受2元元组的列表:

    In [121]: mapping = dict(zip(range(5), reversed(range(5))))
    
    In [122]: mapping
    Out[122]: {0: 4, 1: 3, 2: 2, 3: 1, 4: 0}

    下面的逻辑很常见:

    if key in some_dict:
        value = some_dict[key]
    else:
        value = default_value
    

    因此,dict的方法get和pop可以取默认值进行返回,上面的if-else语句可以简写成下面:

    value = some_dict.get(key, default_value)
    

    get默认会返回None,如果不存在键,pop会抛出一个例外。关于设定值,常见的情况是在字典的值是属于其它集合,如列表。例如,你可以通过首字母,将一个列表中的单词分类:

    In [123]: words = ['apple', 'bat', 'bar', 'atom', 'book']
    
    In [124]: by_letter = {}
    
    In [125]: for word in words:
       .....:     letter = word[0]
       .....:     if letter not in by_letter:
       .....:         by_letter[letter] = [word]
       .....:     else:
       .....:         by_letter[letter].append(word)
       .....:
    
    In [126]: by_letter
    Out[126]: {'a': ['apple', 'atom'], 'b': ['bat', 'bar', 'book']}
    

    setdefault方法就正是干这个的。前面的for循环可以改写为:

    for word in words:
        letter = word[0]
        by_letter.setdefault(letter, []).append(word)
    

    collections模块有一个很有用的类,defaultdict,它可以进一步简化上面。传递类型或函数以生成每个位置的默认值:

    from collections import defaultdict
    by_letter = defaultdict(list)
    for word in words:
        by_letter[word[0]].append(word)

    嵌套列表推导式

    假设我们有一个包含列表的列表,包含了一些英文名和西班牙名:

    In [161]: all_data = [['John', 'Emily', 'Michael', 'Mary', 'Steven'],
       .....:             ['Maria', 'Juan', 'Javier', 'Natalia', 'Pilar']]
    

    你可能是从一些文件得到的这些名字,然后想按照语言进行分类。现在假设我们想用一个列表包含所有的名字,这些名字中包含两个或更多的e。可以用for循环来做:

    names_of_interest = []
    for names in all_data:
        enough_es = [name for name in names if name.count('e') >= 2]
        names_of_interest.extend(enough_es)
    

    可以用嵌套列表推导式的方法,将这些写在一起,如下所示:

    In [162]: result = [name for names in all_data for name in names
       .....:           if name.count('e') >= 2]
    
    In [163]: result
    Out[163]: ['Steven']
    

    嵌套列表推导式看起来有些复杂。列表推导式的for部分是根据嵌套的顺序,过滤条件还是放在最后。下面是另一个例子,我们将一个整数元组的列表扁平化成了一个整数列表:

    In [164]: some_tuples = [(1, 2, 3), (4, 5, 6), (7, 8, 9)]
    
    In [165]: flattened = [x for tup in some_tuples for x in tup]
    
    In [166]: flattened
    Out[166]: [1, 2, 3, 4, 5, 6, 7, 8, 9]
    

    记住,for表达式的顺序是与嵌套for循环的顺序一样(而不是列表推导式的顺序):

    flattened = []
    
    for tup in some_tuples:
        for x in tup:
            flattened.append(x)
    

    你可以有任意多级别的嵌套,但是如果你有两三个以上的嵌套,你就应该考虑下代码可读性的问题了。分辨列表推导式的列表推导式中的语法也是很重要的:

    In [167]: [[x for x in tup] for tup in some_tuples]
    Out[167]: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
    

    这段代码产生了一个列表的列表,而不是扁平化的只包含元素的列表。

    柯里化:部分参数应用

    柯里化(currying)是一个有趣的计算机科学术语,它指的是通过“部分参数应用”(partial argument application)从现有函数派生出新函数的技术。例如,假设我们有一个执行两数相加的简单函数:

    def add_numbers(x, y):
        return x + y
    

    通过这个函数,我们可以派生出一个新的只有一个参数的函数——add_five,它用于对其参数加5:

    add_five = lambda y: add_numbers(5, y)
    

    add_numbers的第二个参数称为“柯里化的”(curried)。这里没什么特别花哨的东西,因为我们其实就只是定义了一个可以调用现有函数的新函数而已。内置的functools模块可以用partial函数将此过程简化:

    from functools import partial
    add_five = partial(add_numbers, 5)
  • 相关阅读:
    违反并发性: UpdateCommand 影响了预期1条记录中的0条——我的解决方案
    (转)使用DataGridView控件常见问题解答
    C#中如何去除数组中重复的项
    C#中如何去除HTML标记
    (转)中断基本概念
    JavaScript 操作 Cookie
    要有梦想创造卓越的职业生涯
    《暗时间》读书笔记与读后感
    前端攻略系列
    前端攻略系列(一) 前端各种优化(保证持续更新)
  • 原文地址:https://www.cnblogs.com/wjw2018/p/10785341.html
Copyright © 2020-2023  润新知