分享一下如何编写高效且优雅的 Python 代码

本文部分提炼自书籍:《Effective Python》&《Python3 Cookbook》,但也做出了修改,并加上了作者自己的理解和运用中的最佳实践。

全文约 9956 字,读完可能需要 24 分钟。

Pythonic列表切割

list[start:end:step]

如果从列表开头开始切割,那么忽略 start 位的 0,例如list[:4]

如果一直切到列表尾部,则忽略 end 位的 0,例如list[3:]

切割列表时,即便 start 或者 end 索引跨界也不会有问题

列表切片不会改变原列表。索引都留空时,会生成一份原列表的拷贝

列表推导式

使用列表推导式来取代mapfilter

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

数据多时,列表推导式可能会消耗大量内存,此时建议使用生成器表达式

迭代

需要获取 index 时使用enumerate

enumerate可以接受第二个参数,作为迭代时加在index上的数值

zip同时遍历两个迭代器

zip遍历时返回一个元组

关于forwhile循环后的else

循环正常结束之后会调用else内的代码

循环里通过break跳出循环,则不会执行else

要遍历的序列为空时,立即执行else

反向迭代

对于普通的序列(列表),我们可以通过内置的reversed()函数进行反向迭代:

除此以外,还可以通过实现类里的__reversed__方法,将类进行反向迭代:

try/except/else/finally

如果try内没有发生异常,则调用else内的代码

else会在finally之前运行

最终一定会执行finally,可以在其中进行清理工作

函数使用装饰器

装饰器用于在不改变原函数代码的情况下修改已存在的函数。常见场景是增加一句调试,或者为已有的函数增加log监控

举个栗子:

除此以外,还可以编写接收参数的装饰器,其实就是在原本的装饰器上的外层又嵌套了一个函数:

但是像上面那样使用装饰器的话有一个问题:

也就是说原函数已经被装饰器里的new_fun函数替代掉了。调用经过装饰的函数,相当于调用一个新函数。查看原函数的参数、注释、甚至函数名的时候,只能看到装饰器的相关信息。为了解决这个问题,我们可以使用

Python 自带的functools.wraps方法。

functools.wraps是个很 hack 的方法,它本事作为一个装饰器,做用在装饰器内部将要返回的函数上。也就是说,它是装饰器的装饰器,并且以原函数为参数,作用是保留原函数的各种信息,使得我们之后查看被装饰了的原函数的信息时,可以保持跟原函数一模一样。

此外,有时候我们的装饰器里可能会干不止一个事情,此时应该把事件作为额外的函数分离出去。但是又因为它可能仅仅和该装饰器有关,所以此时可以构造一个装饰器类。原理很简单,主要就是编写类里的__call__方法,使类能够像函数一样的调用。

使用生成器

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

用这种方法有几个小问题:

每次获取到符合条件的结果,都要调用append方法。但实际上我们的关注点根本不在这个方法,它只是我们达成目的的手段,实际上只需要index就好了

返回的result可以继续优化

数据都存在result里面,如果数据量很大的话,会比较占用内存

因此,使用生成器generator会更好。生成器是使用yield表达式的函数,调用生成器时,它不会真的执行,而是返回一个迭代器,每次在迭代器上调用内置的next函数时,迭代器会把生成器推进到下一个yield表达式:

获取到一个生成器以后,可以正常的遍历它:

如果你还是需要一个列表,那么可以将函数的调用结果作为参数,再调用list方法

可迭代对象

需要注意的是,普通的迭代器只能迭代一轮,一轮之后重复调用是无效的。解决这种问题的方法是,你可以定义一个可迭代的容器类

这样的话,将类的实例迭代重复多少次都没问题:

但要注意的是,仅仅是实现__iter__方法的迭代器,只能通过for循环来迭代;想要通过next方法迭代的话则需要使用iter方法:

使用位置参数

有时候,方法接收的参数数目可能不一定,比如定义一个求和的方法,至少要接收两个参数:

对于这种接收参数数目不一定,而且不在乎参数传入顺序的函数,则应该利用位置参数*args

但要注意的是,不定长度的参数args在传递给函数时,需要先转换成元组tuple。这意味着,如果你将一个生成器作为参数带入到函数中,生成器将会先遍历一遍,转换为元组。这可能会消耗大量内存:

使用关键字参数

关键字参数可提高代码可读性

可以通过关键字参数给函数提供默认值

便于扩充函数参数

定义只能使用关键字参数的函数

普通的方式,在调用时不会强制要求使用关键字参数

使用 Python3 中强制关键字参数的方式

使用 Python2 中强制关键字参数的方式

关于参数的默认值

算是老生常谈了:函数的默认值只会在程序加载模块并读取到该函数的定义时设置一次

也就是说,如果给某参数赋予动态的值(

比如[]或者{}),则如果之后在调用函数的时候给参数赋予了其他参数,则以后再调用这个函数的时候,之前定义的默认值将会改变,成为上一次调用时赋予的值:

因此,更推荐使用None作为默认参数,在函数内进行判断之后赋值:

__slots__

默认情况下,Python 用一个字典来保存一个对象的实例属性。这使得我们可以在运行的时候动态的给类的实例添加新的属性:

然而这个字典浪费了多余的空间 -— 很多时候我们不会创建那么多的属性。因此通过__slots__可以告诉 Python

不要使用字典而是固定集合来分配空间。

__call__

通过定义类中的__call__方法,可以使该类的实例能够像普通函数一样调用。

通过这种方式实现的好处是,可以通过类的属性来保存状态,而不必创建一个闭包或者全局变量。

@classmethod & @staticmethod

@classmethod@staticmethod很像,但他们的使用场景并不一样。

类内部普通的方法,都是以self作为第一个参数,代表着通过实例调用时,将实例的作用域传入方法内;

@classmethodcls作为第一个参数,代表将类本身的作用域传入。无论通过类来调用,还是通过类的实例调用,默认传入的第一个参数都将是类本身

@staticmethod不需要传入默认参数,类似于一个普通的函数

来通过实例了解它们的使用场景:

假设我们需要创建一个名为Date的类,用于储存 年/月/日 三个数据

上述代码创建了Date类,该类会在初始化时设置day/month/year属性,并且通过property设置了一个getter,可以在实例化之后,通过time获取存储的时间:

但如果我们想改变属性传入的方式呢?毕竟,在初始化时就要传入年/月/日三个属性还是很烦人的。能否找到一个方法,在不改变现有接口和方法的情况下,可以通过传入2016-11-09这样的字符串来创建一个Date实例?

你可能会想到这样的方法:

但不够好:

在类外额外多写了一个方法,每次还得格式化以后获取参数

这个方法也只跟Date类有关

没有解决传入参数过多的问题

此时就可以利用@classmethod,在类的内部新建一个格式化字符串,并返回类的实例的方法:

这样,我们就可以通过Date类来调用from_string方法创建实例,并且不侵略、修改旧的实例化方式:

好处:

@classmethod内,可以通过cls参数,获取到跟外部调用类时一样的便利

可以在其中进一步封装该方法,提高复用性

更加符合面向对象的编程方式

@staticmethod,因为其本身类似于普通的函数,所以可以把和这个类相关的 helper

方法作为@staticmethod,放在类里,然后直接通过类来调用这个方法。

将与日期相关的辅助类函数作为@staticmethod方法放在Date类内后,可以通过类来调用这些方法:

创建上下文管理器

上下文管理器,通俗的介绍就是:在代码块执行前,先进行准备工作;在代码块执行完成后,做收尾的处理工作。with语句常伴随上下文管理器一起出现,经典场景有:

通过with语句,代码完成了文件打开操作,并在调用结束,或者读取发生异常时自动关闭文件,即完成了文件读写之后的处理工作。如果不通过上下文管理器的话,则会是这样的代码:

比较繁琐吧?所以说使用上下文管理器的好处就是,通过调用我们预先设置好的回调,自动帮我们处理代码块开始执行和执行完毕时的工作。而通过自定义类的__enter____exit__方法,我们可以自定义一个上下文管理器。

然后可以以这样的方式进行调用:

在调用的时候:

with语句先暂存了ReadFile类的__exit__方法

然后调用ReadFile类的__enter__方法

__enter__方法打开文件,并将结果返回给with语句

上一步的结果被传递给file_read参数

with语句内对file_read参数进行操作,读取每一行

读取完成之后,with语句调用之前暂存的__exit__方法

__exit__方法关闭了文件

要注意的是,在__exit__方法内,我们关闭了文件,但最后返回True,所以错误不会被with语句抛出。否则with语句会抛出一个对应的错误。

(0)

