流畅的python中有不少奇技淫巧,整本书都在强调如何最大限度地利用Python 标准库。介绍了不少python的不经常使用的数据类型、操做、库等,对于入门python后想要提高对python的认识应该有帮助。目前读一遍记录了一些有共鸣的操做:html
按可存放的元素类型分为:容器序列和扁平序列python
常见的容器序列包括:list,tuple,array.array,collections.deque等。程序员
常见的扁平序列包括:str,bytes,bytearray, memoryview, array.array等。express
按序列可否被修改分为:可变序列与不可变序列vim
标准库里collections模块中提供了不少与字典类型类似的变种。segmentfault
OrderDict: 这个类型在添加键的时候,会保存顺序,所以键的迭代顺序老是一致的数组
ChainMap: 该类型能够容纳数个不一样的映射对像,在进行键的查找时,这些对象会被当作一个总体逐个查找,直到键被找到为止安全
Counter: 这个映射类型会给键准备一个整数技术器,每次更行一个键的时候都会增长这个计数器,因此这个类型能够用来给散列表对象计数,或者当成多重集来用。多线程
UserDict: 这个类其实就是把标准的dict用Python又写了一遍。通常用来给程序员想要经过继承dict建立本身的dict时,代替dict使用的。主要是由于直接继承原生dict会出现bug。app
defaultdict:处理找不到的键的一个选择
当某个键不在映射里, 咱们也但愿也能获得一个默认值. 这就是 defaultdict , 它是 dict 的子类, 并实现了 missing 方法.
键必须是可散列的: 一个可散列的对象必须知足如下要求。 (1) 支持 hash() 函数,而且经过 __hash__() 方法所获得的散列值是不变的。 (2) 支持经过 __eq__() 方法来检测相等性。 (3) 若 a == b 为真,则 hash(a) == hash(b) 也为真。 全部由用户自定义的对象默认都是可散列的,由于它们的散列值由 id() 来获取,而 且它们都是不相等的。 字典在内存上开销很大(用内存换效率)。 元组取代字典就能节省空间的缘由有两个: (1) 避免了散列表所耗费的空间, (2) 无需把记录中字段的名字在每一个元素里都存一遍。 键的查询很快 键的次序取决于添加顺序 往字典里添加新键可能会改变已有键的顺序
结合的元素必须是可散列的 集合和消耗内存 能够很高效的判断元素是否存在于某个集合 元素的次序取决于被添加到集合里的顺序 往集合里添加元素,可能会改变集合里已有的元素次序
建立一个具名元组须要两个参数,一个是类名,另外一个是类的各个字段的名字。后者
能够是由数个字符串组成的可迭代对象,或者是由空格分隔开的字段名组成的字符串。
>>> from collections import namedtuple >>> City = namedtuple('City', 'name country population coordinates') >>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667)) >>> tokyo City(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667)) >>> tokyo.population 36.933 >>> tokyo.coordinates (35.689722, 139.691667) >>> tokyo[1] 'JP' >>> City = namedtuple('City_Name', 'name country population coordinates') >>> tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667)) >>> tokyo City_Name(name='Tokyo', country='JP', population=36.933, coordinates=(35.689722, 139.691667))
有跟可变序列有关的操做,包括 .pop、.insert 和 .extend。另外,数组还提供从文件
读取和存入文件的更快的方法,如 .frombytes 和 .tofile。
片。
在进行格式化输出时,%r 与 %s 的区别就比如 repr() 函数处理对象与 str() 函数处理对象的差异。
本文重点列举一些两者的差别化用法:
>> s = 'world' >> print('hello %s'%s) hello world >> print('hello %r'%s) hello 'world' >> str(s) 'world' >> repr(s) "'world'" 2. datetime 库中的 datetime 对象 >> from datetime import datetime >> timeinfo = datetime.today() >> timeinfo datetime.datetime(2016, 6, 7, 21, 17, 34, 925488) >> type(timeinfo) datetime.datetime >> repr(timeinfo) 'datetime.datetime(2016, 6, 7, 21, 17, 34, 925488)' >> str(timeinfo) '2016-06-07 21:17:34.925488'
Python dis 模块支持对Python代码进行反汇编, 生成字节码指令。
In[1]: def test(): ... x = 1 ... if x < 3: ... return "yes" ... else: ... return "no" In[2]: dis.dis(test) 2 0 LOAD_CONST 1 (1) 3 STORE_FAST 0 (x) 3 6 LOAD_FAST 0 (x) 9 LOAD_CONST 2 (3) 12 COMPARE_OP 0 (<) 15 POP_JUMP_IF_FALSE 22 4 18 LOAD_CONST 3 ('yes') 21 RETURN_VALUE 6 >> 22 LOAD_CONST 4 ('no') 25 RETURN_VALUE 26 LOAD_CONST 0 (None) 29 RETURN_VALUE >>> def add(a, b = 0): ... return a + b ... >>> >>> dis.dis(add) 2 0 LOAD_FAST 0 (a) 2 LOAD_FAST 1 (b) 4 BINARY_ADD 6 RETURN_VALUE >>>
class memoryview(obj)是python的内置类,若是要用memoryview 去引用一个object, 那么这个object 必须支持buffer protocol, python3 中原生(built-in) 支持buffer protocol的obj有bytes和bytearray,memoryview可使用不一样的方式读取和操做同一块内存,而且原有的内存字节不会随意移动。相似于C中的强转,好处是不会有内存拷贝。
例如,使用memoryview修改一个短整型有符号整数数组的数据。
from array import array from random import random numbers = array('h', [-2, -1, 0, 1, 2]) #signed short memv = memoryview(numbers) #5个短整型有符号整数的数组建立一个memoryview print (len(memv)) #打印长度 print (memv.tolist()) #转换成列表形式 memv_oct = memv.cast('B') #内存共享 转换成无符号字符类型 print (memv_oct.tolist()) memv_oct[5] = 4 #把位置5的字节赋值成4 print (numbers) #由于咱们把占 2 个字节的整数的高位字节改为了 4,因此这个有符号整数的值就变成了 1024 输出以下: 5 #数组长度 [-2, -1, 0, 1, 2] #列表形式显示 [254, 255, 255, 255, 0, 0, 1, 0, 2, 0]#长度扩大一倍 转换为无符号字符类型 array('h', [-2, -1, 1024, 1, 2]) #原来的数组被修改
bytearray是可变(mutable)的字节序列,相对于Python2中的str,但str是不可变(immutable)的。
在Python3中因为str默认是unicode编码,因此只有经过bytearray才能按字节访问。
下面两种行为的对比:
简单点就是,str和bytearray的切片操做会产生新的切片str和bytearry并拷贝数据,使用memoryview以后不会。
python2中的例子
>> a = 'aaaaaa' >> b = a[:2] # 会产生新的字符串 >> a = bytearray('aaaaaa') >> b = a[:2] # 会产生新的bytearray >> b[:2] = 'bb' # 对b的改动不影响a >> a bytearray(b'aaaaaa') >> b bytearray(b'bb')
>> a = 'aaaaaa' >> ma = memoryview(a) >> ma.readonly # 只读的memoryview True >> mb = ma[:2] # 不会产生新的字符串 >> a = bytearray('aaaaaa') >> ma = memoryview(a) >> ma.readonly # 可写的memoryview False >> mb = ma[:2] # 不会会产生新的bytearray >> mb[:2] = 'bb' # 对mb的改动就是对ma的改动 >> mb.tobytes() 'bb' >> ma.tobytes() 'bbaaaa'
Python 中有各类各样可调用的类型,所以判断置的 callable() 函数:
>>> abs, str, 13 (<built-in function abs>, <class 'str'>, 13) >>> [callable(obj) for obj in (abs, str, 13)] [True, True, False]
>>> import random >>> a=range(10) >>> random.shuffle(a) >>> a [1, 0, 8, 5, 6, 7, 9, 3, 2, 4] >>> random.shuffle(a) >>> a [7, 5, 6, 2, 1, 8, 9, 0, 3, 4]
Math Function Description abs() Returns absolute value of a number divmod() Returns quotient and remainder of integer division max() Returns the largest of the given arguments or items in an iterable min() Returns the smallest of the given arguments or items in an iterable pow() Raises a number to a power round() Rounds a floating-point value sum() Sums the items of an iterable Type Conversion Function Description ascii() Returns a string containing a printable representation of an object bin() Converts an integer to a binary string bool() Converts an argument to a Boolean value chr() Returns string representation of character given by integer argument complex() Returns a complex number constructed from arguments float() Returns a floating-point object constructed from a number or string hex() Converts an integer to a hexadecimal string int() Returns an integer object constructed from a number or string oct() Converts an integer to an octal string ord() Returns integer representation of a character repr() Returns a string containing a printable representation of an object str() Returns a string version of an object type() Returns the type of an object or creates a new type object Iterables and Iterators Function Description all() Returns True if all elements of an iterable are true any() Returns True if any elements of an iterable are true enumerate() Returns a list of tuples containing indices and values from an iterable filter() Filters elements from an iterable iter() Returns an iterator object len() Returns the length of an object map() Applies a function to every item of an iterable next() Retrieves the next item from an iterator range() Generates a range of integer values reversed() Returns a reverse iterator slice() Returns a slice object sorted() Returns a sorted list from an iterable zip() Creates an iterator that aggregates elements from iterables Composite Data Type Function Description bytearray() Creates and returns an object of the bytearray class bytes() Creates and returns a bytes object (similar to bytearray, but immutable) dict() Creates a dict object frozenset() Creates a frozenset object list() Constructs a list object object() Returns a new featureless object set() Creates a set object tuple() Creates a tuple object Classes, Attributes, and Inheritance Function Description classmethod() Returns a class method for a function delattr() Deletes an attribute from an object getattr() Returns the value of a named attribute of an object hasattr() Returns True if an object has a given attribute isinstance() Determines whether an object is an instance of a given class issubclass() Determines whether a class is a subclass of a given class property() Returns a property value of a class setattr() Sets the value of a named attribute of an object super() Returns a proxy object that delegates method calls to a parent or sibling class Input/Output Function Description format() Converts a value to a formatted representation input() Reads input from the console open() Opens a file and returns a file object print() Prints to a text stream or the console Variables, References, and Scope Function Description dir() Returns a list of names in current local scope or a list of object attributes globals() Returns a dictionary representing the current global symbol table id() Returns the identity of an object locals() Updates and returns a dictionary representing current local symbol table vars() Returns __dict__ attribute for a module, class, or object Miscellaneous Function Description callable() Returns True if object appears callable compile() Compiles source into a code or AST object eval() Evaluates a Python expression exec() Implements dynamic execution of Python code hash() Returns the hash value of an object help() Invokes the built-in help system memoryview() Returns a memory view object staticmethod() Returns a static method for a function __import__() Invoked by the import statement
类别 方法名 字符串 / 字节序列表示形式 __repr__、__str__、__format__、__bytes__ 数值转换 __abs__、__bool__、__complex__、__int__、__float__、__hash__、__index__ 集合模拟 __len__、__getitem__、__setitem__、__delitem__、__contains__ 迭代枚举 __iter__、__reversed__、__next__ 可调用模拟 __call__ 上下文管理 __enter__、__exit__ 实例建立和销毁 __new__、__init__、__del__ 属性管理 __getattr__、__getattribute__、__setattr__、__delattr__、__dir__ 属性描述符 __get__、__set__、__delete__ 跟类相关的服务 __prepare__、__instancecheck__、__subclasscheck__
bisect.bisect_left(a,x, lo=0, hi=len(a)) : 查找在有序列表 a 中插入 x 的index。lo 和 hi 用于指定列表的区间,默认是使用整个列表。若是 x 已经存在,在其左边插入。返回值为 index。 bisect.bisect_right(a,x, lo=0, hi=len(a)) bisect.bisect(a, x,lo=0, hi=len(a)) : 这2个函数和 bisect_left 相似,但若是 x 已经存在,在其右边插入。 bisect.insort_left(a,x, lo=0, hi=len(a)) : 在有序列表 a 中插入 x。和 a.insert(bisect.bisect_left(a,x, lo, hi), x) 的效果相同。 bisect.insort_right(a,x, lo=0, hi=len(a)) bisect.insort(a, x,lo=0, hi=len(a)) : 和 insort_left 相似,但若是 x 已经存在,在其右边插入。 Bisect 模块提供的函数能够分两类: bisect* 只用于查找 index, 不进行实际的插入;而 insort* 则用于实际插入。
from array import array from random import random floats = array('d', (random() for i in range(10**7))) fp = open('floats.bin', 'wb') floats.tofile(fp) fp.close() floats2 = array('d') fp = open('floats.bin', 'rb') floats2.fromfile(fp, 10**7) fp.close() floats2 == floats
from queue import Queue #LILO队列 q = Queue() #建立队列对象 q.put(0) #在队列尾部插入元素 q.put(1) q.put(2) print('LILO队列',q.queue) #查看队列中的全部元素 print(q.get()) #返回并删除队列头部元素 print(q.queue) from queue import LifoQueue #LIFO队列 lifoQueue = LifoQueue() lifoQueue.put(1) lifoQueue.put(2) lifoQueue.put(3) print('LIFO队列',lifoQueue.queue) lifoQueue.get() #返回并删除队列尾部元素 lifoQueue.get() print(lifoQueue.queue) from queue import PriorityQueue #优先队列 priorityQueue = PriorityQueue() #建立优先队列对象 priorityQueue.put(3) #插入元素 priorityQueue.put(78) #插入元素 priorityQueue.put(100) #插入元素 print(priorityQueue.queue) #查看优先级队列中的全部元素 priorityQueue.put(1) #插入元素 priorityQueue.put(2) #插入元素 print('优先级队列:',priorityQueue.queue) #查看优先级队列中的全部元素 priorityQueue.get() #返回并删除优先级最低的元素 print('删除后剩余元素',priorityQueue.queue) priorityQueue.get() #返回并删除优先级最低的元素 print('删除后剩余元素',priorityQueue.queue) #删除后剩余元素 priorityQueue.get() #返回并删除优先级最低的元素 print('删除后剩余元素',priorityQueue.queue) #删除后剩余元素 priorityQueue.get() #返回并删除优先级最低的元素 print('删除后剩余元素',priorityQueue.queue) #删除后剩余元素 priorityQueue.get() #返回并删除优先级最低的元素 print('所有被删除后:',priorityQueue.queue) #查看优先级队列中的全部元素 from collections import deque #双端队列 dequeQueue = deque(['Eric','John','Smith']) print(dequeQueue) dequeQueue.append('Tom') #在右侧插入新元素 dequeQueue.appendleft('Terry') #在左侧插入新元素 print(dequeQueue) dequeQueue.rotate(2) #循环右移2次 print('循环右移2次后的队列',dequeQueue) dequeQueue.popleft() #返回并删除队列最左端元素 print('删除最左端元素后的队列:',dequeQueue) dequeQueue.pop() #返回并删除队列最右端元素 print('删除最右端元素后的队列:',dequeQueue) 以上队列在多线程中可使用的且线程安全,但在多进程中都不能用于通讯。在多进程中,须要这样使用: from multiprocessing import Process, Queue myqueue = Queue(100) ## 参考 https://blog.csdn.net/sinat_38682860/article/details/80392493 https://www.cnblogs.com/cmnz/p/6936181.html
from keyword import kwlist print(kwlist)
import builtins dir(builtins)
https://segmentfault.com/a/1190000012724861 def test(): globals()['a2'] = 4 test() print a2 # 输出 4 def aaaa(): print locals() for i in ['a', 'b', 'c']: locals()[i] = 1 print locals() print a # 错误:NameError: global name 'a' is not defined aaaa()
动态地进行变量赋值时,locals() 看到的, 的确是函数的局部命名空间的内容, 可是它自己不能表明局部命名空间, 这就好像一个代理, 它收集了A, B, C的东西, 展现给我看, 可是我却不能简单的经过改变这个代理, 来改变A, B, C真正拥有的东西!
这也就是为何, 当咱们经过locals()[i] = 1的方式去动态赋值时, print a却触发了NameError异常, 而相反的, globals()确实真正的全局命名空间,
因此通常会说locals() 只读, globals() 可读可写。
对于通常不可变类型的变量来讲这两个方法没啥区别,但对于可变类型如list(列表),dict(字典)就有区别了,x += y 就地改变了list的值,而x = x + y建立了一个新的list并从新将x绑定上去,经过id(x)就能够看出。
l = l + [3, 4, 5] 这种背后就是BINARY_ADD l += [3, 4, 5] 这种背后就是INPLACE_ADD
+=实际上应该能算是一个增强版的+, 由于它比+多了一个写回自己的功能.不过是否可以写回自己, 仍是得看对象自身是否支持, 也就是说是否具有Py_NotImplemented标识, 是否支持sq_inplace_concat, 若是具有, 才能实现, 不然, 也就是和 + 效果同样而已.
不只仅是这些,当混合使用可变类型和不可变类型的时候,你会有更加惊奇的发现:
>>> t = ([],) >>> t[0] += [2, 3] Traceback (most recent call last): File "<input>", line 1, in ? TypeError: object doesn't support item assignment >>> t ([2, 3],)
明显的,元组不支持对其中元素的赋值——可是在对他使用+=后,元组里的list确实改变了!缘由依然是+=就地改变list的值。可是元组的赋值不被容许,当异发生时,元组中的list已经被就地改变了。
这就是一个我我的以为很是致命的陷阱。
解决方法:干脆避免使用+=,或者仅仅在整数时使用它。
>>> a=(1) # 错误姿式 >>> type(a) <type 'int'> >>> a=(1,) # 正确姿式 >>> type(a) <type 'tuple'>
通用的解决方案:
num_list = [1, 2, 3, 4, 5, 2, 2, 4] 1. 倒序循环遍历 for i in range(len(num_list) - 1, -1, -1): # 讲究 if num_list[i] == 2: num_list.pop(i) print(num_list) 2. 遍历拷贝的list,操做原始的list。对于过大的list,拷贝后可能很占内存,能够用倒序遍历的方法来实现。 for item in num_list[:]: # 保证能够把num_list从头遍历到尾 if item == 2: num_list.remove(item) # 从头删除遇到的第一个item print(num_list) 3. 对原来的列表作过滤,生成一个新的列表(假设determine(x)为判断条件的函数): list = [x for x in list if not determine(x)] 4. 在原来列表上作切片,仅保留须要的元素 list[:] = [x for x in list if not determine(x)] 5. python2.x ifilterfalse()方法 from itertools import ifilterfalse() list[:] = ifilterfalse(determine, list) 6. Python3 filterfalse()方法 from itertools import filterfalse list[:] = filterfalse(determine, list) 方法5,6对列表的修改会反应到其余对此列表的引用上。
def local_var_err(): b += [3] # UnboundLocalError: local variable 'b' referenced before assignment b = b + [2] # UnboundLocalError: local variable 'b' referenced before assignment
In [109]: %timeit -n100 a = (i for i in range(100000)) 100 loops, best of 3: 659 µs per loop In [110]: %timeit -n100 b = [i for i in range(100000)] 100 loops, best of 3: 2.68 ms per loop
In [112]: %timeit -n100 for x in (i for i in range(100000)):pass 100 loops, best of 3: 4.23 ms per loop In [113]: %timeit -n100 for x in [i for i in range(100000)]:pass 100 loops, best of 3: 3.49 ms per loop
空间换时间
# -*- coding:utf-8 -*- import timeit test_dict = {} class dome(object): def test_class(self): num = 100 self.test_dict = {} # 为了公平,每次执行都一样初始化新的 {} for i in range(num): self.test_dict[i] = i def test_local(self): num = 100 test_dict = {} # 为了公平,每次执行都一样初始化新的 {} for i in range(num): test_dict[i] = i self.test_dict = test_dict def test_global(self): num = 100 global test_dict test_dict = {} # 为了公平,每次执行都一样初始化新的 {} for i in range(num): test_dict[i] = i s = dome() print(timeit.timeit(stmt=s.test_class)) # 9.75976037823 print(timeit.timeit(stmt=s.test_local)) # 7.17526431985 print(timeit.timeit(stmt=s.test_global)) # 7.57540534177 """ 1. 访问局部变量速度要快不少 2. 循环以外能作的事不要放在循环内 在一些会频繁操做 类/实例属性 的状况下,应该是先把 属性 取出来存到 局部变量,而后用 局部变量 来完成操做。最后视状况把变更更新到 属性 上。 """
a=list(str(range(1000))) In [126]: %%timeit s="" for x in a: s+=x .....: 1000 loops, best of 3: 304 µs per loop In [127]: %%timeit .....: s="".join(a) .....: 10000 loops, best of 3: 59.3 µs per loop 参考博客 https://blog.csdn.net/xdhstc/article/details/51719892
# -*- coding:utf-8 -*- import timeit def test_1(): a = [True] * 100 s = [] for i in a: if i == True: s.append(i) return s def test_2(): a = [True] * 100 return [i for i in a if i is True] def test_3(): a = [True] * 100 return [i for i in a if i == True] def test_4(): a = [True] * 100 return [i for i in a if i] print(timeit.timeit(stmt=test_1)) # 11.5888194259 print(timeit.timeit(stmt=test_2)) # 6.00562291202 print(timeit.timeit(stmt=test_3)) # 7.15504198257 print(timeit.timeit(stmt=test_4)) # 4.29275713242
In [145]: %timeit -n100000 c = pow(2,20) 100000 loops, best of 3: 89.3 ns per loop In [146]: %timeit -n100000 c = 2**20 100000 loops, best of 3: 22.2 ns per loop
# -*- coding:utf-8 -*- import timeit def test1(): s = [] for z in range(10): for y in range(100): for x in range(1000): if x > 100 and y > 50 and z > 5: return s s.append((x, y, z)) def test2(): s = [] for x in range(1000): for y in range(100): for z in range(10): if x > 100 and y > 50 and z > 5: return s s.append((x, y, z)) print(timeit.timeit(stmt=test1, number=100)) # 14.1777687741 print(timeit.timeit(stmt=test2, number=100)) # 2.03417086749 print(sorted(test1()) == sorted(test2())) # False print(len(test1())) # 651101 print(len(test2())) # 101516