python中有些跟对象自己有关的方法, 以两个下划线开始,两个下划线结束, 通常称为魔法方法(magic method). 好比 obj[key] 的背后就是 __getitem__
方法,为了能求得my_collection[key]
的值,解释器实际上会调用my_collection.__getitem__(key)
。python
首先明确一点,特殊方法的存在是为了被 Python 解释器调用的,你本身并不须要调用它们.git
特殊几个方法:算法
__init__() 构造函数 __len__() 返回集合的Count __getitem__() 根据传入的位置参数,取出集合内的对象 __repr__() 根据对象的属性等返回一个字符串表示 __repr__ 和 __str__ 的区别在于,后者是在 str() 函数被使用,或是在用 print 函数打印一个对象的时候才被调用的,而且它返回的字符串对终端用户更友好。 若是你只想实现这两个特殊方法中的一个,__repr__ 是更好的选择,由于若是一个对象没有 __str__ 函数,而 Python 又须要调用它的时候,解释器会用 __repr__ 做为替代 __add__ 和 __mul__ 分别实现了简单的+和*的运算符重载, 中缀运算符的基本原则就是不改变操做对象,而是产出一个新的值. __bool__ bool(x) 的背后是调用 x.__bool__() 的结果;若是不存在 __bool__ 方法,那么 bool(x) 会尝试调用 x.__len__()。若返回 0,则 bool 会返回False;不然返回 True。 其他的一些魔法方法能够在Python文档中搜索Data Model查看.
len()为何不是普通函数, 若是是普通函数据就直接从实例对象中取出该函数或者是对应的属性值就能够了, 可是若是是外在的实现len()就能够把len()适用于自定义函数, 而且具备一样的风格而不是有时候是count()有时候是len()数组
容器序列数据结构
扁平序列多线程
因此容器类型是存储的不一样类型的对象的引用, 扁平类型是直接在一块连续内存中存储的值, 注意不是引用.可是里面只能放字符, 字节, 数值等这种基本类型.app
可变序列ide
不可变序列函数
列表推导和生成器表达式
列表推导是构建列表(list)的快捷方式,而生成器表达式则能够用来建立其余任何类型的序列.ui
>>> symbols = '$¢£¥€¤' >>> codes = [] >>> for symbol in symbols: ... codes.append(ord(symbol)) ... >>> codes [36, 162, 163, 165, 8364, 164]
没使用列表推导
>>> symbols = '$¢£¥€¤' >>> codes = [ord(symbol) for symbol in symbols] >>> codes [36, 162, 163, 165, 8364, 164]
使用列表推导, 通常来讲不要超过两行
列表推导、生成器表达式,以及同它们很类似的集合(set)推导和字典(dict)推导,在 Python 3 中都有了本身的局部做用域,就像函数似的。表达式内部的变量和赋值只在局部起做用,表达式的上下文里的同名变量还能够被正常引用,局部变量并不会影响到它们。
>>> x = 'ABC' >>> dummy = [ord(x) for x in x] >>> x ➊ 'ABC' >>> dummy ➋ [65, 66, 67] >>>
在python2.x中可能会出现x=C的状况, 即局部变量会覆盖外部变量, 没有本身的做用域, 在Python3中没有这种问题.
>>> symbols = '$¢£¥€¤' >>> beyond_ascii = [ord(s) for s in symbols if ord(s) > 127] >>> beyond_ascii [162, 163, 165, 8364, 164] >>> beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols))) >>> beyond_ascii [162, 163, 165, 8364, 164]
列表推导和filter和map的对比
列表推导一个很重要的使用场景就是生成笛卡尔集,
self._cards = [Card(rank, suit) for rank in self.ranks for suit in self.suits]
如上, 这样的代码比较多, 要想改变生成的元组的构成, 只要改变两个for的顺序便可, 固然也是至关于两层循环.
列表推导的做用只有一个:生成列表。若是想生成其余类型的序列,生成器表达式就派上了用场。
>>> colors = ['black', 'white'] >>> sizes = ['S', 'M', 'L'] >>> for tshirt in ('%s %s' % (c, s) for c in colors for s in sizes): ➊ ... print(tshirt) ... black S black M black L white S white M white L
和列表推导不一样, 生成器表达式是在循环真正执行的时候才会每次生成一个对象出来, 而不会先生成一个列表, 而后往里面填充, 更节省内存.
元组实际上是对数据的记录:元组中的每一个元素都存放了记录中一个字段的数据,外加这个字段的位置。正是这个位置信息给数据赋予了意义。
元组更相似一种数据输入输出的格式, 在对tuple作某些运算的时候能够保持聚合.
元组拆包能够应用到任何可迭代对象上,惟一的硬性要求是,被可迭代对象中的元素数量必需要跟接受这些元素的元组的空档数一致。不然则必须用*表示省略.
通常的拆包的例子以下:
>>> lax_coordinates = (33.9425, -118.408056) >>> latitude, longitude = lax_coordinates # 元组拆包 >>> latitude 33.9425 >>> longitude -118.408056
因此函数返回多个值其实也是返回一个tuple, 后面再进行拆包, 若是对不感兴趣的字段用"_"表示.
对于拆包中也能够有多个参数省略的作法, 使用*. 如:
>>> a, *body, c, d = range(5) >>> a, body, c, d (0, [1, 2], 3, 4) >>> *head, b, c, d = range(5) >>> head, b, c, d ([0, 1], 2, 3, 4)
注意这里的*必须放在一个变量前, 而变量也会变成一个list. 并且这里的拆包和结构还能够嵌套使用, 只要等式右边的结构是同构的.
相比tuple多了字段名, 可是对于通常的类实例, 也少了不少存储的内容, 本质上是把字段名做为类属性存储, 另外的值存储在tuple中.
建立一个具名元组须要两个参数,一个是类名,另外一个是类的各个字段的名字。后者能够是由数个字符串组成的可迭代对象,或者是由空格分隔开的字段名组成的字符串.
>>> City._fields ➊ ('name', 'country', 'population', 'coordinates') >>> LatLong = namedtuple('LatLong', 'lat long') >>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889)) >>> delhi = City._make(delhi_data) ➋ >>> delhi._asdict() ➌ OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))]) >>> for key, value in delhi._asdict().items(): print(key + ':', value) name: Delhi NCR country: IN population: 21.935 coordinates: LatLong(lat=28.613889, long=77.208889) >>>
❶ _fields 属性是一个包含这个类全部字段名称的元组。
❷ 用 _make() 经过接受一个可迭代对象来生成这个类的一个实例,它的做用跟
City(*delhi_data) 是同样的。
❸ _asdict() 把具名元组以 collections.OrderedDict 的形式返回,咱们能够利用它
来把元组里的信息友好地呈现出来。
切片和C语言中同样, 都是忽略最后一个元素, 而且使用0开头, 这样的话好处有二:
切片能够支持步长, seq.__getitem__(slice(start, stop, step))
, 且步长能够为负.
切片也支持赋值和del, 以下:
>>> l = list(range(10)) >>> l [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] >>> l[2:5] = [20, 30] >>> l [0, 1, 20, 30, 5, 6, 7, 8, 9] >>> del l[5:7] >>> l [0, 1, 20, 30, 5, 8, 9] >>> l[3::2] = [11, 22] >>> l [0, 1, 20, 11, 5, 22, 9] >>> l[2:5] = 100 ➊ Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only assign an iterable >>> l[2:5] = [100] >>> l [0, 1, 100, 22, 9]
这里要注意, 右侧的对象必须是一个可迭代的对象, 即便只有一个也要变成可迭代的.
对序列使用+和*, 都是不改变原序列, 从新建一个序列, 而后填充.
当序列中具备可变对象的引用时, 实际上是一个对象的多个拷贝, 并非新建的对象拷贝.
当对可变序列进行+=和*=操做时, 若是实现了_iadd_(), 那么就是原地增长, 不然则是寻找_add_(), 会从新构造一个序列而后拷贝过去.
当对不可变序列, 如tuple进行操做时, 就是直接拷贝增长了.
注意
对序列的排序有两种方法, list.sort()和sorted(). list.sort()会就地排序, 且返回一个None. 而sorted永远返回一个列表, 即便传入的是不可变对象.
二者都有两个参数:
reverse
若是被设定为 True,被排序的序列里的元素会以降序输出(也就是说把最大值看成最小值来排序)。这个参数的默认值是 False。
key
一个只有一个参数的函数,这个函数会被用在序列里的每个元素上,所产生的结果将是排序算法依赖的对比关键字。好比说,在对一些字符串排序时,能够用key=str.lower 来实现忽略大小写的排序,或者是用 key=len 进行基于字符串长度的排序。这个参数的默认值是恒等函数(identity function),也就是默认用元素本身的值来排序。
>>> fruits = ['grape', 'raspberry', 'apple', 'banana'] >>> sorted(fruits) >>> ['apple', 'banana', 'grape', 'raspberry'] >>> fruits ['grape', 'raspberry', 'apple', 'banana'] >>> sorted(fruits, reverse=True) ['raspberry', 'grape', 'banana', 'apple'] >>> sorted(fruits, key=len) ['grape', 'apple', 'banana', 'raspberry'] >>> sorted(fruits, key=len, reverse=True) ['raspberry', 'banana', 'grape', 'apple'] >>> fruits ['grape', 'raspberry', 'apple', 'banana'] >>> fruits.sort() >>> fruits ['apple', 'banana', 'grape', 'raspberry']
bisect 模块包含两个主要函数,bisect 和 insort,两个函数都利用二分查找算法来在有序序列中查找或插入元素。
bisect负责在一个序列中查找对应的应该插入的index, 后面能够直接插入或者调用bisect.insort.
array.array, set, deque等.
array.array就是C语言中数组的实现, 而且仅支持几种数值的实现. 内部存储的是字节信息, 能够方便的输入输出到文件, 占用内存和资源也较少.
直接映射在内存上的数据结构, 可使用cast方式改变内存读取解释的类型.
>>> numbers = array.array('h', [-2, -1, 0, 1, 2]) >>> memv = memoryview(numbers) ➊ >>> len(memv) 5 >>> memv[0] ➋ -2 >>> memv_oct = memv.cast('B') ➌ >>> memv_oct.tolist() ➍ [254, 255, 255, 255, 0, 0, 1, 0, 2, 0] >>> memv_oct[5] = 4 ➎ >>> numbers array('h', [-2, -1, 1024, 1, 2]) ➏
根据不一样的内存解释方法解释内存中的字节, 'h'有符号整数, 'B'无符号整数.
因此把高字节改为4, 则数值从0变到1024.
列表list本质上仍是顺序排列的数据结构, 而不是相似链表的形式. 增长和删除都有可能复制数据, 因此速度会受影响.
队列的操做都是原子操做, 因此可使用在多线程环境中.