Python实践提升-变量与注释

Python实践提升-变量与注释
编程是一个通过代码来表达思想的过程。听上去挺神秘,但其实我们早就做过类似的事情——当年在小学课堂上写出第一篇 500 字的作文,同样也是在表达思想,只是二者方式不同,作文用的是词语和句子,而编程用的是代码。
  但代码与作文之间也有相通之处,代码里也有许多“词语”和“句子”。大部分的变量名是词语,而大多数注释本身就是句子。当我们看到一段代码时,最先注意到的,不是代码有几层循环,用了什么模式,而是变量与注释,因为它们是代码里最接近自然语言的东西,最容易被大脑消化、理解。
  正因如此,如果作者在编写变量和注释时含糊不清、语焉不详,其他人将很难搞清楚代码的真实意图。就拿下面这行代码来说:

# 去掉 s 两边的空格,再处理
value = process(s.strip())

你能告诉我这段代码在做什么吗?当我看到它时,是这么想的:
在 s 上调用 strip(),所以 s 可能是一个字符串?不过为什么要去掉两边的空格呢?
process(…),顾名思义,“处理”了一下 s,但具体是什么处理呢?
处理结果赋值给了 value,value 代表“值”,但“值”又是什么?
开头的注释就更别提了,它说的就是代码本身,对理解代码没有丝毫帮助。
  最后的结论是:“将一个可能是字符串的东西两端的空格去掉,然后处理一下,最后赋值给某个不明物体。”我只能理解到这种程度了。
  但同样是这段代码,如果我稍微调整一下变量的名字,加上一点点注释,就会变得截然不同:

# 用户输入可能会有空格,使用 strip() 去掉空格
username = extract_username(input_string.strip())

新代码读上去是什么感觉?是不是代码意图变得容易理解多了?这就是变量与注释的魔力。

从计算机的角度来看,变量(variable)是用来从内存找到某个东西的标记。它叫“阿猫”“阿狗”还是“张三”“李四”,都无所谓。注释同样如此,计算机一点儿都不关心你的注释写得是否通顺,用词是否准确,因为它在执行代码时会忽略所有的注释。

但正是这些对计算机来说无关痛痒的东西,直接决定了人们对代码的“第一印象”。好的变量和注释并非为计算机而写,而是为每个阅读代码的人而写(当然也包括你自己)。变量与注释是作者表达思想的基础,是读者理解代码的第一道门,它们对代码质量的贡献毋庸置疑。

本章将对 Python 里的变量和注释做简单介绍,我会分享一些常用的变量命名原则,介绍编写代码注释的几种方式。在编程建议部分,我会列举一些与变量和注释有关的好习惯。

我们从变量和注释开始,学习如何写出给人留下美好“第一印象”的好代码吧!
  基础知识
  本节将介绍一些与变量和注释相关的基础知识。

变量常见用法
  在 Python 里,定义一个变量特别简单:

>>> author = 'andy'
>>> print('Hello, {}!'.format(author))
Hello, andy!

因为 Python 是一门动态类型的语言,所以我们无须预先声明变量类型,直接对变量赋值即可。

你也可以在一行语句里同时操作多个变量,比如调换两个变量所指向的值:

>>> author, reader = 'andy', 'raymond'
>>> author, reader = reader, author ➊
>>> author
'raymond'

❶ 交换两个变量

变量解包

变量解包(unpacking)是 Python 里的一种特殊赋值操作,允许我们把一个可迭代对象(比如列表)的所有成员,一次性赋值给多个变量:

>>> usernames = ['andy', 'raymond']

注意:左侧变量的个数必须和待展开的列表长度相等,否则会报错

>>> author, reader = usernames
>>> author
'andy'

假如在赋值语句左侧添加小括号 (…),甚至可以一次展开多层嵌套数据:

>>> attrs = [1, ['andy', 100]]
>>> user_id, (username, score) = attrs
>>> user_id
1
>>> username
'andy'

除了上面的普通解包外,Python 还支持更灵活的动态解包语法。只要用星号表达式(*variables)作为变量名,它便会贪婪地捕获多个值对象,并将捕获到的内容作为列表赋值给 variables。

比如,下面 data 列表里的数据就分为三段:头为用户,尾为分数,中间的都是水果名称。通过把 *fruits 设置为中间的解包变量,我们就能一次性解包所有变量——fruits 会捕获 data 去头去尾后的所有成员:

>>> data = ['piglei', 'apple', 'orange', 'banana', 100]
>>> username, *fruits, score = data
>>> username
'piglei'
>>> fruits
['apple', 'orange', 'banana']
>>> score
100

和常规的切片赋值语句比起来,动态解包语法要直观许多:

  1. 动态解包
>>> username, *fruits, score = data
  1. 切片赋值
>>> username, fruits, score = data[0], data[1:-1], data[-1]

两种变量赋值方式完全等价
上面的变量解包操作也可以在任何循环语句里使用:

>>> for username, score in [('andy', 100), ('raymond', 60)]:
...     print(username)
...
andy
raymond

单下划线变量名 _
在常用的诸多变量名中,单下划线 _ 是比较特殊的一个。它常作为一个无意义的占位符出现在赋值语句中。_ 这个名字本身没什么特别之处,这算是大家约定俗成的一种用法。

举个例子,假如你想在解包赋值时忽略某些变量,就可以使用 _ 作为变量名:

忽略展开时的第二个变量

>>> author, _ = usernames

忽略第一个和最后一个变量之间的所有变量

>>> username, *_, score = data

而在 Python 交互式命令行(直接执行 python 命令进入的交互环境)里,_ 变量还有一层特殊含义——默认保存我们输入的上个表达式的返回值:

>>> 'foo'.upper()
'FOO'
>>> print(_) ➊
FOO

❶ 此时的 _ 变量保存着上一个 .upper() 表达式的结果

1“贪婪”一词在计算机领域具有特殊含义。比方说,某个行为要捕获一批对象,它既可以选择捕获 1 个,也可以选择捕获 10 个,两种做法都合法,但它总是选择结果更多的那种:捕获 10 个,这种行为就称得上是“贪婪”。

给变量注明类型
  前面说过,Python 是动态类型语言,使用变量时不需要做任何类型声明。在我看来,这是 Python 相比其他语言的一个重要优势:它减少了我们的心智负担,让写代码变得更容易。尤其对于许多编程新手来说,“不用声明类型”无疑会让学 Python 这件事变得简单很多。

但任何事物都有其两面性。动态类型所带来的缺点是代码的可读性会因此大打折扣。

试着读读下面这段代码:

def remove_invalid(items):
    """剔除 items 里面无效的元素"""
    ... ...

你能告诉我,函数接收的 items 参数是什么类型吗?是一个装满数字的列表,还是一个装满字符串的集合?只看上面这点儿代码,我们根本无从得知。

为了解决动态类型带来的可读性问题,最常见的办法就是在函数文档(docstring)里做文章。我们可以把每个函数参数的类型与说明全都写在函数文档里。
  下面是增加了 Python 官方推荐的 Sphinx 格式文档后的效果:

def remove_invalid(items):
    """剔除 items 里面无效的元素

    :param items: 待剔除对象
    :type items: 包含整数的列表,[int, ...]
    """

在上面的函数文档里,我用 :type items: 注明了 items 是个整型列表。任何人只要读到这份文档,马上就能知道参数类型,不用再猜来猜去了。

当然,标注类型的办法肯定不止上面这一种。在 Python 3.5 版本以后,你可以用类型注解功能来直接注明变量类型。相比编写 Sphinx 格式文档,我其实更推荐使用类型注解,因为它是 Python 的内置功能,而且正在变得越来越流行。

具体来说,针对变量的类型注解语法是在 Python 3.6 版本引入的,而 3.5 版本只支持注解函数参数。

要使用类型注解,只需在变量后添加类型,并用冒号隔开即可,比如 func(value: str) 表示函数的 value 参数为字符串类型。

下面是给 remove_invalid() 函数添加类型注解后的样子:

from typing import List

def remove_invalid(items: List[int]):"""剔除 items 里面无效的元素"""
    ... ...

❶ List 表示参数为列表类型,[int] 表示里面的成员是整型

“类型注解”只是一种有关类型的注释,不提供任何校验功能。要校验类型正确性,需要使用其他静态类型检查工具(如 mypy 等)。
  平心而论,不管是编写 Sphinx 格式文档,还是添加类型注解,都会增加编写代码的工作量。同样一段代码,标注变量类型比不标注一定要花费更多时间。

但从我的经验来看,这些额外的时间投入,会带来非常丰厚的回报:

代码更易读,读代码时可以直接看到变量类型;
大部分的现代化 IDE 3 会读取类型注解信息,提供更智能的输入提示;
类型注解配合 mypy 等静态类型检查工具,能提升代码正确性。
IDE 是 integrated development environment(集成开发环境)的缩写,在满足代码编辑的基本需求外,IDE 通常还集成了许多方便开发者的功能。常见的 Python IDE 有 PyCharm、VS Code 等。

因此,我强烈建议在多人参与的中大型 Python 项目里,至少使用一种类型注解方案——Sphinx 格式文档或官方类型注解都行。能直接看到变量类型的代码,总是会让人更安心。

变量命名原则
  如果要从变量着手来破坏代码质量,办法多到数也数不清,比如定义了变量但是不用,或者定义 100 个全局变量,等等。但如果要在这些办法中选出破坏力最强的那个,非“给变量起个坏名字”莫属。

下面这段代码就是一个充斥着坏名字的“集大成”者。试着读读,看看你会有什么感受:

data1 = process(data)
if data1 > data2:
    data2 = process_new(data1)
    data3 = data2
return process_v2(data3)

怎么样,是不是挠破头都看不懂它在做什么?坏名字对代码质量的破坏力可见一斑。

那么问题来了,既然大家都知道上面这样的代码不好,为何在程序世界里,每天都有类似的代码被写出来呢?我猜这是因为给变量起个好名字真的很难。在计算机科学领域,有一句广为流传的格言(俏皮话):

计算机科学领域只有两件难事:缓存失效和命名。

——Phil Karlton

这句话里虽然一半严肃一半玩笑,但“命名”有时真的会难到让人抓狂。我常常呆坐在显示器前,抓耳挠腮好几分钟,就是没法给变量想出一个合适的名字。

要给变量起个好名字,主要靠的是经验,有时还需加上一丁点儿灵感,但更重要的是遵守一些基本原则。下面就是我总结的几条变量命名的基本原则。

遵循 PEP 8 原则

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值