Effictive python学习总结连载(1)

python从读研开始就在用了,拿来做过web后台、安全分析、爬虫、测试框架等等,挺强大的。最近借放假和看书和整理的机会,系统的总结下。主要是2方面:一个是书或资料中学到的核心点,咀嚼过后自己来总结;一个是自己思考的东西。

这里看的书是《Effective Python-编写高质量Python代码的59个有效方法》,这本书还不错,作者应该写过不少python。网上有翻译版本,但不知道是否有版权,gitbook链接

接下来就按照书的章节划分,总结要点,并写写自己的一些思考。

一、Pythonic Thinking-用python的思维去编码

1.尽量用3.X高版本的python

python主要分为2个版本:2.x和3.x。这里比较坑的是,2和3之间不兼容,很多2.x版本的代码需要重写才能在3.x跑。当然会有一些自动转换工具和库使用,这里不阐述。

当前2.X最新版本为2.7.15,2.X版本的应用比较广泛,很多老的工具和库都是基于2.x版本写的,所以用到一些老旧的、未提供3.x版本支持的库时,只能选择2.x版本的python。这里要注意的是,2.x版本功能不再更新了,只修bug,核心团队表示在2020年会停止维护。这里有个倒计时的网站:https://pythonclock.org

3.x最新版本为3.7.0,除非特殊的原因,我们应选择3.x版本的python。不管是性能、安全性、各种新功能都只将在3.x版本中可用。而且基本上常用的库都已经迁移3了,这是大势所趋。

2.遵循PEP8代码风格规范

PEP 8提案

PEP8提案规定了规定了很多的编码规范,这个一定要看,并尽量遵守。之前看很多人写python命名函数等还是用java的驼峰写法,这并不Pythonic。风格一致更有利于维护。这个有很多相关翻译文章了,这里不阐述。

几个大家容易忽略的:

  • import尽量一行一个。先是内置模块,再是第三方模块,再是内部模块。
  • class用驼峰,函数、变量都用小写加下划线。
  • 用好ide的格式化代码功能,每次保存前格式化代码一下。

3.python2编码问题

几乎每个使用python2的人应该都遇到过编码问题,在读写网页、操作文件的时候,不小心就报编码不对的error。然后各种ASCII、Unicode、str、utf-8概念混在一起后,这里也比较使人混乱。这里总结下,把这个问题说明白。

3.1 为什么会存在字符编码这种东西?

大家都知道我们的所有信息在硬盘里其实都是01这样的数字,电脑怎么知道这些01数字代表哪个字符啊?这里的解决方案和计算机原理是一样的。就是人为规定一个表,规定好很多01组合,分别对应一个唯一的字符。

3.2 几种重要的编码

有3个主要的编码,需要搞明白,其实就是一个key-value的对应表。

  • ASCII

美国人最开始搞出计算机,制定了一套字符编码的标准叫ASCII(American Standard Code for Information Interchange)。它把大、小写26个字母、常用标点等都唯一对应一个数。例如大写字母A,对应01000001。标准ASCII码最高位没用,7位最多能表示128个字符。那个时候pc和网络都很菜鸡,这个表已经够用了。

  • GBK

随着计算机进入中国,中国字怎么编码呢?同样的,中国国家标准总局设计了一套编码叫GB2312,又称GB0,它基本覆盖了常见的中国字。后来,在此基础上覆盖了更多生僻字、少数民族字体等,称为GBK。

  • Unicode

由于这个世界上存在很多种文字,其它的文字又该怎么办呢?于是统一联盟国际组织提出了Unicode编码。它统一对各种文字进行支持,大家都能使用。Unicode有两种格式:UCS-2和UCS-4,分别用2个字节和4个字节。2的32次方这个量级,理论上可以把全世界的文字进行唯一标识了。

像Java、python3等语言,内部字符都是采用Unicode来编码的。

