String 与Byte
python3中,原生的string 从python2 的byte string 变成了unicode,并且是不可变类型(因此string可以被当作dict的key)。并且在python3 中,u” 格式的string等同于 ”。
很多时候我们习惯使用 ”.join((str_a, str_b, … str_n)) 来拼接多个string。这种写法的可读性并不高。而且在实际的工程中,我们很有可能会拿到混合类型的容器(即容器内内部既有字符串,又有数值类型)。对这类容器进行join时会遇到意外的错误,比如:TypeError: sequence item 1: expected str instance, int found
除去 join之外,我们也可以用+来做字符串拼接。
Cython 利用peephole 优化器实现了“常量折叠”的优化,它可以将最多20个字符(为什么最多只有20?)的拼接在编译期压缩优化。在字符串总长小于20的时候,+拼接效率极高,一旦长度超出20,+拼接的效率会迅速下降。
最佳实践建议我们用str.format() 或者’%’ 来拼接字符串(当然这前提是我们知道有多少字符串要进行拼接)。
集合 list, tuple, dictionary, set
list与tuple 的区别是前者是可更变的类型,后者是不可变类型: 所以不能用list作为dict的key,同样不能用list做set的key。
如果同样的方法既能用list实现,也能用tuple 实现,那么就无脑用tuple——因为tuple创建和拷贝的效率高。
很多人不知道,在Cython的世界中,list其实是数组而非链表。具体实现时,list初始化时会预留一段空间给append。因此list 的append和pop的性能接近于O(1)。 但是对于insert 和delete, list的时间复杂度是O(n)。
如果你真的需要linked list, python 提供了deque类型(在collections包里)。
列表推导
列表推导式在python中应用极广。如:
#列表推导
>>> [i for i in range(10)]
>>> print (i)
... [0,1,2,3,4,5,6,7,8,9]
# for循环
>>> res = []
>>> for i in range(10):
>>> res.append(i)
>>> print (res)
... [0,1,2,3,4,5,6,7,8,9]
与for 循环表达式相比,列表推导式更简洁易懂。
有一个著名谣言说,用列表推导式比用for循环的性能更好是因为解释器会先推导出最终列表的长度,所以可以避免多次reallocate list。(这是骗人的!)
尽管列表推导式在性能上并没有显著优于for 循环,但是从易读性和可维护性上来说,还是推荐用列表推导式来生成列表。
enumerate,zip
enumerate方法在遍历有 key value容器时会同时返回key 与value,在遍历无key容器时会给它加上一个索引。
zip方法可以将两个容器压缩成一个key value容器,长度与较短容器长度一致。
dict
dict是python中最常用的key value 容器。它的推导式和列表推导式比较类似,仅多了一个key : value 格式。
>>> squares = {number: number **2 for number in range(5)}
>>> print (squares)
... {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}
在python3中,keys(), values(), items() 的返回类型是view 而不是python2 中的list。
相比于list,view会随着字典内容的更新而更新,不需要额外的存储空间,并且也支持len() 与in 操作。
python3 中, dict的 iterkeys(), itervalues() 和 iteritems() 方法也被移除。
一些实现细节:
在Cython中,字典是由hash table实现的。只有不可变类型可以作为key。如果你想要自己创建可以作为hash key的不可变类型,切记实现它的 __hash__ (计算hash 值)与__eq__ (比较)这两个魔术方法。
Cython使用开放寻址法来解决hash 冲突。
在字典中,key的存放并没有顺序一说(想象下所有的key都被保存在一个set中)。如果需要key值有序,用OrderedDict。本质上来所OrderedDict是一个红黑树。
set
set是一个可变的容器(不可变的集合是frozenset)。从数学定义上来说,set中的元素具有无序性,唯一性,确定性。所以set中的每一个元素必须是不可变类型,而且set常被用来去重复值。
一些实现细节:
同样,在Cython中,set也是由hash table 实现的(事实上,set更类似一个value为bool的dict)。因此,set和dict相比,操作的时间复杂度十分类似。
迭代器:
实现__next__ 和 __iter__ 方法即可。
class CountDown:
def __init__(self, step):
self.step = step
def __next__(self):
“””Return the next element.”””
if self.step <= 0:
raise StopIteration
self.step -= 1
return self.step
def __iter__(self):
“””Return the iterator itself.”””
return self