Effective Python

Python提高班

Posted by 淼淼淼 on August 24, 2018

Python提高早知道


用列表推导来取代map和filter

   even_squares = [x**2 for x in a if x % 2 == 0]
   randk_dict = {rank: name for name,rank in chile_ranks.items()}
   alt = map(lambda x: x**2, filter(lambda x: x%2 ==0,a))

用生成器表达式来改写数据量较大的列表推导

  • 把实现列表推导所所用的那种写法放在一对圆括号中,就构成了生成器表达式.
it = (len(x) for x in open(/tmp/my_file.txt))
print(next(it))
  • 逐次调用内置的next函数;
roots = ((x,x**0.5) for x in it)
print(next(roots))
  • 当输入的数据量较大时,列表推导可能会因为占用太多内存而出问题.
  • 由生成器表达式所返回的迭代器,可以逐次产生输出值,从而避免了内存用量的问题.
  • 把某个生成器表达式所返回的迭代器,放在另一个生成器表达式的for子表达式中,即可将二者组合起来
  • 串在一起的生成器表达式执行速度很快.

尽量用enumerate取代range

for I , flavor in enumerate(flavor_list):
    print('%d: %s'%(i+1,flavor))
  • enumerate函数提供了一种精简的写法,可以在遍历迭代器时获知每个元素的索引.
  • 尽量用enumerate来改写那种将range与下标访问相结合的序列遍历编码;
  • 可以为enumerate提供第二个参数,以指定开始计数时所用的值(默认为0)

用zip函数同时遍历两个迭代器

for name, count in zip(names, letters):
    if conunt > max_letters:
    longest_name = name
    max_letters = count

如果待遍历的迭代器长度不同,那么zip会提前终止,这将会导致意的结果.若不能确定zip所封装的列表是否等长,则可考虑改用itertools内置模块中的zip_longest函数(python2中为izip_loongest)

  • 内置的zip函数可以平行地遍历多个迭代器.
  • python3中的zip相当于生成器,会在遍历过程中逐次产生元组,而python2中的zip则是直接把这些元组完全生成好,并一次性地返回整份列表.
  • 如果提供的迭代器长度不等,那么zip就会自动提前终止;
  • itertools内置模块中的zip_longest函数可以平行地遍历多个迭代器,而不用在乎它们的长度是否相等;

尽量用异常来表示特殊情况,而不要返回None

考虑用生成器来改写直接返回列表的函数

生成器是使用yield表达式的函数.调用生成器函数时,它并不会真的运行,而是会返回迭代器.每次在这个迭代器上面内置的next函数时,迭代器会生成器推进到下一个yield表达式哪里.生成器穿黑yiled的每一个值,都会由迭代器返回给调用者.

  • 使用生成器比把收集到的结果放入列表里返回给调用者更加清晰;
  • 由生成器函数所返回的那个迭代器,可以把生成器函数体中,传给yield表达式的那些值,逐次产生出来.
  • 无论输入量有多大,生成器都能产生一系列输出,因为这些输入量和输出量,都不会影响它在执行时所耗的内存.
  • 解释器是读取并运行Python代码的计算机程序

子进程的运用

  • 可以用subprocess模块运行子进程,病管理其输入流与输出流
  • python解释器能够平行地运行多条子进程,这使得开发者可以充分运用其处理能力
  • 可以给commnunicate传入timeout参数,以避免子进程死频或失去相应;(hanging,挂起

可以用线程来执行阻塞式I/O,但不要用它做平行计算

  • 标准的python实现叫做CPython
  • GIL(global interpreter lock 全局解释器锁),GIL实际上就是一把互斥锁(mutual-exclusion lock,又称为mutex,互斥体)。所谓占先式多线程切换,是指某个线程可以通过打断另外一个线程的方式,来获取程序控制权
  • Python解释器会给每个线程分配大致相等的处理器时间。而为了达成这样的分配策略,Python系统能够可能当某个线程正在执行时候,将其暂停,然后使另外一个线程继续往下执行;
  • 线程A执行到一半的时候,线程B插了进来,等线程B执行完整个递增操作之后,线程A又继续执行,于是线程A就把线程B刚才对计数器所做的递增效果,完全抹去了。传感器采样程序所统计到的样本总数之所有会出错。
  • 最简单,最用用的工具,就是Lock类,该类相当于互斥锁(也叫做互斥体)
  • Python确实存在全局解释器锁,但是在编写自己的程序时,依然要设法防止多个线程征用同一份数据。
  • 如果不在加锁的前提下,允许多条线程修改同一个兑现,那么程序的数据结构可能会遭到破坏。
  • 在python内置的threading的模块中,有名叫Lock的类,它用标准的方式实现了互斥锁

用Queue来协调各线程之间的工作

  • Queue类使用工作线程无需再频繁地查询输入队列的状态,因为它的get方法会持续阻塞,知道新的数据加入。
  • 线管是一种优秀的任务处理方式,它可以把处理流程划分为若干阶段,并使用多条python线程来同时执行这些任务
  • 构建并发式的线管时,要注意许多问题。其中包括:如何防止某个阶段陷入持续等待的状态之中、如何停止工作线程,以及如何防止内存膨胀等
  • Queue类所提供的机制,可以彻底解决上述问题,它具备阻塞式的队列操作能够制定缓存区尺寸,而且还支持join方法。

考虑用协程来并发地运行多个函数

  • 协程工作原理:每当生成器函数执行到yield表达式的时候,消耗生成器的那段代码,就通过send方法给生成器回传一个值。而生成器在收到了经由send函数传进来的这个值后,会将其视为yield表达式的执行结果。
  • 协程提供一种有效的方式,令程序看上去好像能够同时运行大量函数
  • 对于生成器内的yield表达式来说,外部代码通过send方法传给生成器的那个值,就是该表达式所需要具备的值
  • 协程是一种强大的工具,它可以把程序的核心逻辑,与程序同外部环境交互时所用的代码相分离

TypeError: unhashable type: ‘list’

s = set([1,2,3,4,4,5]) 在使用list创建set的时候,一直出现TypeError: unhashable type: ‘list’,这种错误,检查代码,并没有出错,只可能是有些未知的错误,之前可能没有遇到过,百度后发现,原来是hash错误,就是list中的值不能hash。但是,set的创建也需要使用list,只能够在声明的时候直接使用list创建可以。

python对list去重的各种方法

1.直观方法

ids = [1,2,3,3,4,2,3,4,5,6,1]
news_ids = []
for id in ids:
    if id not in news_ids:
        news_ids.append(id)
print news_ids

2.用set

ids = [1,4,3,3,4,2,3,4,5,6,1]
ids = list(set(ids))

3.按照索引再次排序

ids = [1,4,3,3,4,2,3,4,5,6,1]
news_ids = list(set(ids))
news_ids.sort(key=ids.index)

4.使用itertools.grouby

ids = [1,4,3,3,4,2,3,4,5,6,1]
ids.sort()
it = itertools.groupby(ids)
for k, g in it:
    print k

5.用reduce

ids = [1,4,3,3,4,2,3,4,5,6,1]
func = lambda x,y:x if y in x else x + [y]
reduce(func, [[], ] + ids)

bye