Unicode编码只是定义了一个表,某个01串代表某个字符,但是怎么在网络传输、在磁盘上保存没有硬性限制。由于网络带宽和磁盘是有限的,大家就开始进行了一些优化,希望用更少的字符来传输、保存unicode字符。这时候常见的UTF-8、UTF-16等登场了,我们以使用最多的UTF-8来举例。

UTF-8是一种可变长度的编码格式,使用1-4个字节来表示对应的Unicode字符。例如,对于UTF-8编码中的任意字符S,如果S的第一位为0,则S独立的表示一个字符(ASCII码)。这样就只需要1个字节,来对应原先的4字节Unicode字符。这样说就清晰了,UTF-8、UTF-32等等,都是Unicode的一种表示方式,如同指针一样,但是它们真正指向的还是Unicode中定义的字符。

3.3 python2中的坑

python在91年被设计出来,并没有考虑太多编码格式的问题,所以python默认的编码是ASCII。因此不在py文件的第一行声明utf-8编码,那么代码中含有中文等非ASCII字符时,就会产生语法错误提示存在非ASCII字符。

python中保存字符有2种类型:str和unicode。我们从下方可以看到,这里s_2直接赋值类型是str,s_3加上u再赋值就是unicode类型。再看内部表示,s_2是utf-8的编码(跟操作系统有关),而s_3是unicode4字节编码。

>>> s_2 = '你好'
>>> s_3 = u'你好'
>>> type(s_2)
<type 'str'>
>>> type(s_3)
<type 'unicode'>
>>> s_2
'\xe4\xbd\xa0\xe5\xa5\xbd'
>>> s_3
u'\u4f60\u597d'

这个时候大家是否回忆起这样的经历:在mac或者pycharm内运行的py脚本各种中文显示正常,但是在Windows的cmd中执行的时候却打印的是乱码呢?这就是因为cmd命令行中的字符编码是gbk的,而py中输出的却是utf-8或unicode编码字符。

python中从str转换为unicode使用解码decode函数,而从unicode转换为其它编码使用编码encode函数。

原则:我们在py内部使用的字符格式都要转换为unicode类型,而输出和读取外部结构时(数据库、文件、web等)全部统一使用某一种编码,可以是utf-8、gbk、utf-16等等,但切忌混合使用容易出错。

4. 使用辅助函数替换复杂表达式

意思就是如果逻辑复杂不要糅杂在一起,不清晰,可以将其抽象为函数调用来解决。

这里我觉得更有意义的是要有一个意识:写代码是给别人看的,顺带着实现功能。工作中也接触到一些同事的代码,通篇零星注释或者根本没有注释,要花好多时间去猜代码的意思,代码结构也冗杂。所以这里要重点注意的是可维护性、注释清晰,方便后期的维护升级。尽量少留坑给后人。

5. 切片操作的底层

切片不指定开始、结束,会浅复制一份,对复制后的操作不会影响之前的。但是要注意是不可变类型。如果是list等可变类型就不是了。

>>> a = [1,2,3,4]
>>> b = a[:]
>>> b
[1, 2, 3, 4]
>>> a
[1, 2, 3, 4]
>>> b[0] = 1000
>>> b
[1000, 2, 3, 4]
>>> a
[1, 2, 3, 4]

切片除了指定开始、结束,还可以设置选择的步长。3个不要同时指定。

>>> a = ['a', 'b','c','d','e','f']
>>> a[::2]
['a', 'c', 'e']

7. 使用列表推导

生成list、dict等的时候,可以采用列表推导的方式,这样更简洁、高效。

>>> a = ['a', 'b','c','d','e','f']
>>> b = [each for each in a if each not in ('a', 'c')]
>>> b
['b', 'd', 'e', 'f']

8. 列表推导中不适用超过2个表达式

这里的意思列表推导中超过2个for,就比较难看懂了,不推荐,然后使用2个传统的for循环进行替代。

注意:用下面代码测了简单测试了下性能,列表推导来生成列表,相对于传统的for循环加append,速度上列表推导快3-4倍。所以我觉得应该是尽量用列表推导的方式来生成,但格式和注释要跟上,抵消逻辑复杂的影响。

