某同学应聘Python岗位被录用。上班第一天,Leader吩咐他写一个获取次日日期信息的函数。该同学信心满满地写下了这样一段代码, 然后就没有然后了。

import time
def get_next_date():
time.sleep(24*60*60)
return time.strftime('%Y-%m-%d')

当然,这只是一个段子,相信没有Python程序员真的会写出这样的代码。不过,很多时候,我们写出来的代码尽管功能满足需求,效率也还说得过去,但可读性较差,且难以维护,和人们通常所说的简洁优美相去甚远。

那么,怎样才能写出传说中的简洁优美的Python代码呢?前几天,我在Python学习群里征集一个Python小题目的答案,借以了解程序员的编程习惯,分析影响程序员写出Pythonic代码的主要原因,最终总结出三条秘诀。题目如下:二维列表转置。

严格讲,Python的列表并没有维度的概念。这里说的二维列表是指类似下面这样的列表。

[ [1, 2, 3],
[4, 5, 6],
[7, 8, 9] ]

请实现二维列表的转置(行列互换,首行变首列,尾行变尾列,如下所示)。

[ [1, 4, 7],
[2, 5, 8],
[3, 6, 9] ]

这个活动得到了同学们的热情支持,甚至有同学们使用Java语言提交了自己的答案。对于这些同学,除了表示感谢,我无法回报更多,只能在能力所及的范围内,为每人提供30分钟的一对一服务,比如,代写作业、答疑等。

这是我收到的众多答案中的一个,并且很有代表性。

>>> a = [[1,2,3], [4,5,6], [7,8,9]]
>>> a_copy = []
>>> a_temp = []
>>> a_size = len(a)
>>> a_item_size = len(a[0])
>>> for i in range(a_item_size):
for j in range(a_size):
a_temp.append(a[j][i])
a_copy.append(a_temp)
a_temp = [] >>> a_copy
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

阅读这段代码的时候,最感吃力的是我需要记住a_copy、a_temp 、a_size、a_item_size等中间变量,否则读不懂后面的代码。在适当的位置合理地使用中间变量或临时变量,会提高效率,增强代码地可读性,但是,不加限制地使用,则会降低代码的可读性,同时也会带来更多的风险。

对上面的代码稍加修改,并封装成函数,感觉顺眼了很多。

>>> def transpose(arr):
result, arr_len = list(), len(arr)
for j in range(len(arr[0])):
result.append(list())
for i in range(arr_len):
result[-1].append(arr[i][j])
return result >>> transpose([[1,2,3], [4,5,6], [7,8,9]])
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

在我收到的答案中,大约有70%和修改后的这段代码类似。不过,这段代码看起来还是有点啰嗦,不够简洁。如果使用列表推导式,则可以在一行之内完成转置。

>>> def transpose(arr):
return [[arr[i][j] for i in range(len(arr))] for j in range(len(arr[0]))] >>> transpose([[1,2,3], [4,5,6], [7,8,9]])
[[1, 4, 7], [2, 5, 8], [3, 6, 9]]

使用列表推导式,可以让代码更加紧凑,但会降低可读性,因此应限制使用。在所有收集到的答案中,有几位同学使用了列表推导式,还有两位同学使用了NumPy的数组转置。遗憾的是,没有一位同学写出下面这个我认为的最佳答案。

>>> def transpose(arr):
return list(zip(*arr)) >>> transpose([[1,2,3], [4,5,6], [7,8,9]])
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]

这个答案,用到了内置函数zip()和一颗星(*)解包魔法,代码简洁高效。想要写出这样经典的答案,有一个前提条件就是对Python的内置函数能够信手拈来。Python内置了七十多个常用函数,配合不同的参数,可以实现非常精妙的功能。比如,从包含重复元素的列表中找出重复次数最多的元素,可以用一行代码高效实现。

>>> arr = [7,3,5,3,6,7,3,5,6,3,5]
>>> max(set(arr), key=lambda x:arr.count(x))
3

最后,总结一下写出简洁优美的Python代码的三条秘诀:

秘诀第1条:合理使用中间变量或临时变量
秘诀第2条:适度使用列表推导式等语法特性
秘诀第3条:尽量使用Python的内置函数

