effective python-Effective python

第一章:用pythonic的方式思考

1:确认使用的python版本

python --version

python3 --version

2:遵循PEP8风格指南

《Python Enhancement Proposal #8》又叫PEP8,它是针对Python代码风格而编订的风格指南。链接

采用一致的代码风格来书写可以令代码更加易懂、更加易读;

3:了解bytes、str与Unicode的区别

python2有两种表示字符的类型:str、Unicode;str包含原始的8位值,Unicode包含Unicode字符。

python3有两种表示字符的类型:bytes、str;bytes包含原始的8位值,str包含Unicode字符。

在编写程序时,一定要把编码、解码的操作放在界面最外围来做,程序的核心应该使用Unicode字符类型。

注意:

python2中,如果str只包含7位ASCII字符,此时Unicode和str似乎就成了同一种类型,可以使用+、等价、不等价来判断str和Unicode,也可以使用%s来格式化Unicode。

python3中,内置open函数默认会以utf-8编码格式来操作文件,python2中默认是二进制格式。

4:用辅助函数来取代复杂的表达式

总结下来就是:

开发者很容易过度运用python的语法特性,从而写出那种特别复杂又难以理解的单行表达式。

请把复杂的表达式移到辅助函数中,如果要反复使用相同的逻辑,那更应该这么做。

使用if/else表达式,要比用or、and这样的bool操作符写成的表达式更加清晰。

5:了解切割序列的办法

不要写多余的代码:当start索引为0,或者end索引为序列长度时,应该将其省略。

切片操作不会计较start与end索引是否越界,这使得我们很容易就能从序列的前端或者后端开始,对其进行范围固定的切片操作。

对list赋值的时候,如果使用切片操作,就会把原列表中处在相关范围内的值替换成新值,即便它们的长度不同也依然可以替换。

6:在单次切片操作内,不要同时指定start、end和stride

既有start和end,又有stride的切割操作,可能会令人费解。

尽量使用stride为正数,且不带start或end索引的切割操作。尽量避免使用负数做stride。

在同一个切片操作内,不要同时使用start、end和stride。

7:用列表推导来取代map和filter

列表推导要比内置的map和filter函数清晰,因为它无需额外编写lambda表达式。

列表推导可以跳过输入列表中的某些元素,如果改用map来做,那就必须辅以filter方能实现。

字典和集合也支持推导表达式。

8:不要使用包含两个以上表达式的列表推导

超过两个表达式的列表推导是很难理解的,应该尽量避免。

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

当输入的数据量较大时,列表推导可能会因为占用太多内存而出问题。

由生成器表达式返回的的迭代器,可以逐次产生输出值,从而避免了内存占用高的问题。

10:尽量用enumerate取代range

enumerate函数提供了一种简洁的写法,可以在遍历迭代器时获知每个元素的索引。

尽量用enumerate来改写那种将range与下标访问相结合的序列遍历代码。

可以给enumerate提供第二个参数,以指定开始计数时所用的值(默认为0)。

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

12:不要在for和while循环后面写else块

只有当整个循环主体都没有遇到break语句时,循环后面的else块才会执行。

不要在循环后面使用else块,因为这种写法既不直观,又容易引人误解。

13:合理利用try/except/else/finally结构中的每个代码块

无论try块是否发生异常,都可利用try/finally复合语句中的finally块来执行清理工作。

else块可以用来缩减try块中的代码量,并把没有发生异常时所要执行的语句与try/except代码块隔开。

顺利执行try块后,若想使某些操作能在finally块的清理代码之前执行,则可以将这些操作写到else块中。

第二章:函数

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

用None这个返回值来表示特殊意义的函数,很容易使调用者犯错,因为None和0以及空字符串之类的值,在条件表达式中都会评估为False。

函数在遇到特殊情况时应该抛出异常,而不是返回None。

15:了解如何在闭包中使用外围作用域中的变量

去了解global和nonlocal的用法(nonlocal是python3的语法)

对于定义在某作用域内的闭包来说,它可以引用这些作用域中的变量,但是不能修改不可变变量。

使用默认方式对闭包内的变量赋值不会影响外围作用域中的变量值。

