开篇废话

这是在美国Amazon上评价很不错的一本书,其实严格来说这可能不算书,而是一本小册子。就像书名一样,里面的内容主要是用一些例子讲述地道的Python的代码是怎样写的。书中把很多例子用不良风格和地道Python写法作对比,内容覆盖谈不上很全,但是每一条都很有代表性。总体而言非常适合新手,同时里面有些条目老手看了或许也会有豁然开朗的感觉。作者Jeff Knupp曾在全球最牛B的高盛和其他银行里做过金融系统开发,在北美Python社区里也很有活跃度。

自己用Python也有些年头了,做过一年多的商业开发,不过其他大部分还是以科研和预研期的算法为主。最近因为又开始用Python做商业开发,所以想着顺便找些书看看,无意中看到了这本小书,觉得很不错,国内没有卖的,更别提中文版了。翻译这本书,算是复习和重新思考下Python,同时也会有少量自己的见解(C++风格注释绿色粗体),希望能坚持下去吧。我看的版本主要分为四部分:Control Structures and Functions(控制结构和函数)、Working with Data(数据和类型)、Organizing Your Code(代码组织)、General Advice(一般性建议)。每一部分里又分为不同的小章节,一共二十几个。我会按这个顺序不定期放出数目不定的章节。本人英文水平尚可,不过没有翻译经验,虽然不知道会不会有人关注这个系列,还是希望如果有看官,请轻拍指正:)

原书参考:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/

下一篇:翻译《Writing Idiomatic Python》(二):函数、异常


1. 控制结构和函数

1.1 if语句

1.1.1 通过链式比较让语句更加简明

当使用if语句时,优先使用链式比较操作,不仅会让语句更加简明,也会让执行效率更好。

不良风格:

 if x <= y and y <= z:
return True

地道Python:

 if x <= y <= z:
return True

// Python解释执行以上两种不同的比较方式时,其实都是先比较x<=y,如果为真,再比较y<=z。主要的区别在于,链式比较时,会先取y的值,然后复制压栈,整个过程中y的求值只执行了一次,而用and的方式时,y的求值会执行两次,也就是说,如果比较的是三个函数或者复杂的对象的话,链式比较只会求值三次,而通过and比较的方式则会求值4次。这大概就是为什么作者说执行效率会更好,但实际上如果只是简单的变量进行比较,效率未必会有提高。

1.1.2 避免将条件分支中的代码和冒号放在同一行

使用缩进来表示代码块的结构会让人更容易判断条件分支的代码结构。ifelifelse语句应该都总是独占一行,在冒号后没有代码。

不良风格:

 name = 'Jeff'
address = 'New York, NY' if name: print(name)
print(address)

地道Python:

 name = 'Jeff'
address = 'New York, NY' if name:
print(name)
print(address)

// 文件中的代码应该遵循这个规则,在控制台下放一行也未尝不可

1.1.3 避免在复合的if语句中重复出现同一个变量名

当想用if语句检查一个变量是否和许多值中的一个相等时,用==or重复写许多遍是否相等的检查会显得代码很冗长。简洁的写法是判断该变量是否在一个可遍历的结构中。

不良风格:

 is_generic_name = False
name = 'Tom'
if name == 'Tom' or name == 'Dick' or name == 'Harry':
is_generic_name = True

地道Python:

 name = 'Tom'
is_generic_name = name in ('Tom', 'Dick', 'Harry')

1.1.4 避免直接与True, False或者None直接比较

对于任意Python中的对象,无论是内建的还是用户定义的,本身都会关联一个内部的“真值”(truthiness)。所以很自然地,当判断一个条件是否为真的时候,尽量在条件判断语句中优先依靠这个隐式的“真值”。下面列举的是“真值”为False的情况:

None
False
数值0
空的序列(列表,元组等)
空的字典
当__len__或者__nonzero__被调用后返回的0值或者False

按照上面的最后一条,通过检查调用__len__或者__nonzero__后返回的值的方式,我们也可以定义自己创建的类型的“真值”。除了上面列举的这些,其他的情况都被认为“真值”为True

在Python中if语句隐式地使用“真值”,所以你的代码中也应该这样做。比如对于下面这种写法:

if foo == True:

更简单而直接的写法是:

if foo:

这样做的理由有很多。最明显的一条理由是,如果你的代码发生了变化,比如当foo变成了一个int型而不是TrueFalseif语句在判断是否为0时仍然正确。在更深的层面上,这是基于相等性(equality)和等价性(identity)的差别。使用==检查的是两个对象是否有相等或是等效的值(由_eq属性定义),而is语句则检查的是两个对象在底层是否同一个对象。

// Python对相等的实现在C代码中实现将比较对象用PyInt_AS_LONG转化成long型,然后再用C中的==进行比较,而is的实现是直接==比较。

