Effective Python

这本书看什么?

能收获什么,作者的python经验,语言语法经验,语言实用技巧。还有作者想给我们推荐的一种编码思想,就是pythonic,这个东西是什么呢?我目前的理解就是用python的语法糖去简化代码逻辑,最终达到简单不简陋。

整理自己觉得有用的技巧,但是不会按照他的点来排序。

ASCII码

基于拉丁字母的一套电脑编码系统,主要用于显示现代英语和其他西欧语言

Unicode字符

一种世界通用的编码方式,存任意字符,包括中文,拉丁字母。转为二进制的话必须用encode,解码decode。

Utf-8

UTF-8是针对Unicode的一种可变长度字符编码;它可以用来表示Unicode标准中的任何字符,而且其编码中的第一个字节仍与ASCII相容,使得原来处理ASCII字符的软件无须或只进行少部份修改后,便可继续使用。

怎么理解呢?

ASCII 是简单的解码拉丁字母,但是随着开发应用,中文或者其他语言的加入,这肯定不够,所以开发了UNicode编码来使用,这个编码是全世界的语言都包含了,但是字节数比较多,所以如果你全部用unicode 的话,那么可能容量会变大。所以utf-8是一个变长的编码,如果是拉丁字母就用ascii的长度去解,如果是中文,就用unicode的编码解。这样的话,尽量是减少了内存的冗余。

1.Pythonic的思考方式

崇尚直观,简洁又易读,简单不简陋。

python 的版本注意

注意2.0跟3.0的一些语法区别,如果你的代码里面有两个语法规则,很容易出错。
案例:做过的项目里面,旧的包体还是用2.5的版本,但是最新的是2.7的版本,所以如果程序开发的时候用了2.7的语法那2.5的包体就不行了。所以尽量还是统一你的版本。

首先我们写的语法应该遵照pep8,pylint语法规则

这里我的理解是一个项目组应该有尽量统一的编码规范,除了规范正确的语法,也方便沟通和交流。

多辅助函数来取代复杂的表达式,应该多拆分函数处理

1.字典获取的值的时候,使用内置get函数加上默认参数简化if
案例:
num = dicNum.get("Num", 0) 如果dicNum里面没有"Num" 那么就会默认返回0,如果不填0 的话,返回的是None,推荐还是填写。

2.不要过度使用语法糖去简化表达式
比如利用空字符串 空列表 0都会判断为False, 因此可以简化if条件
案例:
isShow = lstTemp or false
当lstTemp是空列表那么就会返回or 右边的值
但是这样阅读性其实比较差,执行需要条件。
所以加入了这样的写法来替代:
isShow = lstTemp if lstTemp else false
我目前认为简单的语句可以使用,即if和esle里面的条件比较单一可以这样做。
书里推荐的方式是,新写一个函数去替代重复的if else,而不是过度的简化每一句if else。

序列技巧

sName[3:len*(sName)] ==> sName[3:0]
sName[0:3] ==> sName[:3]
用负数表示倒着数的位置,比如-1就是倒过来数的下标1 即倒数第二个
sName[3:-1]

序列在切割时候可以越界切割(即你的序列长度只有10,但是你可以[:20]也能返回正确的答案),不会出现异常,但是越界访问还是会报错的。
切割之后会产生新的序列(因为序列是值引用,比如字符串)

sName[:] 拷贝一份

序列切片可以使用步进的方式

[::n] 从每n个元素里面取1个出来

翻转字符串
[::-1] 对于字节串ASCII串有效果,不过对于Utf-8编码的不行
w = "谢谢"
x = w.encode("utf-8")
y = x[::-1]
z = y.decode("utf-8") // 会报错,已经错乱了

用列表制作另一份列表的技巧 ,列表推导式

使用列表推导式

slst = [x**2 for x in lstA if x % 2 == 0]

建议:不要使用两个推导式组合,不好理解,阅读性太差,而且很长

使用迭代器来改写数据量大的列表推导式,而且可以进行组合:

it = (len(x)  for x in open("temp/myfile.txt"))// it是一个迭代器,很类似一个列表指针,但是单向一次性的,只能一个个往下遍历,不能后退、跳
print(next(it))
print(next(it))

组合:
roots = ((x,x**0.5) for x in it) // it ,roots都是迭代器,这样组合起来也看起来简单一些了,不会很长。

用enumerate 替代range enumerate(lstname,1)

为什么?
案例:

for i in range(lstTemp):
	print(i, lstTemp[i])
	
==>
for i, item in enumerate(lstTemp)
	print( i,item)