python3可以使用nonlocal修饰某个名称,使该闭包能够修改外围作用域中的同名变量。

python2可以使用可变值来实现nonlocal相仿的机制(放入列表中)。

16:考虑用生成器来改写直接返回表聊得函数。

当数据量大时,使用迭代器替换直接返回列表可以避免消耗太大的内存。

17:在参数上面迭代时,要多加小心

函数在参数上多次迭代要小心,如果参数是迭代器,那么可能会导致奇怪的行为并错失某些值。

python的迭代器协议,描述了容器和迭代器应该如何与iter和next内置函数、for循环及相关表达式配合。

把__iter__实现为生成器,即可定义自己的容器。

判断某个值是不是迭代器,可以两次调用iter方法,如果两次值相同,就是迭代器,可以调用next方法使迭代器前进一步。

for x in foo会先调用iter(foo)返回一个迭代器对象,iter会调用foo.__iter__方法。

18:用数量可变的位置参数减少视觉杂讯

def定义函数时使用*arg,可令函数接受数量可变的位置参数。

19:用关键字参数表达可选的行为

函数参数可以按位置或关键字来指定。

只使用位置参数可能会使函数调用不明晰。

给函数添加新行为时,可以使用带默认值的关键字参数,以保持代码兼容。

可选参数应该总是以关键字参数形式出现,而不是可变位置参数。

20:用None和文档字符串来描述具有动态默认值的参数

参数的默认值只会在加载模块时计算一次,对于{},[]等动态的值,可能会出现奇怪的问题。

应该把默认值设置为None,在函数中判断为None的时候再设置为需要的值。

21:用只能以关键字形式指定的参数来确保代码明晰

关键字参数能够使函数调用的意图更加明确。

第三章:类与继承

22:尽量用辅助类来维护程序的状态,不要用元组或者字典

不要使用包含其他字典的字段,也不要使用过长的元组。

如果容器包含简单不可变的数据,可以考虑使用nametuple,但是nametuple也有自己的局限。

保存内部状态的字典如果变得比较复杂,就应该把这些代码拆解为多个辅助类。

23:简单的接口应该接收函数,而不是类的实例

python中的函数和对象一样,都是都可以当做参数传递给函数的。

通过名为__call__的特殊方法,可以使类的实例能够像普通的python函数那样得到调用。

如果要用函数保存状态,那就定义新的类,并实现__call__方法,而不要定义带状态的闭包。

24:以@classmethod形式的多态去通用的构建对象

在python中,每个类只能有一个构造器,也就是__init__方法。

通过@classmethod机制,可以用一种与构造器相仿的方式来构造类的对象。

通过类方法多态机制,能够以更加通用的方式构建并拼接具体的子类。

25:用super初始化父类

python采用标准的方法解析顺序来解决超类初始化次序及钻石继承问题,可以用mro方法来查看这个顺序,调用顺序和这个顺序相反。

总是应该使用内置的super函数来初始化父类。

26:只在使用Mix-in组件制作工具时进行多重继承

mix-in是一种小型的类,它只定义了其他类可能需要提供的一套附加方法,而不定义自己的实例属性,也不要求使用者调用自己的__init__方法。

能用mix-in组件实现的效果,就不要用多重继承来做。

将各个功能实现为可插拔的mix-in组件,使需要该功能的类继承mix-in组件即可具备该行为。

27:多用public属性,少用private属性

python编译器无法严格保证private字段的私密性。

不要盲目的将属性设置为private。

应该多用protected,即以一个下划线为前缀的属性名。

28:继承collections.abc以实现自定义的容器类型

collections.abc模块定义了一系列抽象基类,它们提供了每一种容器类型所应该具备的常用方法。从这样的基类中继承之类之后,如果忘记实现某个方法,那么collections.abc模块就会指出这个错误。

编写自定义的容器类型时,可以从collections.abc模块的抽象基类中继承,那些基类能够确保我们的之类拥有适当的接口和行为。

第四章:元类及属性

29:用纯属性取代get和set方法

编写新类时,应该用最简单的public属性来定义其接口。