所以对FalseNone和和空的序列比如[], {},以及()应该避免直接进行比较。如果一个叫my_list的列表为空, if my_list 会判断为False。当然有些情况下,虽然不推荐,但是直接和None比较是必须的。当在一个函数中需要判断一个默认值为None的参数是否被赋值的时候,比如:

 def insert_value(value, position=None):
"""向自定义的容器中插入一个值,插入值
的位置作为可选参数,默认值为None"""
if position is not None:
...

如果使用 if position: 的话,哪里会出错呢?设想如果有人想在0位置插入一个值,那么函数会认为position这个参数没有设置,因为 if 0: 会判定为False。注意这里使用的是is not,根据PEP8,和None比较应该总是用is或者is not而不是==

总之,就让Python的“真值”代替你做比较的工作。

不良风格:

 def number_of_evil_robots_attacking():
return 10 def should_raise_shields():
# 只有当一只以上的巨型机器人进攻时才打开防护罩
# 所以我只需要返回巨型机器人的数量,如果不为零会自动判断为真
return number_of_evil_robots_attacking() if should_raise_shields() == True:
raise_shields()
print('防护罩已打开')
else:
print('安全!并没有巨型机器人在进攻')

地道Python:

 def number_of_evil_robots_attacking():
return 10 def should_raise_shields():
# 只有当一只以上的巨型机器人进攻时才打开防护罩
# 所以我只需要返回巨型机器人的数量,如果不为零会自动判断为真
return number_of_evil_robots_attacking() if should_raise_shields():
raise_shields()
print('防护罩已打开')
else:
print('安全!并没有巨型机器人在进攻')

1.1.5 使用if 和 else作为三元操作符的替代

和许多其他语言不同,Python没有三元操作符(比如: x ? true : false)。不过Python可以将赋值推迟到条件判断之后,所以在Python中三元操作可以用条件判断来替代。当然需要注意的是,除非是很简单的语句,否则三元操作的替代方案会让语句的可读性降低。

不良风格:

 foo = True
value = 0 if foo:
value = 1 print(value)

地道Python:

 foo = True

 value = 1 if foo else 0

 print(value)

1.2 For循环

1.2.1 在循环中使用enumerate函数来创建计数或索引

在许多其他语言中,开发者习惯显式地声明一个变量用来作为循环中的计数或者相关容器的索引。例如在C++中:

 for ( int i = ; i < container.size(); ++i )
{
// Do stuff
}

在Python中,内置的enumerate函数就可以很自然地处理这种需要。

不良风格:

 my_container = ['Larry', 'Moe', 'Curly']
index = 0
for element in my_container:
print('{} {}'.format(index, element))
index += 1

地道Python:

 my_container = ['Larry', 'Moe', 'Curly']
for index, element in enumerate(my_container):
print('{} {}'.format(index, element))

1.2.2 使用in关键字遍历可迭代结构

在没有for_each风格的语言中,开发者习惯于用索引(下标)来遍历一个容器中的元素。而在Python中,这种操作可以通过in关键字来更为优雅地实现。

不良风格:

 my_list = ['Larry', 'Moe', 'Curly']
index = 0
while index < len(my_list):
print(my_list[index])
index += 1

地道Python:

 my_list = ['Larry', 'Moe', 'Curly']
for element in my_list:
print(element)

1.2.3 使用else去执行一个for循环全部遍历结束后的代码

在Python的for循环中可以包含一个else分句,这是一个不多人知道的技巧。else语句块会在for循环中的迭代结束后执行,除非在迭代过程中循环因为break语句结束。利用这种写法我们可以在循环中执行条件检查。要么在要检查的条件语句为真时用break语句停止循环,要么在循环结束后进入else语句块并执行条件未被满足的情况下要执行的动作。这样做避免了在循环中单独使用一个标示变量来检查条件是否被满足。

不良风格:

 for user in get_all_users():
has_malformed_email_address = False
print('检查 {}'.format(user))
for email_address in user.get_all_email_addresses():
if email_is_malformed(email_address):
has_malformed_email_address = True
print('包含恶意email地址!')
break
if not has_malformed_email_address:
print('所有email地址均有效!')

地道Python:

 for user in get_all_users():
print('检查 {}'.format(user))
for email_address in user.get_all_email_addresses():
if email_is_malformed(email_address):
print('包含恶意email地址!')
break
else:
print('所有email地址均有效!')

转载请注明出处:達聞西@博客園

下一篇:翻译《Writing Idiomatic Python》(二):函数、异常