更加简洁而且容易理解,你是想要索引下标,而且2.7以上可以指定下标开始的数字enumrate(lsttemp, 1)

zip 函数同时遍历两个迭代器 for name, num in zip(lstname, lstnum)

案例:

lstnames = ["lisa", "xiaoming", "xiaohong"]
lstnums = [len(name) for name in lstnames]
for idx, name in enumerate(lstnums):
	dName[name] = lstnums[idx]

你获取了名字和长度的字典数据,上面的做法实际上是遍历了两个列表, 你要选择一个进行遍历。可读性不是最优的

==>
for name, num in zip(lstnames, lstnums):
	dName[name] = num

是不是相当于两个列表是同等地位的,也很容易看出来
但是如果俩个列表不等长,会终端循环,所以推荐是一定是两个列表同等长度的时候使用

不要在for,while循环外边使用else

这个我不是很认同,我觉得是可以使用的,作者觉得可读性太差, 新人很容易误解,但是文中提到一个解决方案是写辅助函数帮忙处理可能更好。

try/except/else/finally

try 写可能有异常的代码,else 没有异常执行的代码(减少try的代码量,逻辑的拆分),finally 有异常返回异常并且处理一些事情(关闭文件打开等问题)

2.函数

函数位置参数

1.采用可变参数*args
2.指定参数 ishow = 1
3.函数默认参数不要用{},[] 因为在编译的过程中就已经执行了默认参数的执行,都是引用对项目,很容易被改变

3.类于继承

推荐辅助类的使用, 函数嵌套函数尽量避免,使用类替代

@classmethod 类函数用于多态,多个类一样的操作,可以使用类方法去多态

因为:

@classmethod
def generate_inputs(cls, config):
	...返回类相关cls 类似__init__的构造器,返回类的对象

使用:
1.作为构造函数的补充
2.作为普通函数的补充,不需要实例就能执行的函数
3.可以作为工厂方法的结构
4.在创建实例前做一些处理

mix-in 是一种小型的组件类,它只是定义了其他类可能需要提供的一套附加方法,而不定义自己的实例属性。通过继承实现。

Mixin 只用于拓展子类的功能,不能影响子类的主要功能,子类也不能依赖 Mixin
像是扩展方法的一种,不改变原来的类,而给类增加新的功能,可以通过继承处理。
一个mixin一般只有一类功能,方便组合,可以继承多个mixin类实现多个功能。
可以结合定制函数的方式,getitem setitem 的方式去简化
跟基类功能的区别是什么?有无主需求一样实现

super 作为多重继承,父类的区分,supre(父类,self).add

  class FooChild(FooParent):
      def __init__(self):
          //super(FooChild,self) 首先找到 FooChild 的父类(就是类 FooParent),然后把类 FooChild 的对象转换为类 FooParent 的对象
          super(FooChild,self).__init__()  
  单继承的时候项目用的更多是 FooParent.__init__() 跟super没有区别的,但是多继承的时候,
  使用了super一个节点只会调用一次,没有使用,基类的节点多一个子类就会多执行一次,效率很差

私有变量的方式

两个下划线的是私有__name,所以我们自己定义自己的私有形式,而不去使用python的私有变量命名方式。实际上是变换了私有属性的名字来,比如你命名为__Name, 事件上生成对象后会有一个__XXX__Name 的属性。就是说实际上存在,但是改了名字,你直接用不了而已。但是你可以根据名字找到那个变量,就一样可以用。为什么做成这样呢?因为py是一个开放的语言,不想太限制

定制函数

__getitem__ 可以跟列表一样lsttemp[index] 访问下标

def __getitem__(self, index):
	return self.__dict__.get(key) // 通过__dict__获取对象的属性
	
__len__ 实现接受len()

def __len__(self):
	reurn xx

__repr__  用于print()的参数

def __repr__(self):
	return 

容器类型可以继承 collections.abc import Sequence 抽象基类,提醒你实现上面的一些容器相关的接口。这样做比较统一安全

class BetterNode(Sequence)
	__len

但是如果你的容器比较简单那么建议直接继承字典或者列表,dict,list

4.元类及属性

不需要用get set属性

直接使用xx.xxx 更符合python的风格
如果需要,@Property

6.内置工具

双向队列 deque
有序字典 OrderDict
默认字典 defaultdict
堆队列 heappush(A,5)
二分插值 bisect_left(lst, idx)
decimal 精确小数
pip工具

原文地址:https://www.cnblogs.com/leilei-weapon/p/14381422.html