如果要控制某个属性的访问时,应该是用@property。

@property修饰的方法应该尽量简单。

30:考虑用@property来代替属性重构

@property可以为现有的实例属性添加新的功能

可以用@property来逐步完善数据模型

31:用描述符来改写需要复用的@property方法

如果想复用@property方法及其验证机制,可以自己定义描述符类。

WeakKeyDictionary可以保证描述符类不会内存泄露。

32:用__getattr__和__get_attribute__和__setattr__实现按需生成的属性。

如果某个类定义了__getattr__,同时系统在该类对象的实例字典中又找不到待查询的属性,系统就会调用该方法。

通过__setattr__和__getattr__,我们可以用惰性的方式来加载并保存对象的属性。

要理解__getattr__和__getattribute__的区别:前者只会在属性不存在时才会被调用,而后者则会在每次访问属性时触发。

如果要在__getattribute__和__setattr__方法中访问实例属性,应该直接通过super()来做,避免无限递归。

33:用元类来验证子类

元类最简单的一种用途,就是验证某个类定义的是否正确。构建复杂的类体系时,我们可能需要确保类的风格协调一致、确保某些方法得到了覆写,或是确保类属性之间具备某些严格的关系。

通过元类,我们可以在生成子类对象之前,先验证子类的定义是否合乎规范。

34:用元类来注册子类

在构建模块化的python程序时,类的注册是一种很有用的模式。

通过元类来实现类的注册,可以确保所有的子类都不会被遗漏。

35:用元类来注解类的属性

借助元类,我们可以在某个类完全定义好之前,率先修改该类的属性。

第五章:并发及并行

36:用subprocess模块管理子进程

可以用subprocess模块运行子进程,并管理其输入流与输出流。

37:可以用线程来执行阻塞式io,但不要用它做平行计算

python的多线程适合io密集型应用。

38:在线程中使用lock来防止数据竞争

python虽然存在GIL,但是多线程依然需要加锁保护竞争资源。

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

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

虽然可以用开发多个线程来实现并发,但是线程有以下缺点:

为了确保数据安全,必须要使用同步工具。

线程需要占用大量内存,大约8M。

线程启动时的开销比较大。

协程提供了一种有效的方式,令程序看上去好像能够同时运行大量函数。

41:考虑用concurrent.futures来实现真正的平行计算

利用ProcessPoolExecutor可以突破GIL的限制,利用多核的优势,但是这种方案的限制是仅适用于需要在进程间传递少量数据的场景。

第六章:内置模块

42:用functools.wraps定义函数装饰器

python为装饰器提供了专门的语法,它使得程序在运行的时候,能够用一个函数来修改另一个函数。

内置的functools模块提供了名为wraps的装饰器,开发者在定义自己的装饰器时应该用wraps对其做一些处理,避免一些问题。

43:考虑以contextlib和with语句来改写可复用的try/finally代码

内置模块contextlib提供了contextmanager的装饰器,方便开发者用来实现自己的上下文管理。

44:用copyreg实现可靠的pickle操作

内置的pickle模块,只适合用来在彼此信任的程序之间,对相关对象执行序列化和反序列化。

把内置的copyreg和pickle结合起来使用,以便给旧数据添加缺失的属性值、进行类的版本管理。

45:应该用datetime模块来处理本地时间,而不是用time

time模块的实现依赖操作系统而运行,应该仅仅用来在UTC(时间戳)和当地时区时间之间转换。

不要用time模块在不同时区之间进行转换。

如果要在不同时区之间,可靠地执行转换操作,那就应该使用datetime和pytz。

开发者应该总是把时间转换为UTC格式,然后做各种操作,最终转换为当地时间。

46:使用内置算法与数据结构

双向队列:deque

有序字典:OrderedDict()

带有默认值的字典:defaultdict()

堆队列:heap

与迭代有关的工具:itertools

47:在重视精确度的场合,应该使用decimal

48:学会安装有python开发者社区所构建的模块

第七章:协作开发

49:为每个函数、类和模块编写文档字符串

50:用包来安排模块,并提供稳固的API