# coding: utf-8
import cProfile
import random

a = [[[random.randint(0, 100) for i in range(100)]] for i in range(100)]


def test():
    """
    使用列表推导来创建
    :return:
    """
    for m in range(100000):
        c = [temp for each_a in a for temp in each_a]
    return c


def test2():
    """
    使用for循环的方式来创建
    :return:
    """
    for m in range(100000):
        c = []
        for each in a:
            for temp in each:
                c.append(temp)
    return c


cProfile.run('test()')
cProfile.run('test2()')

test大概0.887秒,test2大概3.089秒。2.9 GHz Intel Core i5、8 GB 2133 MHz LPDDR3、macbook pro。

9. 用生成器表达式替换数据量较大的列表推导

列表推导会把数据全部加载到内存,当数据过大时会有问题。这时候要使用生成器表达式,这时并不会把整个序列计算出来,而是会返回一个iterator,每次使用next来拿下一个。

生成器表达式:使用列表推导的地方,外面变为括号即可。

10.使用enumerate来替换range

这个是常规用法,不多说。

11.zip的使用

2个数组的元素对应的拼接处理。常规用法。

12.不要在for和while后面写else

for和while的else,当有break跳出时不执行,否则执行。很绕,不熟悉的人可能会产生混淆。所以从可维护性等角度考虑,不要这样写。

13. 利用好try/except/else/finally

2个点注意下就好。

  • finally

不管怎么样都会执行的语句,常用于不管是否异常,需要关闭文件流等。

  • else

try语句块里没有发生异常,那么会执行else中的语句。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏AndroidTv

谈谈你对 Java 平台的理解声明提问正文

1124
来自专栏Java架构师学习

十年Java”老兵“浅谈源码的七大设计模式

一个专业的程序员,总是把代码的清晰性,兼容性,可移植性放在很重要的位置。他们总是通过定义大量的宏,来增强代码的清晰度和可读性,而又不增加编译后的代码长度和代码...

37512
来自专栏Golang语言社区

论golang是世界上最好的语言

概述 golang is a better C and a simple C++ golang主要特性 1、语法简单 舍弃语法糖,严格控制关键字 C++语法糖之...

4069
来自专栏编程

养良好C语言编程风格,编优质C语言代码,这才是C语言的开始

个人c语言编程风格总结 总结一下我个人的编程风格及这样做的原因吧,其实是为了给实验室写一个统一的C语言编程规范才写的。首先声明,我下面提到的编程规范,是自己给自...

4965
来自专栏進无尽的文章

设计模式| 行为型模式 (上)

行为型模式共十一种:策略模式、模板方法模式、观察者模式、迭代器模式、解释器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式。 分两篇文...

1622
来自专栏编程

Python教学——第六天

今天我们要说说dict,在第四天里我们说到了tuple,list也知道了list比tuple好用多了,至少能添加删除还能修改里面的值 在Python里,我们知道...

1997
来自专栏互扯程序

设计模式不止23种!

现在是资源共享的时代,同样也是知识分享的时代,如果你觉得本文能学到知识,请把知识与别人分享。

1254
来自专栏屈定‘s Blog

设计模式--组合模式的思考

组合模式是一种抽象树形结构的模式,其在业务开发中也是一种很有用的设计模式,下面开始分析.

3923
来自专栏java学习

Java每日一练(2017/7/6)

最新通知 ●回复"每日一练"获取以前的题目! ●【新】Ajax知识点视频更新了!(回复【学习视频】获取下载链接) ●答案公布时间:为每期发布题目的第二天 ★【新...

3379
来自专栏程序员互动联盟

【编程基础】写代码,你应该知道九类规则

网上有太多讲编码规范、编码习惯的文章,但我总是念的多,实际去认真阅读理解的少。或多或少的按照自己的思维去编写代码。这种习惯让我吃大亏,比如一个指针未赋值导致偶尔...

4105

扫码关注云+社区

领取腾讯云代金券