Python高级拾遗1

1、Python解释器碰到特殊的句法时,会使用特殊方法去激活一些基本的对象操作,这些特殊方法的名字以两个下划线开头,以两个下划线结尾,这些特殊方法名能让你自己的对象实现和支持以下语言架构并与之交互:迭代、集合类、属性访问、运算符重载、函数和方法的调用、对象的创建和销毁、字符串表示形式和格式化、管理上下文(with块)。

namedtuple具名元组,创建一些有少数属性没有方法的类。具名元组有一些自己的专有属性:_fields属性、类方法_make(iterable)和实例方法_asdict()。

__getitem__:提供索引、切片、迭代(和反向迭代)。

import collections
from random import choice

Card = collections.namedtuple('Card',['rank','suit'])#Card is class

class FrenchDeck:
    ranks = [str(n) for n in range(2,11)] + list('JQKA')
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank,suit) for suit in self.suits
                       for rank in self.ranks]

    def __len__(self):
        return len(self._cards)

    def __getitem__(self,position):
        return self._cards[position]

suit_values = dict(spades=3,hearts=2,diamonds=1,clubs=0)
def spades_high(card):
    rank_value = FrenchDeck.ranks.index(card.rank)
    return rank_value * len(suit_values) + suit_values[card.suit]

deck = FrenchDeck()
'''for card in reversed(deck):
    print(card)'''

for card in sorted(deck,key=spades_high):
    print(card)

特殊方法的存在是被解释器调用的,并不是被自己调用的,如__len__方法,用户实际调用的是len,内部会调用__len__。

很多时候,特殊方法的调用是隐式的,比如for i in x:,背后调用的实际是iter(x),这个函数背后实际上是__iter__(x)。

Python语言参考手册中的Data Model列出了83个特殊方法的名字,其中47个用于实现算术运算、位运算和比较操作。

2、序列

list、tuple、collections.deque这些序列能存放不同的类型,称为容器序列;str、bytes、bytearray、memoryview和array.array只能放同一种类型,称为扁平序列。容器序列存放的是它们所包含的任意对象的引用,扁平序列存放的是值。

可变序列:list、bytearray、array.array、collections.deque和memoryview

不可变序列:tuple、str和bytes

列表推倒的作用只有一个:生成列表。

元组拆包:city,year,pop=('Tokyo',2003,32450)    可以用_作占位符,用*接受多参数。

除了跟增减元素相关的方法之外,元组支持列表的其他所有方法,元组没有__reversed__方法。

如果赋值的对象是切片,那么赋值语句的右边必须是可迭代对象。

嵌套的列表易出错的地方:[['-']*3]*3,实际上里面的每一个嵌套列表是同样的引用对象,正确的写法为:[['-']*3 for i in range(3)]

序列的增量赋值:+=背后的特殊方法是__iadd__(就地加法),如果一个类没有实现这个方法的话Python就会退一步调用__add__,可变序列一般都实现了__iadd__方法。str是一个例外,因为对字符串做+=太普遍了,所以CPython对它做了优化,分配初始化内存的时候会为它留出额外的可扩展空间。

不要把可变对象放在元组里面,增量赋值 不是一个原子操作,可以用dis.dis得出汇编代码,分析性能优化。

list.sort会就地排序列表,返回None;内置sorted会新建一个列表作为返回值,这个方法可接受任何形式的可迭代对象作为参数,最后返回一个列表。sorted(sequencexxx,key,reverse)

bisect模块包含两个主要函数,bisect和insort,两个函数都利用二分查找法在有序序列中查找或插入元素。

array.array:如果需要只包含数字的列表,array.array比list更高效,还提供存取文件的方法:frombytes和tofile。

memoryview(内存视图)是一个内置类,能让用户在不复制内容的情况下共享内存,数据结构可以是任何形式,如PIL图片、SQLite数据库。

Numpy、Scipy、Pandas、Blaze

collections.deque是一个线程安全、可以快速从两端添加或删除元素的数据类型。

3、泛映射类型

标准库里所有映射类型都是用dict实现的,它的键必须是可散列的。如果一个对象是可散列的,那么在这个对象的生命周期中,它的散列值是不变的,而且这个对象需要实现__hash__方法。原子不可变序列str、bytes和数值类型都是可散列类型,frozenset也是可散列的,元组的话当他所包含的对象都是可散列的时候它才是可散列的。