翻译《Writing Idiomatic Python》(一):if语句、for循环的更多相关文章

  1. 《Writing Idiomatic Python》前两部分的中文翻译

    汇总了一下这本小书前两部分的内容: 翻译<Writing Idiomatic Python>(一):if语句.for循环 翻译<Writing Idiomatic Python> ...

  2. 翻译《Writing Idiomatic Python》(五):类、上下文管理器、生成器

    原书参考:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/ 上一篇:翻译<Writing Idiomatic ...

  3. 翻译《Writing Idiomatic Python》(四):字典、集合、元组

    原书参考:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/ 上一篇:翻译<Writing Idiomatic ...

  4. 翻译《Writing Idiomatic Python》(三):变量、字符串、列表

    原书参考:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/ 上一篇:翻译<Writing Idiomatic ...

  5. 翻译《Writing Idiomatic Python》(二):函数、异常

    原书参考:http://www.jeffknupp.com/blog/2012/10/04/writing-idiomatic-python/ 上一篇:翻译<Writing Idiomatic ...

  6. 分享书籍[writing idiomatic python ebook]

    你是不是总是觉得学了python好久,蓦然回首,总是感觉写的代码不是那么有pythonic的味道.看看别人的代码(django,webpy),再看看自己的代码,觉得就是一java-python的混合体 ...

  7. Python 的条件语句和循环语句

    一.顺序结构 顺序结构是最简单的一种程序结构,程序按照语句的书写次序自上而下顺序执行. 二.分支控制语句 Python条件语句是通过一条或多条语句的执行结果(True或者False)来决定执行的代码块 ...

  8. 分享书籍[writing idiomatic python ebook] 二

    对多个变量设置相同的值时,用连等号一起赋值 x = 10 y = 10 z = 10 改成: x = y = z = 10 交换变量值时,可以避免定义新的临时变量 x = 10 y = 5 temp ...

  9. Python之条件语句以及循环

    Python代码的缩进规则.具有相同缩进的代码被视为代码块 缩进请严格按照Python的习惯写法:4个空格,不要使用Tab,更不要混合Tab和空格,否则很容易造成因为缩进引起的语法错误. 注意: if ...

随机推荐

  1. PowerDesigner工具箱(palette)如何打开

    我使用的PowerDesigner是15.1版本的,其他版本的操作可能会有所不同 我们在使用PowerDesigner的时候,有时候可能会不小心把悬浮的工具箱隐藏掉,就是下面这个东西 怎么显示出来呢, ...

  2. u-boot移植总结(一)start.S分析

    本次移植u-boot-2010.09是基于S3C2440的FL440板子,板子自带NANDFLASH而没有NORFLASH,所以在U-BOOT启动的过程中必须实现从NANDFLASH到SDRAM的重定 ...

  3. ahjesus解决win下U盘无法写入的问题

    可能是由于不同品牌的U盘出厂时磁盘分区和格式化方式不同而引起的兼容性问题.解决方案如下 启动cmd.输入diskpart,启动DISKPART工具 在DISKPART窗口中输入以下命令: >li ...

  4. oauth授权协议的原理

    http://oauth.net/2/ 协议的原文.原来是1.0版本,现在是2.0版本了 https://ruby-china.org/topics/15396 https://blog.yorkxi ...

  5. dbcp/c3p0连接池设置mysql会话变量

    我们有几个计算风控值的定时任务,几乎每隔5秒会更新所有账户的当前总资产并以此通知风控,每隔一小时就产生一两个G的binlog,几十台服务器折腾..数据库是公用的,代码是通过工具自动生成的,直接修改流程 ...

  6. javascript函数中的三个技巧【二】

    技巧二: [惰性载入函数] 因为浏览器之间的行为的差异,我们经常会在函数中包含了大量的if语句,以检查浏览器特性,解决不同浏览器的兼容问题,比如,我们最常见的为dom节点添加时间的函数 functio ...

  7. javascript --- 词法分析

    JavaScript代码自上而下执行,但是在js代码执行前,会首先进行词法分析,所以事实上,js运行要分为词法分析和执行两个阶段. 词法分析主要分为三步: 第一步: 分析形参: 第二步: 分析变量声明 ...

  8. lazyload.js详解

    简介 lazyload.js用于长页面图片的延迟加载,视口外的图片会在窗口滚动到它的位置时再进行加载,这是与预加载相反的. 优点: 它可以提高页面加载速度: 在某些情况清晰它也可以帮助减少服务器负载. ...

  9. 【github】github 使用教程初级版

    github 是一个基于 git 的代码托管平台,付费用户可以建私人仓库,免费用户只能使用公共仓库.对于一般人来说公共仓库就已经足够了,而且也没多少代码来管理.下面简单介绍如何使用 github,供初 ...

  10. Android 系统版本&API对照表

    最新Android系统版本与API等级对应关系表 数据来源:http://d.android.com/guide/topics/manifest/uses-sdk-element.html#ApiLe ...