zoukankan      html  css  js  c++  java
  • Python Collections详解

    Python Collections详解

    collections模块在内置数据结构(list、tuple、dict、set)的基础上,提供了几个额外的数据结构:ChainMap、Counter、deque、defaultdict、namedtuple和OrderedDict等;

    • ChainMap:Python3的新特性,将多个map组成一个新的单元(原来的map结构仍然存在,类似于这些map被存在了一个list中),这比新建一个map再将其他的map用update加起来快得多,ChainMap可以用来模拟嵌套的情景,而且多用于模板之中;
    • Counter:dict的子类,计算可hash对象的简单计算器;
    • deque:类似于list的双端队列,可以同时在list的左边增删元素;
    • defaultdict:在普通dict支上添加了默认工厂函数,使得键不存在时会自动生成相应类型的值;
    • namedtuple:加强版的tuple,最大作用就是给tuple的每个位置的值设置了别名,增加程序可读性;
    • OrderedDict:dict是无序的,但有时我们希望字典是有序的,OrderedDict提供了这项服务,OrderedDict中的键值对是按照它们加入时的顺序存储的;

    ChainMap

    from collections import ChainMap
    # 新建ChainMap及其结构
    m1 = {"color": "red", "user": "guest"}
    m2 = {"name": "cat", "age": 22}
    chain_map = ChainMap(m1, m2)
    print(chain_map)            # ChainMap({'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22})
    print(chain_map.items())    # ItemsView(ChainMap({'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22}))
    
    # 获取ChainMap中的元素
    print(chain_map.get("name"))    # cat
    print(chain_map.get("age"))     # 22
    print(chain_map.get("gender"))  # None
    
    # 新增
    m3 = {"date": "1-6", "gender": "female"}
    chain_map = chain_map.new_child(m3)
    print(chain_map.items())
    # ItemsView(ChainMap({'date': '1-6', 'gender': 'female'}, {'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22}))
    
    # parents属性
    print(chain_map.parents)                    # ChainMap({'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22})
    print(chain_map.parents.parents)            # ChainMap({'name': 'cat', 'age': 22})
    print(chain_map.parents.parents.parents)    # ChainMap({})
    
    # maps属性
    print(chain_map.maps)       # [{'date': '1-6', 'gender': 'female'}, {'color': 'red', 'user': 'guest'}, {'name': 'cat', 'age': 22}]
    
    • 可以传入都多个参数来初始化ChainMap,如果参数为空,会自动加一个空的map;
    • 获取的key如果不存在,则返回None;
    • 可以通过new_child()方法来新增map,新的map添加在最前面;
    • parents属性返回除去第一个map后的ChainMap实例;

    Counter

    Counter可以支持方便、快速的计数,例如:

    from collections import Counter
    
    counter = Counter()
    word_list = ["q", "a", "c", "b", "a", "q"]
    for word in word_list:
        counter[word] += 1
    print(counter)      # Counter({'q': 2, 'a': 2, 'c': 1, 'b': 1})
    print(counter.get("a"))     # 2
    

    对可迭代对象进行计数或者从另一个映射(counter)进行初始化:

    >>> from collections import Counter
    >>> counter = Counter()     # 创建空的counter
    >>> counter
    Counter()
    >>> counter = Counter("intelligence")   # 从可迭代的字符初始化counter
    >>> counter
    Counter({'e': 3, 'n': 2, 'i': 2, 'l': 2, 'c': 1, 't': 1, 'g': 1})
    >>> counter = Counter({"red": 255, "blu": 235})     # 从map映射初始化counter
    >>> counter
    Counter({'red': 255, 'blu': 235})
    >>> counter = Counter(person=100, cats=22, dogs=33)     # 从kwargs参数初始化counter
    >>> counter
    Counter({'person': 100, 'dogs': 33, 'cats': 22})
    >>> counter = Counter([2, 3, 5, 9, 2, 3, 9, 5, 4])      # 从列表书池化counter
    >>> counter
    Counter({9: 2, 2: 2, 3: 2, 5: 2, 4: 1})
    

    Counter对象类似于字典,如果某个项缺失,会返回0,而不会出现KeyError异常;

    >>> counter = Counter(["eggs", "harm"])
    >>> counter
    Counter({'eggs': 1, 'harm': 1})
    >>> counter["hanm"]     # 没有"hanm"
    0
    >>> counter["eggs"]     # 有"eggs"
    1
    >>>
    

    将一个元素的数目设置为0,并不能将它从counter中删除,使用del可以将这个元素删除:

    >>> from collections import Counter
    >>> counter = Counter({"eggs": 10, "harmer": 12})
    >>> counter
    Counter({'harmer': 12, 'eggs': 10})
    >>> counter["eggs"] = 0
    >>> counter
    Counter({'harmer': 12, 'eggs': 0})      # "eggs"依然存在
    >>> del counter["eggs"]
    >>> counter
    Counter({'harmer': 12})     # "eggs"不存在
    

    Counter对象支持以下三个字典不支持的方法,elements(),most_common(),substract();
    elements()返回一个迭代器,每个元素重复的次数为它的数目,顺序是任意顺序,如果某个元素的数目少于1,那么elements()将会忽略它;

    >>> counter = Counter(e=1, b=2, c=0, d=-2, a=3)
    >>> counter
    Counter({'a': 3, 'b': 2, 'e': 1, 'c': 0, 'd': -2})
    >>> counter.elements()
    <itertools.chain object at 0x7f7a6c7e40f0>
    >>> list(counter.elements())
    ['a', 'a', 'a', 'b', 'b', 'e']
    

    most_common()返回一个列表,包含counter中n个最大数目的元素,如果忽略n或者为None,most_common()将会返回counter中的所有元素,元素数目相同的将会以任意顺序排序;

    >>> most_counter = Counter("ambulance").most_common(3)
    >>> most_counter
    [('a', 2), ('l', 1), ('e', 1)]
    >>> most_counter = Counter("ambulance").most_common()
    >>> most_counter
    [('a', 2), ('l', 1), ('e', 1), ('c', 1), ('m', 1), ('u', 1), ('n', 1), ('b', 1)]
    >>> most_counter = Counter("ambulance").most_common(None)
    >>> most_counter
    [('a', 2), ('l', 1), ('e', 1), ('c', 1), ('m', 1), ('u', 1), ('n', 1), ('b', 1)]
    

    subtract()从一个可迭代对象中或者另一个映射(或counter)中所有元素相减,类似于dict.update();但subtract()是数目,而不是替换它们,输入和输出都有可能为0或为负;

    >>> a_counter = Counter(a=3, b=1, c=-2, d=6, e=0, f=-3)
    >>> b_counter = Counter(a=-2, b=0, c=2)
    >>> a_counter.subtract(b_counter)
    >>> a_counter
    Counter({'d': 6, 'a': 5, 'b': 1, 'e': 0, 'f': -3, 'c': -4})
    >>> b_counter.subtract(a_counter)
    >>> b_counter
    Counter({'c': 6, 'f': 3, 'e': 0, 'b': -1, 'd': -6, 'a': -7})
    

    update()从一个可迭代对象中或者另外一个映射(或counter)中所有元素相加,类似于dict.update;但update()是数目相加而非替换它们,另外可迭代对象是一个元素序列,而非(key,value)对构成的序列;

    >>> a_counter
    Counter({'d': 6, 'a': 5, 'b': 1, 'e': 0, 'f': -3, 'c': -4})
    >>> d_counter
    Counter({'c': 3, 'b': 2, 'a': 1, 'd': -6})
    >>> a_counter.update(d_counter)
    >>> a_counter
    Counter({'a': 6, 'b': 3, 'e': 0, 'd': 0, 'c': -1, 'f': -3})
    >>> d_counter.update(a_counter)
    >>> d_counter
    Counter({'a': 7, 'b': 5, 'c': 2, 'e': 0, 'f': -3, 'd': -6})
    

    Counter对象常见的操作:

    >>> c_counter
    Counter({'d': 6, 'a': 3, 'c': 0, 'b': -4})
    >>> sum(c_counter.values())     # 统计所有的数目
    5
    >>> list(c_counter)     # 列出所有唯一的元素
    ['c', 'd', 'b', 'a']
    >>> set(c_counter)      # 转换为set
    {'c', 'a', 'b', 'd'}
    >>> dict(c_counter)     # 转换为常见的dict
    {'c': 0, 'a': 3, 'b': -4, 'd': 6}
    >>> c_counter.items()       # 转换为(element, counter)对构成的列表
    dict_items([('c', 0), ('d', 6), ('b', -4), ('a', 3)])
    >>> c_counter.most_common()     # Counter中的所有元素
    [('d', 6), ('a', 3), ('c', 0), ('b', -4)]
    >>> c_counter.most_common()[:-4:-1]     # 切片输出
    [('b', -4), ('c', 0), ('a', 3)]
    >>> c_counter += Counter()      # 删除数目为0和负数的元素
    >>> c_counter
    Counter({'d': 6, 'a': 3})
    >>> Counter(dict(c_counter.items()))        # 从(element, counter)对构成的列表转换为Counter
    Counter({'d': 6, 'a': 3})
    >>> c_counter.clear()       # 清空Counter
    >>> c_counter
    Counter()
    

    在Counter对象进行数学操作,得多集合(counter中元素数目大于0)加减法操作,是相加或相减对应元素的数目;交集和并集返回对应数目的最小值和最大值;每个操作均接受有符号的数目,但输出并不包含数目为0或为负的元素;

    >>> c = Counter(a=3,b=1,c=-2)
    >>> d = Counter(a=1,b=2,c=4)
    >>> c+d#求和
    Counter({'a': 4, 'b': 3, 'c': 2})
    >>> c-d#求差
    Counter({'a': 2})
    >>> c & d#求交集
    Counter({'a': 1, 'b': 1})
    >>> c | d#求并集
    Counter({'c': 4, 'a': 3, 'b': 2})
    

    deque

    deque是栈和队列的一种广义实现。deque是"double-end queue"的简称,deque支持线程安全、有效内存也以近似O(1)的性能在deque的两端插入和删除元素,尽管list也支持相似的操作,但它主要在固定长度操作上的优化,从而在pop(0)和insert(0, v)(会改变数据的位置和大小)上有O(n)的时间复杂度。
    deque支持如下方法:

    • append(element):将元素添加到deque的右侧;
    • appendleft(element):将元素添加到deque的左侧;
    • clear():将deque中的元素全部删除,最后长度为0;
    • count(element):返回deque中元素等于element的个数;
    • extend(iterable):将可迭代变量iterable中的元素添加到deque的右侧;
    • extendleft(iterable):将可迭代变量iterable中的元素添加到deque的左侧,往左添加序列的顺序与可迭代变量iterable中的元素相反;
    • pop():移除和返回deque中最右边的元素,如果没有元素,将会产生IndexError异常;
    • popleft():移除和返回deque中最左侧的元素,如果没有元素,将会产生IndexError异常;
    • remove(value):移除第一次出现的value,如果没有找到,将会产生ValueError异常;
    • reverse():反转deque中的元素,并返回None;
    • rotate(n):从右侧反转n步,如果n为负数,则从左侧反转。d.rotate(1)等于d.appendleft(d.pop());
    • maxlen:只读的属性,deque的最大长度,如果无解,就返回None;

    除了以上的方法之外,deque还支持迭代、序列化、len(d)、reversed(d)、copy.copy(d)、copy.deepcopy(d),通过in操作符进行成员测试和下标引用,索引的时间复杂度在两端是O(1),在中间是O(n),为了快速获取,可以使用list代替;

    >>> from collections import deque
    >>> deq = deque("police")       # 创建deque
    >>> for ele in deq:     # 遍历deque
    ...     print(ele.upper())
    ...
    P
    O
    L
    I
    C
    E
    >>> deq.append("r")     # deque右侧添加元素
    >>> deq.appendleft("s")     # deque左侧添加元素
    >>> deq
    deque(['s', 'p', 'o', 'l', 'i', 'c', 'e', 'r'])
    >>> deq.pop()       # 返回和移除最右侧元素
    'r'
    >>> deq.popleft()       # 返回和移除最左侧元素
    's'
    >>> list(deq)       # 以列表形式展示deque的内容
    ['p', 'o', 'l', 'i', 'c', 'e']
    >>> deq[0]      # 获取最左侧的元素
    'p'
    >>> deq[-1]     # 获取最右侧的元素
    'e'
    >>> list(reversed(deq))     # 以列表形式展示出倒序的deque的内容
    ['e', 'c', 'i', 'l', 'o', 'p']
    >>> "l" in deq      # 成员判断
    True
    >>> deq.extend("terrorist")     # 添加多个元素
    >>> deq
    deque(['p', 'o', 'l', 'i', 'c', 'e', 't', 'e', 'r', 'r', 'o', 'r', 'i', 's', 't'])
    >>> deq.rotate(1)       # 往右反转
    >>> deq
    deque(['t', 'p', 'o', 'l', 'i', 'c', 'e', 't', 'e', 'r', 'r', 'o', 'r', 'i', 's'])
    >>> deq.rotate(-1)      # 往左反转
    >>> deq
    deque(['p', 'o', 'l', 'i', 'c', 'e', 't', 'e', 'r', 'r', 'o', 'r', 'i', 's', 't'])
    >>> deque(reversed(deq))        # 以逆序新建一个deque
    deque(['t', 's', 'i', 'r', 'o', 'r', 'r', 'e', 't', 'e', 'c', 'i', 'l', 'o', 'p'])
    >>> deq.clear()     # 清空deque
    >>> deq.pop()       # 不能在空的deque上pop
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
    IndexError: pop from an empty deque
    >>> deq.extendleft("terrorist")     # 向左扩展
    >>> deq
    deque(['t', 's', 'i', 'r', 'o', 'r', 'r', 'e', 't'])
    

    其他的应用:
    1、限定长度的deque提供了Unix中tail命令相似的功能:

    from collections import deque
    
    
    def tail(filename, n=10):
        return deque(open(filename, encoding="utf-8"), n)
    
    
    print(tail("collections.txt", 10))
    

    2、使用deque维护一个列表(右侧添加元素,左侧删除元素)中窗口的平均值:

    from collections import deque
    import itertools
    
    
    def moving_average(iterable, n=3):
        it = iter(iterable)
        # 第一次只有两个元素,在右移的过程中,需要先删除最左端的元素,因此先在最左端加入0
        deq = deque(itertools.islice(it, n-1))
        deq.appendleft(0)
        s = sum(deq)
        for ele in it:
            # 删除最左端的元素,再加上新元素
            s += ele - deq.popleft()
            # 右端添加新元素
            deq.append(ele)
            yield s / float(n)
    
    
    array = [40, 30, 50, 46, 39, 44]
    for element in moving_average(array, n=3):
        print(element)
    

    3、rotate()方法提供了一种实现deque切片和删除的方式,例如:del deque[n]依赖于rotate方法的纯Python实现,如下:

    from collections import deque
    
    
    def delete_nth(deq, n):
        # 从左侧开始,将n个元素翻转到右侧
        deq.rotate(-n)
        # 删除第n个元素
        deq.popleft()
        # 从右侧开始,将n个元素翻转到左侧
        deq.rotate(n)
    
    
    d = deque("terrorist")
    delete_nth(d, n=3)
    print(d)        # deque(['t', 'e', 'r', 'o', 'r', 'i', 's', 't'])
    

    4、slice依赖于rotate方法的纯Python实现,如下:

    from collections import deque
    
    
    def slice_nth(deq, m, n):
        # 从左侧开始,将前m个元素翻转到右侧
        deq.rotate(-m)
        i = m
        slice_list = []
        # 依次将[m, n]区间的元素出栈
        while i < n:
            item = deq.popleft()
            slice_list.append(item)
            i += 1
    
        # 再将出栈的元素扩展到deque右侧
        deq.extend(slice_list)
        # 从右侧开始,将后n个元素翻转到左侧
        deq.rotate(n)
        return slice_list
    
    
    d = deque("abcdefgh")
    print(slice_nth(d, 1, 5))       # ['b', 'c', 'd', 'e']
    

    defaultdict

    defaultdict是内置数据结构dict的一个子类,基本功能与dict一样,只是重写了一个方法__missing__(key)和增加了一个可写的对象变量default_factory。

    >>> dir(dict)
    ['__class__', '__contains__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
    >>> from collections import defaultdict
    >>> dir(defaultdict)
    ['__class__', '__contains__', '__copy__', '__delattr__', '__delitem__', '__dir__', '__doc__', '__eq__', '__format__', '__ge__', '__getattribute__', '__getitem__', '__gt__', '__hash__', '__init__', '__iter__', '__le__', '__len__', '__lt__', '__missing__', '__ne__', '__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__setitem__', '__sizeof__', '__str__', '__subclasshook__', 'clear', 'copy', 'default_factory', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 'setdefault', 'update', 'values']
    

    missing(key):

    • 1.如果default_factory属性为None,就报出以key作为遍历的KeyError异常;
    • 2.如果default_factory不为None,就会向给定的key提供一个默认值,这个值插入到字典中,并返回;
    • 3.如果default_factory报出异常,这个异常在传播时不会改变;
    • 4.这个方法是当要求的key不存在时,dict类中的__getitem__()方法所调用,无论它返回或者报异常,最终返回或报出给__getitem__();
    • 5.只有__getitem__()才能调用__missing__(),这意味着,如果get()起作用,如普通的字典,将会返回None作为默认值,而不是使用default_factory;

    default_factory这个属性用于__missing__()方法,使用构造器中的第一个参数初始化;
    使用list作为default_factory,很容易将一个key-value的序列转换为一个关于list的字典:

    >>> from collections import defaultdict
    >>> s_dict = [("red", 3), ("blue", 1), ("yellow", 2), ("blue", 4), ("yellow", 5)]
    >>> d_dict = defaultdict(list)
    >>> for key, value in s_dict: d_dict[key].append(value)
    ...
    >>> d_dict.items()
    dict_items([('yellow', [2, 5]), ('red', [3]), ('blue', [1, 4])])
    

    当每个key第一次遇到时,还没有准备映射。首先会使用default_factory函数自动创建一个空的list,list.append()操作将value添加至新的list,当key再次遇到时,通过查表,返回对应这个key的list,list.append()会将新的value添加至list,这个技术要比dict.setdefault()要简单快速;

    >>> e = {}
    >>> for k, v in s_dict: e.setdefault(k, []).append(v)
    ...
    >>> e.items()
    dict_items([('yellow', [2, 5]), ('red', [3]), ('blue', [1, 4])])
    

    设置default_factory为int,使得defaultdict可以用于计数:

    >>> str = "intelligence"
    >>> dict = defaultdict(int)
    >>> for k in str: dict[k]+=1
    ...
    >>> dict.items()
    dict_items([('i', 2), ('t', 1), ('e', 3), ('g', 1), ('l', 2), ('n', 2), ('c', 1)])
    

    当一个字母第一次遇到时,默认从default_factory中调用int()用于提供一个默认为0的计数,递增操作会增加每个字母的计数。函数int()经常返回0,是常量函数的一种特例,一种更快和更灵活的创建常量函数的方式是使用itertools.repeat(),可以提供任意常量值(不仅仅是0):

    import itertools
    from collections import defaultdict
    
    
    def constant_factory(value):
        return lambda: next(itertools.repeat(value))        # Python3的.next方法已经不存在了所以使用next()方法,如果没有lambda会出错<不信你干掉它试试!!!>
    
    
    def_dict = defaultdict(constant_factory("<missing>"))
    def_dict.update(name="Jack", action="speak")
    print("%(name)s %(action)s to %(object)s" % def_dict)       # Jack speak to <missing>
    

    将default_factory设置为set,使得defaultdict可以建立一个关于set的字典:

    >>> s = [('red', 1), ('blue', 2), ('red', 3), ('blue', 4), ('red', 1), ('blue',
    ... 4)]
    >>> d = defaultdict(set)
    >>> for k, v in s: d[k].add(v)
    ... 
    >>> d.items()
    dict_items([('red', {1, 3}), ('blue', {2, 4})])
    

    namedtuple

    命名元组,意味着给元组中的每个位置赋予含义,意味着代码可读性更强,namedtuple可以在任何常规元素使用的地方使用,而且它可以通过名称来获取字段信息而不仅仅是通过位置索引。

    # 基本用法
    >>> from collections import namedtuple
    >>> Point = namedtuple("Point", ["x", "y"])
    >>> p = Point(11, y=22)     # 实例化一个对象,可以使用位置或关键字
    >>> p[0] + p[1]     # 通过索引访问元组中的元素
    33
    >>> x, y = p        # 分开,类似于常规的元组
    >>> x, y
    (11, 22)
    >>> p.x + p.y       # 通过名称来访问
    33
    >>> p       # 可读的__repr__,通过name = value风格
    Point(x=11, y=22)
    

    namedtuple在给csv或者sqlite3返回的元组附上名称特别有用:

    from collections import namedtuple
    import csv
    
    EmployeeRecord = namedtuple('EmployeeRecord', 'name, age, title, department, paygrade')
    
    for emp in map(EmployeeRecord._make, csv.reader(open("employees.csv", "rb"))):
        print(emp.name, emp.title)
    
    import sqlite3
    conn = sqlite3.connect('/companydata')
    cursor = conn.cursor()
    cursor.execute('SELECT name, age, title, department, paygrade FROM employees')
    for emp in map(EmployeeRecord._make, cursor.fetchall()):
        print(emp.name, emp.title)
    

    除了从tuple继承的方法之外,namedtuple还支持三种法发和一个属性,为了避免名称冲突,这些方法和属性以下划线开始。
    somenamedtuple._make():从已有的序列或者可迭代对象中创建一个新的对象:

    >>> t = [99, 88]
    >>> Point._make(t)
    Point(x=99, y=88)
    

    somenamedtuple._asdict():返回一个OrderedDict,由名称到对应值建立映射:

    >>> p = Point(x=11, y=22)
    >>> p
    Point(x=11, y=22)
    >>> p_dict = p._asdict()
    >>> p_dict
    OrderedDict([('x', 11), ('y', 22)])
    

    somenamedtuple._replace():返回一个新的namedtuple对象,用新值替换指定名称中的值:

    >>> p2 = p._replace(x=33)
    >>> p2
    Point(x=33, y=22)
    

    somenamedtuple._fields:以字符串的构成的元组的形式返回namedtuple的名称,在自省或基于一个已存在的namedtuple中创建新的namedtuple时,非常有用:

    >>> p._fields
    ('x', 'y')
    >>> Color = namedtuple("Color", "red blue green")
    >>> Pixel = namedtuple("Pixel", Point._fields + Color._fields)
    >>> Pixel(11, 22, 127, 205,0)
    Pixel(x=11, y=22, red=127, blue=205, green=0)
    

    当名称存储在字符串中,可以使用getattr()函数进行检索:

    >>> getattr(p, "x")
    11
    

    使用**操作符,可以将一个字典转换成namedtuple:

    >>> d = {"x": 11, "y": 22}
    >>> Point(**d)
    Point(x=11, y=22)
    

    由于namedtuple也是Python中的一个类,因此在子类中,它很容易添加或修改一些功能,如下是添加一个可计算名称和固定长度的输出格式;子类中的__slots__是一个空的元组,可以通过避免字典实例的创建来节约内存开销:

    from collections import namedtuple
    
    
    class Point(namedtuple("Point", "x y")):
        __slots__ = ()
    
        @property
        def hypot(self):
            return (self.x ** 2 + self.y ** 2) ** 0.5
    
        def __str__(self):
            return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)
    
    
    for p in Point(3, 4), Point(14, 5/7):
        print(p)
    
    # Point: x= 3.000  y= 4.000  hypot= 5.000
    # Point: x=14.000  y= 0.714  hypot=14.018
    

    子类在增加、存储名称时,并不是非常有用;相反,可以容易地通过_fields属性来创建一个新的namedtuple:

    >>> Point3D = namedtuple("Point3D",Point._fields + ('z',))
    >>> Point3D._fields
    ('x', 'y', 'z')
    

    默认可以通过_replace()来实现,以便于标准化一个原型实例:

    >>> Account = namedtuple('Account','owner balance transaction_count')
    >>> default_account = Account('<owner name>',0.0,0)
    >>> johns_account = default_account._replace(owner = "John")
    >>> johns_account
    Account(owner='John', balance=0.0, transaction_count=0)
    

    枚举类型常量可以通过namedtuple来实现,更简单和有效的方式是通过简单的类声明:

    Status = namedtuple('Status','open pending closed')._make(range(3))
    print Status
    
    class Status:
    open, pending, closed = range(3)
    
    print Status.open
    print Status.pending
    print Status.closed
    

    运行输出结果为:

    Status(open=0, pending=1, closed=2)
    0
    1
    2
    

    OrderedDict

    • OrderedDict类似于正常的字典,只是它记录了元素插入的顺序,当在有序的字典上迭代时,返回的元素就是它们第一次添加的顺序。
    • OrderedDict返回已给dict的子类,支持常规的dict方法,OrderedDict是一个记录元素首次插入顺序的字典,如果一个元素重写已经存在的元素,那么原始的插入位置保持不变,如果删除一个元素再重新插入,那么它就在末尾;
    • OrderedDict.popitem(last=True),popitem方法返回和删除一个(key, value)对,如果last=True,就以LIFO方式执行,否则以FIFO方式执行;
    • OrderedDict也支持反向迭代,例如:reversed();
    • OrderedDict对象之间的相等测试,例如:list(od1.items()) == list(od2.items()),是对顺序敏感的;OrderedDict和其他的映射对象(例如:常规的字典)之间的相等测试是顺序不敏感的,这就允许OrderedDict对象可以在使用常规字典的地方替换掉常规字典;
    • OrderedDict构造器和update()方法可以接收关键字参数,但是它们丢失了顺序,因为Python的函数调用机制是将一个无须的字典传入关键字变量;

    一个有序的字典记住它的成员插入顺序,可以使用排序函数,将其变为排序的字典:

    >>> org_dict = {"apple": 1, "tencent": 2, "alibaba": 3, "baidu": 4, "facebook": 5}
    >>> from collctions import OrderedDict
    >>> OrderedDict(sorted(org_dict.items(), key = lambda t: t[0])) # 根据key排序字典
    OrderedDict([('alibaba', 3), ('apple', 1), ('baidu', 4), ('facebook', 5), ('tencent', 2)])
    >>> OrderedDict(sorted(org_dict.items(), key = lambda t: t[1])) # 根据value排序字典
    OrderedDict([('apple', 1), ('tencent', 2), ('alibaba', 3), ('baidu', 4), ('facebook', 5)])
    >>> a = OrderedDict(sorted(org_dict.items(), key = lambda t: len(t[0])))    # 根据key的长度排序字典
    >>> a
    OrderedDict([('baidu', 4), ('apple', 1), ('tencent', 2), ('alibaba', 3), ('facebook', 5)])
    >>> del a["facebook"]
    >>> a
    OrderedDict([('baidu', 4), ('apple', 1), ('tencent', 2), ('alibaba', 3)])
    >>> a["facebook"]=9
    >>> a
    OrderedDict([('baidu', 4), ('apple', 1), ('tencent', 2), ('alibaba', 3), ('facebook', 9)])
    

    当元素删除时,排好序的字典保持着排序的顺序;但是当新增元素时,就会被添加到末尾,就不能保持已排序;
    创建一个有序的字典,可以记住最后插入的key的顺序,如果一个新的元素要重写已存在的元素,那么原始的插入位置就会变成末尾:

    from collections import OrderedDict
    
    
    class LastUpdateOrderedDict(OrderedDict):
        def __setitem__(self, key, value):
            if key in self:
                del self[key]
            OrderedDict.__setitem__(self, key, value)
    
    
    obje = LastUpdateOrderedDict()
    obje["amazon"] = 12
    obje["microsoft"] = -3
    print("the first output: ", obje)       # LastUpdateOrderedDict([('amazon', 12), ('microsoft', -3)])
    obje["amazon"] = -1
    print("the second output: ", obje)      # LastUpdateOrderedDict([('microsoft', -3), ('amazon', -1)])
    

    一个有序的字典可以和Counter类一起使用,counter对象就可以记住首次出现的顺序:

    from collections import Counter, OrderedDict
    
    
    class OrderedCounter(Counter, OrderedDict):
        def __repr__(self):
            return "%s(%r)" % (self.__class__.__name__, OrderedDict(self))
    
        def __reduce__(self):
            return self.__class__, (OrderedDict(self))
    
    
    # 和OrderedDict一起使用的Counter对象
    obj = OrderedCounter()
    word_list = ['t', 'e', 'r', 'o', 'r', 'i', 's', 't']
    for word in word_list:
        obj[word] += 1
    print("the new result is: ", obj)   # OrderedCounter(OrderedDict([('t', 2), ('e', 1), ('r', 2), ('o', 1), ('i', 1), ('s', 1)]))
    
    # 普通的Counter对象
    counter = Counter()
    for ele in word_list:
        counter[ele] += 1
    print("the ordinary result is: ", counter)      # Counter({'t': 2, 'r': 2, 'e': 1, 'o': 1, 'i': 1, 's': 1})
    
  • 相关阅读:
    JavaScript -基础- 变量、常量
    Python 执行SQL带参数
    Python 装饰器
    Bootstrap
    python 数据如何保存到excel中--xlwt
    django 中下载文件与下载保存为excel
    AttributeError: 'cx_Oracle.Cursor' object has no attribute 'numbersAsStrings'
    Python Django 之 基于JQUERY的AJAX 登录页面
    Python Django 之 登录页面
    Python pycharm 引入需要使用的包
  • 原文地址:https://www.cnblogs.com/love9527/p/9009398.html
Copyright © 2011-2022 走看看