相关推荐

  • 分享一下如何编写高效且优雅的 Python 代码

    本文部分提炼自书籍:<Effective Python>&<Python3 Cookbook>,但也做出了修改,并加上了作者自己的理解和运用中的最佳实践. 全文约 9956 字,读完可能需要 24 分钟. Pythonic列表切割 list[start:end:step] 如果从列表开头开始切割,那么忽略 start 位的 0,例如list[:4] 如果一直切到列表尾部,则忽略 end 位的 0,例如list[3:] 切割列表时,即便 start 或者 end 索引跨界也不

  • 如何利用Promises编写更优雅的JavaScript代码

    你可能已经无意中听说过 Promises,很多人都在讨论它,使用它,但你不知道为什么它们如此特别.难道你不能使用回调么?有什么了特别的?在本文中,我们一起来看看 Promises 是什么以及如何使用它们写出更优雅的 JavaScript 代码. Promises 易于阅读 比如说我们想从 HipsterJesus 的API中抓取一些数据并将这些数据添加到我们的页面中.这些 API 的响应数据形式如下: { "text": "<p>Lorem ipsum...<

  • 分享3个简单的Python代码高效运行技巧

    目录 1. 引言 2. 获取字典的值 3. 循环中使用enumerate 4. 使用f-strings来拼接和打印字符串 5. 总结 1. 引言 小伙伴们日常工作中都必不可少地使用Python实现一些简单的功能,但是不同的人所编写的代码执行效率往往是不同的.本文重点介绍大家经常遇到的场景下,三个有效的,方便理解的,执行高效的实用技巧. 闲话少说,我们直接开始吧!!! 2. 获取字典的值 不妨假设我们有以下字典: my_dict = {'first_name': 'Michaela',      

  • 详解如何在ASP.NET Core中编写高效的控制器

    通过遵循最佳实践,可以编写更好的控制器.所谓的"瘦"控制器(指代码更少.职责更少的控制器)更容易阅读和维护.而且,一旦你的控制器很瘦,可能就不需要对它们进行太多测试了.相反,你可以专注于测试业务逻辑和数据访问代码.瘦控制器的另一个优点是,它更容易维护控制器的多个版本. 这篇文章讨论了使控制器变胖的坏习惯,然后探索了使控制器变瘦和易于管理的方法.我列出编写控制器的最佳实践可能并不全面,但我已经讨论了最重要的一些,并在适当的情况下提供了相关的源代码.在接下来的几节中,我们将研究什么是胖控制

  • 让你的python代码更加pythonic(简练、明确、优雅)

    何为pythonic? pythonic如果翻译成中文的话就是很python.很+名词结构的用法在中国不少,比如:很娘,很国足,很CCTV等等. 我的理解为,很+名词表达了一种特殊和强调的意味.所以很python可以理解为:只有python能做到的,区别于其他语言的写法,其实就是python的惯用和特有写法. 置换两个变量的值. 很python的写法: 复制代码 代码如下: a,b = b,a 不python的写法: 复制代码 代码如下: temp = a a = b b = temp 上面的例

  • 如何写出优雅的JS 代码

    变量 使用有意义和可发音的变量名 // 不好的写法 const yyyymmdstr = moment().format("YYYY/MM/DD"); // 好的写法 const currentDate = moment().format("YYYY/MM/DD"); 对同一类型的变量使用相同的词汇 // 不好的写法 getUserInfo(); getClientData(); getCustomerRecord(); // 好的写法 getUser(); 使用可

  • 基于Python编写微信清理工具的示例代码

    目录 主要功能 运行环境 核心代码 完整代码 前几天网上找了一款 PC 端微信自动清理工具,用了一下,电脑释放了 30GB 的存储空间,而且不会删除文字的聊天记录,很好用,感觉很多人都用得到,就在此分享一下,而且是用 Python 写的,喜欢 Python 的小伙伴可以探究一下. 主要功能 它可以自动删除 PC 端微信自动下载的大量文件.视频.图片等数据内容,释放几十 G 的空间占用,而且不会删除文字的聊天记录,可以放心使用. 工作以后,微信的群聊实在太多了,动不动就被拉入一个群中,然后群聊里大

  • 分享方便调试Python代码的2个实用工具

    目录 1. 引言 2. 动机 3. Loguru 3.1 安装 3.2 举个栗子 3.3 使用Loguru 4. Snoop 4.1 安装 4.2 举例 4.3 使用factorial 5. 总结 1. 引言 今天来给小伙伴推荐两款实用的便于调试Python代码的工具,可以方便展示我们调试代码的中间状态,提升大家的编码效率. 2. 动机 在日常工作中,经常写Python的小伙伴经常会遇到需要调试代码bug的情形,有时候我们Python的错误提示信息特别丑, 举例如下: 2 divided by

  • python代码的几种常见加密方式分享

    目录 1.发行.pyc文件 1.1 编译加密 1.2 是否可逆 1.3是否影响模块调用 2.代码混淆 3.生成exe可执行文件 4…py文件转化为.so文件 5.附加运行辅助包文件 方式一:高级加密模式,需要引导代码 方式二:超级加密模式 方式三: 虚拟加密模式 Python的文件类型介绍: .py python的源代码文件 .pyc Python源代码import后,编译生成的字节码 .pyo Python源代码编译优化生成的字节码.pyo比pyc并没有优化多少,只是去掉了断言 .pyd Py

  • 提高Python代码可读性的5个技巧分享

    目录 1. Comments 2. Explicit Typing 3. Docstrings (Documentation Strings) 4. Readable Variable Names 5. Avoiding Magic Numbers 总结 不知道小伙伴们是否有这样的困惑,当我们回顾自己 6 个月前编写的一些代码时,往往会看的一头雾水,或者是否当我们接手其他人的代码时, Python 中有许多方法可以帮助我们理解代码的内部工作原理,良好的编程习惯,可以使我们的工作事半功倍! 例如,

随机推荐