理解了这三点,才敢说自己会写Python代码的更多相关文章

  1. 通俗理解TCP的三次握手

    三次握手流程的本质,可以这么理解:TCP的三次握手其实是双方各一次握手,各一次确认,只是其中一次握手和确认合并在一起. 当然也可以更通俗的去理解: "喂,你听得到吗?" " ...

  2. 深入理解C#第三版部分内容

     最近,粗略的读了<深入理解C#(第三版)>这本技术书,书中介绍了C#不同版本之间的不同以及新的功能. 现在将部分摘录的内容贴在下面,以备查阅. C#语言特性: 1.C#2.0 C#2的主 ...

  3. 如何理解TCP的三次握手协议?

    • TCP是一个面向链接的协议,任何一个面向连接的协议,我们都可以将其类比为我们最熟悉的打电话模型. 如何类比呢?我们可以从建立和销毁两个阶段分别来看这件事情. 建立连接阶段 首先,我们来看看TCP中 ...

  4. 深入理解NIO(三)—— NIO原理及部分源码的解析

    深入理解NIO(三)—— NIO原理及部分源码的解析 欢迎回到淦™的源码看爆系列 在看完前面两个系列之后,相信大家对NIO也有了一定的理解,接下来我们就来深入源码去解读它,我这里的是OpenJDK-8 ...

  5. 使用神经网络来识别手写数字【译】(三)- 用Python代码实现

    实现我们分类数字的网络 好,让我们使用随机梯度下降和 MNIST训练数据来写一个程序来学习怎样识别手写数字. 我们用Python (2.7) 来实现.只有 74 行代码!我们需要的第一个东西是 MNI ...

  6. 理解JavaScript设计模式与开发应用中发布-订阅模式的最终版代码

    最近拜读了曾探所著的<JavaScript设计模式与开发应用>一书,在读到发布-订阅模式一章时,作者不仅给出了基本模式的通用版本的发布-订阅模式的代码,最后还做出了扩展,给该模式增加了离线 ...

  7. 深入理解 GIL:如何写出高性能及线程安全的 Python 代码

    深入理解 GIL:如何写出高性能及线程安全的 Python 代码 本文由 伯乐在线 - 郑芸 翻译.未经许可,禁止转载!英文出处:A. Jesse.欢迎加入翻译组. GIL对多线程的影响:http:/ ...

  8. 如果有三个Bool型变量,请写出一程序得知其中有2个以上变量的值是true

    下面这篇文章是从StackOverflow来的.LZ面试的时候遇到了一道面试题:“如果有三个Bool型变量,请写出一程序得知其中有2个以上变量的值是true”,于是LZ做了下面的这样的程序: bool ...

  9. 页面三个txt加载联动省市县的代码,类似淘宝的收货地址的布局

    页面三个txt加载联动省市县的代码,假如有一个树形的JSON,分别显示的省市县这时候三个TXT怎么做联动效果呢,这里用framework7为例HTML: <div class="lis ...

随机推荐

  1. 改进你的c#代码的5个技巧(三)

    本文完全独立于前两篇文章.如果你喜欢它们,我希望你也会喜欢这个.在上一篇文章中,我展示了哪种方法更快,并比较了代码的执行速度.在本文中,我将展示不同代码片段的内存消耗情况.为了显示内存映射和分配图,我 ...

  2. Laya 踩坑日记-人物模型穿模,模型显示不正常

    最近做游戏,人物要跑到很远的位置,z轴距离大概有20000个单位,然后就发现一个bug,到远处人物模型穿了,而且没办法改,这就尴尬了 Z轴对应值    0    100000 100000 当距离零点 ...

  3. 原生工程接入Flutter实现混编

    前言 上半年我定的OKR目标是帮助团队将App切入Flutter,实现统一技术栈,变革成多端融合开发模式.Flutter目前是跨平台方案中最有潜力实现我们这个目标的,不管是Hybird还是React ...

  4. 【Linux】CentOS7中yumbackend.py进程的结束方法

    环境: CentOS Linux release 7.3.1611 (Core) 今天启动这个不怎么用的机器,才启动,就发现后台的yum无法进行安装,持续报这个错误 Loaded plugins: f ...

  5. LeetCode530. 二叉搜索树的最小绝对差

    题目 又是常见的BST,要利用BST的性质,即中序遍历是有序递增序列. 法一.中序遍历 1 class Solution { 2 public: 3 vector<int>res; 4 v ...

  6. [Usaco2002 Feb]Rebuilding Roads重建道路

    题目描述 一场可怕的地震后,奶牛用N个牲口棚(1 <= N <= 150,编号1..N)重建了农民John的牧场.奶牛没有时间建设多余的道路,所以现在从一个牲口棚到另一个牲口棚的道路是唯一 ...

  7. QQ刷屏助手

    本人小学生,求大佬轻点儿 制作原因 原因很简单,还不是想报仇呗! 代码 1 # 原理是先将需要发送的文本放到剪贴板中,然后将剪贴板内容发送到qq窗口 2 # 之后模拟按键发送enter键发送消息 3 ...

  8. MySQL中redo log、undo log、binlog关系以及区别

    MySQL中redo log.undo log.binlog关系以及区别 本文转载自:MySQL中的重做日志(redo log),回滚日志(undo log),以及二进制日志(binlog)的简单总结 ...

  9. vue-cli快速创建项目,交互式

    vue脚手架用于快速构建vue项目基本架构 下面开始安装vue-cli npm install -g @vue/cli # OR yarn global add @vue/cli以上两句命令都可以安装 ...

  10. 关于MongoDB的简单理解(一)--基础篇

    一.什么是MongoDB? MongoDB是一个基于分布式文件存储的文档数据库,旨在简化开发和扩展,为WEB应用提供可扩展的高性能数据存储解决方案. MongoDB是一个介于关系数据库和非关系数据库之 ...