一般来说,用户自定义类型都是可散列的,散列值就是它们的id()函数返回值。

字典的几种构造方法:

a=dict(one=1,two=2,three=3)           b={'one':1,'two':2,'three':3}       c=dict(zip(['one','two','three'],[1,2,3]))  

d = dict([('two',2),('onw',1),('three',3)])            e = dict({'three':3,'one':1,'two':2})             a=b=c=d=e

字典推导法

dict、collections.defaultdict、collections.OrderedDict

映射的弹性键查询:一个是通过defaultdict这个类型,一个是通过重写dict的__missing__方法。在defaultdict中如果查询不到键,default_factory会被调用。

如果要自定义一个映射类型,从collections.UserDict类继承比较好,这个类是把标准的dict用python又实现了一遍。

collections.OrderedDict在添加键的时候会保持顺序;collections.ChainMap可以容纳数个不同的映射对象;collections.Counter会给键准备一个整数计数器;

types模块的MappingProxyType会返回一个映射的只读视图。

集合中的元素必须是可散列的,set本身是不可散列的,frozenset是可散列的。

空集合必须用set创建而不是{}。

字典使用了散列表,散列表又必须是稀疏的,这导致它的空间效率低下,典型的空间换时间。

往字典里添加新键的时候Python解释器可能会为它扩容,这个过程中可能会发生新的散列冲突,导致新散列表中键的次序发生变化。如果你在迭代一个字典的所有键的过程中同时对字典进行修改,那么这个循环可能会跳过一些键。

4、bytes和bytearray的各个元素介于0-255之间,bytearray没有字面量句法,二进制序列有个类方法是str没有的:fromhex。

构建bytes或bytearray实例可调用各自的构造方法,传入以下参数:一个str对象和一个encoding关键字参数;一个可迭代对象,提供0-255之间的值;一个整数,使用空字节创建对应长度的二进制序列;一个实现了缓冲协议的对象。

struct模块提供了一些函数,把打包的字节序列转换成不同类型字段组成的元组,还有一些函数用于执行反向转换。struct模块能处理bytes、bytearray和memoryview。

为UnicodeEncodeError和UnicodeDecodeError指定错误处理函数。

统一字符编码探测包:chardet。

BOM:byte-order-mark,UTF-16中有,指明字节序。UTF-8的优势是,不管设备使用哪种字节序,生成的字节序列始终一致,因此不需要BOM。

Unicode三明治:bytes—>str     处理str      str—>bytes

不能依赖系统的默认编码。  

unicode标准提供了一个完整的数据库。

5、函数

高阶函数:接受函数为参数或者把函数作为结果返回的函数是高阶函数。比较常见的高阶函数有map、filter、deduce。列表推导和生成器表达式能代替map和filter的功能。

sum和deduce的通用思想是把某个操作连续应用到序列的元素上,累计之前的结果,把一系列值归约成一个值。

Python句法限制了lambda函数的定义体只能使用纯表达式,不能赋值,也不能使用while、try等python语句。在高阶函数的参数列表中最适合使用lambda表达式。

callable函数判断对象是否可调用。函数注解,注解只是元数据,可以供IDE、框架和装饰器等工具使用。

6、装饰器

装饰器能把函数替换成其它函数。装饰器在被装饰的函数定义之后立即运行。

Python内置了3个用于装饰方法的函数:property、classmethod和staticmethod。

另一个常见的装饰器是functools.wraps,使用functools.lru_cache做备忘,@functools.lru_cache(),singledispatch为单分派函数。

参数化装饰器通常会把被装饰的函数替换掉,而且在结构上需要多一层嵌套。

7、每个对象都有标识、类型和值,标识可以理解为在内存中的地址,是唯一的,在对象的生命周期中绝不会变。==比较对象的值(调用__eq__),is比较标识。

copy做浅复制,deepcopy做深复制。

使用None作为接收可变值的参数的默认值,不然的话函数会为传进来的可变参数创建别名,直接操作此可变参数。

