Python的内置数据结构
日常编程,只要涉及对数据的操作就离不开数据结构,无论是自己实现的优化还是直接使用编程语言自带的数据结构。Python、Go、Java都以库或语言内置的形式提供强大的数据结构。本文分享Python的数据结构的用法和技巧。通常情况下编程语言自带的数据结构性能都很好,经得起考验,通常使用它而不是自己实现,除非出于某种场景的优化或通过再写而满足特定需求。
Python内置丰富的数据结构,在日常编程时一般不用自己重复造轮子,当然,为了满足丰富的场景需求,复杂的数据结构还是需要自己实现的,比如Flask Web框架中的HTTP工具werkzeug就实现了很多自己的数据结构以方便使用。Python实现的数据结构除了数据结构本身外,还要让其显得Pythonic。比如:实现一个容器或序列数据结构Seq,在Pythonic的方式是通过len(Seq)
取得数据结构的长度,而不是通过Seq.length()
等方式。这和Java很不同!今天总结Python的数据结构,以及谈谈如何实现Pythonic的数据结构。
Python数据结构的特点
Python数据结构有几个特点,包括约定的特殊方法框架(典型的方法包括__len__
),内置的数据结构支持推导式,迭代器和生成器等。下面一一讲述。
特殊方法
Python的特殊方法可以看做是Python类型系统的框架,该框架定义了获取特殊值的方法名,例如:
__len__
获取对象长度__hash__
获取可哈希对象的哈希值__contains__
item in container
操作的实现__setitem__
切片操作__gt__
对象大小比较,还有其它用于比较的特殊方法
等等。要让Python实现的数据结构更Pythonic,需要充分利用特殊方法的优势。接下来会讲到。
这些特殊方法统一了Python类型系统中获取相关属性和相关操作的实现。这样的好处是,不管什么类型,我们要获取某一属性或操作都可以使用内置函数激活。
推导式
Python内置的数据结构都支持推导式。
- list
1 | for num in range(1, 11) if num % 2 == 0] numbers = [num |
- tuple
元组的推导式返回生成器。
1 | for n in range(1, 11) if n % 2 == 0) i = (n |
- dict
1 | for i, j in zip('123', 'abc')} d = {i: j |
- set
1 | for num in range(1, 11) if num % 2 == 0} numbers = {num |
这就是推导式的基本用法,在复杂的场景下,可以通过嵌套使用上面的推导式以方便解决问题。但要考虑简便和可读性的平衡。
内建实现和库实现
像dict
、list
、set
这些数据结构及其方法的底层实现都是使用C语言,内置于Python的解析器中,即使没有标准库也可以使用,可以从源码看到它们的具体实现。这样的数据结构称为内建实现。数据结构如果来自标准库,例如collections.defaultdict
,称为库实现。需要注意的是,有些库看似是库实现,实质是C实现,例如array
模块就是C语言实现,而array.array
的底层也是C语言。
1 | array |
迭代器和生成器
什么是迭代器?
什么是生成器?
Python内置的所有数据结构都可以迭代。用户自定义数据结构也可以通过实现迭代器协议(实现__iter__
和__next__
方法 )实现迭代操作。生成器是实现yield语法的函数。
内建数据结构
str、bytes、bytearray、array.array、tuple、list、dict、memoryview、set、frozenset、代理数据结构ProxyTypes。
- str
- bytes
- bytearray
- array.array
- tuple
- list
- dict
- memoryview
- set
- frozenset
- 代理数据结构ProxyTypes
来自collections库的数据结构
collections
模块中常用的数据结构有:deque
, defaultdict
, namedtuple
, UserDict
, UserList
, UserString
, Counter
, OrderedDict
, ChainMap
, ByteString
。
view
类:MappingView
, KeysView
, ItemsView
, ValuesView
其他的类:Awaitable
, Coroutine
, AsyncIterable
, AsyncIterator
, Hashable
, Iterable
, Iterator
, Generator
, Sized
, Container
, Callable
, Set
, MutableSet
, Mapping
, MutableMapping
, Sequence
, MutableSequence
都是抽象类。
collections.abc
包括了自定义数据机构的抽象基类。collections
的类都是具体可用的。
- namedtuple
namedtuple
的使用在交互式下
1 | 'Person', 'age sex job name') Person = collections.namedtuple( |
对于一些简单的类,没有方法,只有属性,可以使用namedtuple
来代替。这样可以节省空间。
1 | class Person: |
类维护属性需要使用字典,而namedtuple
并不需要,这就是节省空间的原因所在。
- defaultdict
defaultdict
用于指定当键为空时,创建默认值的类型。
一个具体的例子:根据文件的哈希值归类文件(实际就是找出相同的文件)
1 | import collections |
- deque
deque
是双端队列,兼容list
对象的所有方法,同时添加常见的方法:’extendleft’, ‘maxlen’, ‘popleft’, ‘appendleft’, ‘rotate’。
1 | from collections import deque |
deque
既可以当做栈(LIFO)使用,也可以当做队列(FIFO)使用。当在编写功能代码时需要这两种数据结构,优先使用deque
或通过它做扩展。
演示下它的使用。
Counter
Counter
是一个基数统计类,常用于简单的基数统计。Counter
调用后返回一个字典对象,包括常规的字典方法,另外还有most_common
方法,返回出现频率最高的基数和统计频次。可以输入一个整形参数n,表示返回前n个最常出现的统计频次。下面是交互式下演示:1
2
3
4
5
6
7
8
9from collections import Counter
'The ORM is in contrast to the SQLAlchemy Expression Language, upon which the ORM is constructed. ' s =
c = Counter(s)
c
Counter({' ': 16, 'e': 7, 't': 7, 'n': 6, 's': 6, 'h': 6, 'i': 5, 'c': 5, 'o': 5, 'a': 3, 'u': 3, 'r': 3, 'M': 2, 'L': 2, 'p': 2, 'R': 2, 'g': 2, 'O': 2, 'S': 1, 'T': 1, 'x': 1, 'l': 1, '.': 1, ',': 1, 'd': 1, 'Q': 1, 'A': 1, 'E': 1, 'y': 1, 'm': 1, 'w': 1})
2) c.most_common(
[(' ', 16), ('e', 7)]
1) c.most_common(
[(' ', 16)]
OrderedDict
ChainMap
UserDict、UserList、UserString
用于多进程的queue
multiprocessing.Queue
multiprocessing.SimpleQueue
multiprocessing.JoinableQueue
用于线程并发的queue
queue.Queue
queue.PriorityQueue
queue.LifoQueue
用于协程并发的queue
asyncio.Queue
asyncio.PriorityQueue
asyncio.LifoQueue
弱引用数据结构
weakref.WeakKeyDictionary
weakref.WeakValueDictionary
weakref.WeakSet
自定义数据结构
本部分综合上述的内容实现一个LRUCache
。
1 | class LRUCache(collections.MutableMapping): |
总结
转载请包括本文地址:https://allenwind.github.io/blog/5004
更多文章请参考:https://allenwind.github.io/blog/archives/