Python 编程语言要掌握的技能之一:善用变量来改善代码质量
如何为变量起名
在计算机科学领域,有一句著名的格言(俏皮话):
There are only two hard things in Computer Science: cache invalidation and naming things. 在计算机科学领域只有两件难事:缓存过期 和 给东西起名字
— Phil Karlton
第一个『缓存过期问题』的难度不用多说,任何用过缓存的人都会懂。至于第二个『给东西起名字』这事的难度,我也是深有体会。在我的职业生涯里,度过的作为黑暗的下午之一,就是坐在显示器前抓耳挠腮为一个新项目起一个合适的名字。
编程时起的最多的名字,还数各种变量。给变量起一个好名字很重要,因为好的变量命名可以极大的提高代码整体可读性。
下面几点,是我总结的为变量起名时,最好遵守的基本原则。
1. 变量名要有描述性,不能太宽泛
在可接受的长度范围内,变量名能把它所指向的内容描述的越精确越好。所以,尽量不要用那些过于宽泛的词来作为你的变量名:
· GOOD: day_of_week, hosts_to_reboot, expired_cards
· BAD: day, host, cards, temp
2. 变量名最好让人能猜出类型
老司机们都知道,Python 是一门动态类型语言,它(至少在 PEP 484 出现前)没有变量类型声明。所以当你看到一个变量时,除了通过上下文猜测,没法轻易知道它是什么类型。
不过,人们对于变量名和变量类型的关系,通常会有一些直觉上的约定,我把它们总结在了下面。
『什么样的名字会被当成 bool 类型?』
布尔类型变量的最大特点是:它只存在两个可能的值『是』 或 『不是』。所以,用 is、has等非黑即白的词修饰的变量名,会是个不错的选择。原则就是:让读到变量名的人觉得这个变量只会有『是』或『不是』两种值。
下面是几个不错的示例:
· is_superuser:『是否超级用户』,只会有两种值:是/不是
· has_error:『有没有错误』,只会有两种值:有/没有
· allow_vip:『是否允许 VIP』,只会有两种值:允许/不允许
· use_msgpack:『是否使用 msgpack』,只会有两种值:使用/不使用
· debug:『是否开启调试模式』,被当做 bool 主要是因为约定俗成
『什么样的名字会被当成 int/float 类型?』
人们看到和数字相关的名字,都会默认他们是 int/float 类型,下面这些是比较常见的:
· 释义为数字的所有单词,比如:port(端口号)、age(年龄)、radius(半径) 等等
· 使用 _id 结尾的单词,比如:user_id、host_id
· 使用 length/count 开头或者结尾的单词,比如: length_of_username、max_length、users_count
注意:不要使用普通的复数来表示一个 int 类型变量,比如 apples、trips,最好用 number_of_apples、trips_count 来替代。
其他类型
对于 str、list、tuple、dict 这些复杂类型,很难有一个统一的规则让我们可以通过名字去猜测变量类型。比如 headers,既可能是一个头信息列表,也可能是包含头信息的 dict。
对于这些类型的变量名,最推荐的方式,就是编写规范的文档,在函数和方法的 document string 中,使用 sphinx 格式(Python 官方文档使用的文档工具)来标注所有变量的类型。
3. 适当使用『匈牙利命名法』
第一次知道『匈牙利命名法』,是在 Joel on Software 的一篇博文中。简而言之,匈牙利命名法就是把变量的『类型』缩写,放到变量名的最前面。
关键在于,这里说的变量『类型』,并非指传统意义上的 int/str/list 这种类型,而是指那些和你的代码业务逻辑相关的类型。
比如,在你的代码中有两个变量:students 和 teachers,他们指向的内容都是一个包含 Person 对象的 list 。使用『匈牙利命名法』后,可以把这两个名字改写成这样:
students -> pl_students teachers -> pl_teachers
pl 是 person list 的首字母缩写。变量名被加上前缀后,当你看到以 pl_ 打头的变量时,就能知道它所指向的值类型了。
很多情况下,使用『匈牙利命名法』是一个不错的注意,它可以改善你的代码可读性,尤其在那些变量众多、同一类型多次出现时。注意不要滥用就好。
4. 变量名尽量短,但是绝对不要太短
在前面,我们提到要让变量名有描述性。如果不给这条原则加上任何限制,那么你很有可能写出这种描述性极强的变量名:how_much_points_need_for_level2。如果代码中充斥着这种过长的变量名,对于代码可读性来说是个灾难。
一个好的变量名,长度应该控制在 两到三个单词左右。比如上面的名字,可以缩写为 points_level2。
绝大多数情况下,都应该避免使用那些只有一两个字母的短名字,比如数组索引三剑客 i、j、k,用有明确含义的名字,比如 persion_index 来代替它们总是会更好一些。
使用短名字的例外情况
有时,不能使用短名字的原则也会有一些例外。当一些意义明确但是较长的变量名重复出现时,为了让代码更简洁,使用短名字缩写是完全可以的。但是为了降低理解成本,同一段代码内最好不要使用太多这种短名字。
比如在 Python 中导入模块时,就会经常用到短名字作为别名,像 Django i18n 翻译时常用的 gettext 方法通常会被缩写成 _ 来使用(from django.utils.translation import ugettext as _)
5. 其他注意事项
其他一些给变量命名的注意事项:
· 同一段代码内不要使用过于相似的变量名,比如同时出现 users、users1、 user3 这种序列
· 不要使用带否定含义的变量名,用 is_special 代替 is_not_normal
更好的使用变量
前面讲了如何为变量取一个好名字,下面我们谈谈在日常使用变量时,应该注意的一些小细节。
1. 保持一致性
如果你在一个方法内里面把图片变量叫做 photo,在其他的地方就不要把它改成 image,这样只会让代码的阅读者困惑:『image 和 photo 到底是不是同一个东西?』
另外,虽然 Python 是动态类型语言,但那也不意味着你可以用同一个变量名一会表示 str 类型,过会又换成 list。同一个变量名指代的变量类型,也需要保持一致性。
2. 尽量不要用 globals()/locals()
也许你第一次发现 globals()/locals() 这对内建函数时很兴奋,迫不及待的写下下面这种极端『简洁』的代码:
def render(request, user_id, trip_id): user= User.objects.get(id=user_id) trip= get_object_or_404(Trip, pk=trip_id) is_suggested= is_suggested(user, trip) # 利用 locals() 节约了三行代码,我是个天才! **return** render(request, 'trip.html', locals())
千万不要这么做,这样只会让读到这段代码的人(包括三个月后的你自己)痛恨你,因为他需要记住这个函数内定义的所有变量(想想这个函数增长到两百行会怎么样?),更别提 locals() 还会把一些不必要的变量传递出去。
更何况, The Zen of Python(Python 之禅) 说的清清楚楚:Explicit is better than implicit.(显式优于隐式)。还是老老实实把代码改成这样吧:
**return** render(request, 'trip.html', { 'user': user, 'trip': trip, 'is_suggested': is_suggested })
3. 变量定义尽量靠近使用
这个原则属于老生常谈了。很多人(包括我)在刚开始学习编程时,会有一个习惯。就是把所有的变量定义写在一起,放在函数或方法的最前面。
def generate_trip_png(trip): path= [] markers= [] photo_markers= [] text_markers= [] marker_count= 0 point_count= 0
这样做只会让你的代码『看上去很整洁』,但是对提高代码可读性没有任何帮助。
更好的做法是,让变量定义尽量靠近使用。那样当你阅读代码时,可以更好的理解代码的逻辑,而不是费劲的去想这个变量到底是什么、哪里定义的?
4. 合理使用 dict 来让函数返回多个值
Python 的函数可以返回多个值:
def latlon_to_address(lat, lon): **return** country, province, city
# 利用多返回值一次定义多个变量 country, province, city= latlon_to_address(lat, lon)
但是,这样的用法会产生一个小问题:如果某一天, latlon_to_address 函数需要返回『城区(District)』时怎么办?
如果是上面这种写法,你需要找到所有调用 latlon_to_address 的地方,补上多出来的这个变量,否则 ValueError: too many values to unpack 就会找上你:
country, province, city, district= latlon_to_address(lat, lon)
# 或者忽略多出来的返回值 country, province, city, _= latlon_to_address(lat, lon)
对于这种多返回值可能会变动的情况,使用 dict 作为返回值会更方便一些,当你新增返回值时,不会对之前的函数调用产生任何破坏性的影响:
def latlon_to_address(lat, lon): **return** { 'country': country, 'province': province, 'city': city } addr_dict= latlon_to_address(lat, lon)
这样做的坏处也有,代码兼容性虽然增加了,但是你不能继续用之前 x, y = f() 的方式一次定义多个变量了。取舍在于你自己。
5. 控制单个函数内的变量数量
人脑的能力是有限的,研究表明,人类的短期记忆只能同时记住不超过十个名字。所以,当你的某个函数过长(一般来说,超过一屏的的函数就会被认为有点过长了),包含了太多变量时。请及时把它拆分为多个小函数吧。
6. 及时删掉那些没用的变量
这条原则非常简单,也很容易做到。但是如果没有遵守,那它对你的代码质量的打击是毁灭级的。会让阅读你代码的人有一种被愚弄的感觉。
def fancy_func(): # 读者心理:嗯,这里定义了一个 fancy_vars fancy_vars= get_fancy() ... ...(一大堆代码过后) # 读者心理:这里就结束了?之前的 fancy_vars 去哪了?被猫吃了吗? **return** result
所以,请打开 IDE 的智能提示,及时清理掉那些定义了但是没有使用的变量吧。
7. 能不定义变量就不定义
有时候,我们定义变量时的心理活动是这样的:『嗯,这个值未来说不定会修改/二次使用』,让我们先把它定义成变量吧!
def get_best_trip_by_user_id(user_id): user= get_user(user_id) trip= get_best_trip(user_id) result= { 'user': user, 'trip': trip } **return** result
其实,你所想的『未来』永远不会来,这段代码里的三个临时变量完全可以去掉,变成这样:
def get_best_trip_by_user_id(user_id): **return** { 'user': get_user(user_id), 'trip': get_best_trip(user_id) }
没有必要为了那些可能出现的变动,牺牲代码当前的可读性。如果以后有定义变量的需求,那就以后再加吧。
结语
这是『Python 编程语言要掌握的技能之一』系列文章的第一篇,不知道看完文章的你,有没有什么想吐槽的?请留言告诉我吧。
更多Python技术文章请关注2019,Python技术持续更新(附教程)
Python 编程语言要掌握的技能之一:善用变量来改善代码质量的更多相关文章
- Python 工匠:善用变量来改善代码质量
欢迎大家前往腾讯云+社区,获取更多腾讯海量技术实践干货哦~ 本文由鹅厂优文发表于云+社区专栏 作者:朱雷 | 腾讯IEG高级工程师 『Python 工匠』是什么? 我一直觉得编程某种意义上是一门『手艺 ...
- Python 编程语言要掌握的技能之一:编写条件分支代码的技巧
Python 里的分支代码 Python 支持最为常见的 if/else 条件分支语句,不过它缺少在其他编程语言中常见的 switch/case 语句. 除此之外,Python 还为 for/whil ...
- python里用变量命名改善代码质量
编程时,总会遇到各种各样的变量,取一个好的变量名能够有效提高代码的可读性,而且python是一种,动态类型的语言,良好的变量名,能够在编写代码或者再次阅读代码时提高效率. 1. 变量名不要太宽泛,要有 ...
- Python 编程语言要掌握的技能之一:使用数字与字符串的技巧
最佳实践 1. 少写数字字面量 “数字字面量(integer literal)” 是指那些直接出现在代码里的数字.它们分布在代码里的各个角落,比如代码 del users[0] 里的 0 就是一个数字 ...
- 中小学Python编程语言教学
中小学Python编程语言教学 作为一名高中信息技术老师,被技术的发展潮流推动着,不断更新教学内容和方法,以适应快速发展的信息社会. 以前的中小学信息技术课程,老师们各显神通,身怀绝技,教PS,Fal ...
- 3.Python编程语言基础技术框架
3.Python编程语言基础技术框架 3.1查看数据项数据类型 type(name) 3.2查看数据项数据id id(name) 3.3对象引用 备注Python将所有数据存为内存对象 Python中 ...
- 使用Python编程语言连接MySQL数据库代码
使用Python编程语言连接MySQL数据库代码,跟大家分享一下: 前几天我用python操作了mysql的数据库,发现非常的有趣,而且python操作mysql的方法非常的简单和快速,所以我把代码分 ...
- python编程语言 函数的形参
python编程语言 函数的形参的讲解: 我在交互模式中写了个函数: def adder(**args): sum=0 for x in args.keys(): sum+=args[x] retur ...
- 可以用 Python 编程语言做哪些神奇好玩的事情?除了生孩子不能,其他全都行!
坦克大战 源自于一个用Python写各种小游戏的github合集,star数1k.除了坦克大战外,还包含滑雪者.皮卡丘GOGO.贪吃蛇.推箱子.拼图等游戏. 图片转铅笔画 帮助你快速生成属于自己的铅笔 ...
随机推荐
- GStreamer基础教程11 - 与QT集成
摘要 通常我们的播放引擎需要和GUI进行集成,在使用GStreamer时,GStreamre会负责媒体的播放及控制,GUI会负责处理用户的交互操作以及创建显示的窗口.本例中我们将结合QT介绍如何指定G ...
- The usage of Markdown---表格
更新时间:2019.09.14 谈到怎么在Markdown中插入表格,其实只要熟知以下几点就可以了: 使用管道符|进行内容的分割 使用冒号:和连号符-表示表格内容的对齐情况,连号符-在中间,冒号: ...
- IDEA配置maven+错误记录
使用maven并不是那么顺利所以把自己遇到的问题也记录一下 下载maven 从官网下载maven官网 配置环境变量 新建一个变量名:MAVEN_HOME 值:maven目录 path:%MAVEN_H ...
- Spring(三)面向切面编程(AOP)
在直系学长曾经的指导下,参考了直系学长的博客(https://www.cnblogs.com/WellHold/p/6655769.html)学习Spring的另一个核心概念--面向切片编程,即AOP ...
- DirectX9:基础篇 第一章 初始化Direct3D
一.简介 二.Direct3D类 1.创建D3D类 IDirect3D9* WINAPI Direct3DCreate9(UINT SDKVersion); //Direct3D类的创建 IDirec ...
- docker入门级详解
Docker 1 docker安装 yum install docker [root@topcheer ~]# systemctl start docker [root@topcheer ~]# mk ...
- forEach,map,every,some,filter简单用法实例
平时简单总结:不足之处还望见谅: 实例: 11.1 forEach:只能进行取值操作,不能进行修改 arr.forEach(function(element,index,arr){});//注意参数: ...
- Java抽象类、接口、内部类
抽象类的概念: 1.Java中可以定义没有方法体的方法,还方法的具体实现由子类完成,该方法称为抽象方法,包含抽象方法的类就是抽象类: 2.如,shape类计算周长和面积的方法无法确定,那么就可以将这样 ...
- CSPS模拟 44
状态不是很好吧 这套和前边是一套的, skyh在我旁边AK,好像开了三个对拍又在拼小人 T3 正解没调出来,暴力又忘交了qwq 当时心情都要爆炸了 T1 区间$gcd$乘区间长度的最大值 暴力是$n^ ...
- NOIP模拟 16
嗯我已经是个不折不扣的大辣鸡了 上次的T3就弃了,这次又弃 颓废到天际 T1 巨贪贪心算法 我就是一个只会背板子的大辣鸡 全裸的贪心看不出来,只会打板子 打板子,加特判,然后一无进展,原题不会,这就是 ...