del不删除对象,只是删除对象的引用。弱引用不会增加对象的引用数量,weakref.ref。WeakValueDictionary、WeakSet。int、list、tuple、dict实例不能作为弱引用的目标,list子类实例可以,set实例可以。

对元组t来说,t[:]返回同一个对象的引用,使用另一个元组构建元组实际上是同一个元组。

8、classmethod定义了操作类而不是操作实例的方法,方法的第一个参数是类本身。staticmethod方法就是普通的函数。

私有属性的名称会被改写。使用__slot__属性的目的是告诉解释器:类的所有实例属性都在这儿,使用类似元组的结构存储实例变量,而不是__dict__,从而节省大量内存。使用__slot__要注意几点:每个子类都要定义__slot__属性,解释器会忽略继承的__slot__属性,实例只能拥有__slot__中列出的属性,如果不把__weakref__加入__slot__,实例就不能作为弱引用的目标。

Python有个很独特的特性:类属性可用于为实例属性提供默认值。

9、在面向对象编程中,协议是非正式的接口,只在文档中定义,在代码中不定义。如Python的序列协议只需要__len__和__getitem__两个方法。

random.shuffle就地打乱序列(序列需实现__setitem__).

标准库中的抽象基类:collections.abc模块中定义了16个抽象基类,

各个集合应该继承Iterable、Container和Sized这3个抽象基类,Iterable通过__iter__方法实现迭代,Container通过__contains__方法实现支持in运算符,Sized通过__len__方法支持len函数。

10、内置类型不会调用子类覆盖的方法,直接子类化内置类型容易出错。

11、对象的可迭代检查:__iter__——>__getitem__

迭代器是这样的对象:实现了无参数的__next__方法,返回序列的下一个元素,如果没有元素了,则抛出StopIteration异常。Python中的迭代器还实现了__iter__方法,因此迭代器也可以迭代。

可迭代的对象要实现__iter__方法;迭代器要实现__iter__和__next__方法。

可迭代的归约函数:all、any、max、min、reduce、sum。。

与__next__方法一样,.send()方法也使生成器前进到下一个yield语句,.send()方法还允许生成器的客户把数据发给自己,.send方法允许客户代码和生成器之间双向交换数据,__next__方法只允许客户从生成器获取数。

12、with语句的目的是简化try/finally模式。上下文管理器协议包含__enter__和__exit__两个方法,with语句开始运行时会在航下文管理器对象上调用__enter__方法,运行结束后会在上下文管理器对象上调用__exit__方法。

13、从句法上看,协程与生成器类似,都是定义体中包含yield关键字的函数,在协程中,yield出现在表达式的右边,可以产出值,也可以不产出值。

生成器调用方可以使用send方法发送数据,发送的数据会成为生成器函数中yield表达式的值。

协程可以身处4个状态中的一个,当前状态可以用inspect.getgeneratorstate(...)确定:GEN_CREATED  GEN_RUNNING GEN_SUSPENDED GEN_CLOSED ,仅当协议处于暂停状态时才可以使用send。最先调用next函数这一步通常称为预激,让协程向前执行到第一个yield表达式。可以使用装饰器完成预激功能。

客户代码可以在生成器对象上调用两个方法显示地把异常发送给协程:throw和close。

yield from x表达式对x对象所做的第一件事就是调用iter(x),从中获取迭代器。yield from的主要作用是打开双向通道,把最外层的调用方与最内层的子生成器连接起来:

14、标准库中所有执行阻塞型IO操作的函数,在等待操作系统返回结果时都会释放GIL,这意味着Python在这个语言层次上可以使用多线程。尽管有GIL,Python线程还是能在I/O密集型应用中发挥作用。

concurrent.futures模块实现的是真正的并行计算,因为它使用ProcessPollExecutor把工作分配给多个进程处理,ProcessPollExecutor和ThreadPollExecutor都实现了通用的Executor接口,因此使用concurrent.futures模块能轻松地把基于线程的方案转换为基于进程的方案。

对CPU密集型工作来说,要启动多个进程,规避GIL,创建多个进程最简单的方式是使用futures.ProcessPollExecutor类,如果场景复杂,使用multiprocessing模块。

15、asyncio,aiohttp,回调地狱

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值