Python 编程语言要掌握的技能之一:善用变量来改善代码质量

如何为变量起名

在计算机科学领域,有一句著名的格言(俏皮话):html

There are only two hard things in Computer Science: cache invalidation and naming things. 在计算机科学领域只有两件难事:缓存过时 和 给东西起名字python

— Phil Karltondjango

第一个『缓存过时问题』的难度不用多说,任何用过缓存的人都会懂。至于第二个『给东西起名字』这事的难度,我也是深有体会。在个人职业生涯里,度过的做为黑暗的下午之一,就是坐在显示器前抓耳挠腮为一个新项目起一个合适的名字。编程

编程时起的最多的名字,还数各类变量。给变量起一个好名字很重要,由于好的变量命名能够极大的提升代码总体可读性。数组

下面几点,是我总结的为变量起名时,最好遵照的基本原则。缓存

1. 变量名要有描述性,不能太宽泛

可接受的长度范围内,变量名能把它所指向的内容描述的越精确越好。因此,尽可能不要用那些过于宽泛的词来做为你的变量名:app

· 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、users一、 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技术持续更新(附教程)

相关文章
相关标签/搜索