51:为自编的模块定义根异常,以便调用者与API相隔离

52:用适当的方式打破循环依赖关系

53:用虚拟环境隔离项目,并重建其依赖关系

第八章:部署

54:考虑用模块级别的代码来配置不同的部署环境

55:通过repr字符串来输出调试信息

repr可以展示要输出的对象的类型。

56:用unittest来测试全部代码

57:考虑用pdb实现交互调试

import pdb

pdb.settrace()

58:先分析性能再优化

Profile和CProfile工具可以用来分析性能。

59:用tracemalloc来掌握内存的使用及泄露情况

python内置的gc

python3.4支持的tracemalloc

python2可以使用开源包:heapy

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CMA-ES(Covariance Matrix Adaptation Evolution Strategy)是一种基于进化算法的优化方法,用于寻找非线性、高维、非凸函数的最优解。下面是Python实现CMA-ES的示例代码: ```python import numpy as np from scipy.stats import multivariate_normal class CMAES: def __init__(self, dim, mu, sigma): self.dim = dim self.mu = mu self.sigma = sigma self.lambda_ = int(4 + np.floor(3 * np.log(dim))) # population size self.weights = np.log(self.lambda_ + 1 / 2) - np.log(np.arange(1, self.lambda_ + 1)) self.weights /= np.sum(self.weights) self.mu_eff = np.sum(self.weights) ** 2 / np.sum(self.weights ** 2) # variance-effective size of mu self.c_sigma = (self.mu_eff + 2) / (dim + self.mu_eff + 5) # learning rate for sigma control self.d_sigma = 1 + 2 * max(0, np.sqrt((self.mu_eff - 1) / (dim + 1)) - 1) + self.c_sigma # damping parameter for sigma control self.pc = np.zeros(dim) # evolution path for C self.ps = np.zeros(dim) # evolution path for sigma self.B = np.eye(dim) # transformation matrix self.D = np.ones(dim) # diagonal matrix self.C = np.eye(dim) # covariance matrix self.sigma_hist = [] def ask(self): self.z = np.random.randn(self.lambda_, self.dim) self.y = np.dot(self.z, self.B * np.diag(self.D)) * self.sigma + self.mu return self.y def tell(self, x, fit): idx = np.argsort(fit) z = np.dot(self.B.T, self.z[idx, :].T).T / np.sqrt(self.D) self.pc = (1 - self.c_sigma) * self.pc + np.sqrt(self.c_sigma * (2 - self.c_sigma) * self.mu_eff) * np.sum(self.weights[:, None] * z[:self.mu, :], axis=0) self.ps = (1 - self.c_sigma) * self.ps + np.sqrt(self.c_sigma * (2 - self.c_sigma)) * np.dot(self.B, np.dot(np.diag(1 / np.sqrt(self.D)), np.mean(z[:self.mu, :], axis=0))) hsig = np.linalg.norm(self.ps) / np.sqrt(1 - (1 - self.c_sigma) ** (2 * self.iteration)) / self.d_sigma < 1.4 + 2 / (self.dim + 1) self.sigma *= np.exp((self.c_sigma / self.d_sigma) * (np.linalg.norm(self.ps) / self.sigma - 1)) if hsig: self.C = (1 - self.c_sigma) * self.C + self.c_sigma * np.dot(self.pc[:, None], self.pc[None, :]) + self.c_sigma * (1 - np.sum(self.weights ** 2)) * self.C else: self.C = (1 - self.c_sigma) * self.C + self.c_sigma * np.dot(self.pc[:, None], self.pc[None, :]) self.D, self.B = np.linalg.eigh(self.C) self.D = np.sqrt(self.D) self.sigma_hist.append(self.sigma) self.iteration += 1 def result(self): return self.mu, self.sigma, self.sigma_hist ``` 使用示例: ```python def sphere(x): return np.sum(x ** 2) es = CMAES(dim=10, mu=5, sigma=1) for i in range(100): x = es.ask() fit = np.array([sphere(x[i]) for i in range(len(x))]) es.tell(x, fit) print(es.result()) ``` 此示例演示了使用CMA-ES来优化10维球函数的最小值。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值