什么是“工匠”?
我一直觉得编程在某种意义上是一门“手艺”,因为优雅高效的代码就像完美的手工艺品一样赏心悦目。在雕刻代码的过程中,有很大的项目:比如应该使用什么架构和设计模式。还有更多的小细节,比如何时使用(),或者如何命名变量。那些真正优秀的代码是由无数优秀的细节创造出来的。 《工匠》这个系列文章是我的一个小小的尝试。重点分享编程中的一些“小”事。希望能够对编程路上的每一位工匠有所帮助。
系列文章:
关联:
关联:
前言
编写条件分支代码是编码过程的一个组成部分。如果我们用道路来比喻,现实世界中的代码从来都不是一条笔直的高速公路,而更像是由无数岔路组成的城市地图。我们程序员就像司机一样,我们需要告诉程序在下一个路口是否需要向左走还是向右走。编写良好的条件分支代码很重要,因为糟糕、复杂的分支处理很容易让人们感到困惑并降低代码质量。因此,本文将重点讨论在 .
分支代码在
它支持最常见的 if/else 条件分支语句,但缺少其他编程语言中常见的 /case 语句。此外,还为for/while循环和try/语句提供了else分支,这在一些特殊场景下很有用。下面我将从最佳实践、常用技巧、常见陷阱三个方面来谈谈如何编写优秀的条件分支代码。
最佳实践 1. 避免多级分支嵌套
如果这篇文章只能精简为一句话,那么那句话一定是“尽力避免分支嵌套”。分支嵌套太深是许多新手程序员最常犯的错误之一。如果新手程序员写了很多层分支嵌套,你可能会看到一层又一层的花括号:if { if { if { ... }}}。俗称“嵌套如果地狱(If Hell)”。但是,由于使用缩进而不是 {},因此分支嵌套太深会比其他语言产生更严重的后果。例如,太多的缩进级别很容易导致代码超出 PEP8 中指定的每行字数限制。我们看一下这段代码:
def(书呆子,商店):
》》》去水果店买苹果
- 首先检查商店是否营业
- 如果有苹果,就买一个
- 如果你没有足够的钱,就回家拿点钱然后再回来
”“”
.():
。(“苹果”):
.(store.price("苹果",=1)):
nerd.buy(商店,"苹果",=1)
别的:
书呆子.y()
(书呆子,商店)
别的:
提高(“商店里没有苹果!”)
别的:
提高(“商店是!”)
上面代码最大的问题在于,它对原始条件分支需求的翻译过于直接,导致短短十几行代码就出现了三层嵌套分支。这样的代码可读性和可维护性都很差。然而,我们可以使用一个非常简单的技巧:“提前结束”来优化这段代码:
def(书呆子,商店):
.():
提高(“商店是!”)
。(“苹果”):
提高(“商店里没有苹果!”)
.(store.price("苹果",=1)):
nerd.buy(商店,"苹果",=1)
别的:
书呆子.y()
(书呆子,商店)
“提前终止”是指:在函数内使用raise或raise等语句提前结束分支中的函数。例如,在new函数中,当不满足分支条件时,我们直接抛出异常并结束代码分支。这样的代码没有嵌套分支,更直接、更容易阅读。
2.封装那些过于复杂的逻辑判断
如果条件分支中的表达式太复杂,有太多的not/and/or,那么这段代码的可读性就会大大降低,比如下面的代码:
# 如果活动仍在进行且活动剩余名额大于10,则所有性别均为女性,或者等级大于3
#活跃用户将获得10,000金币
。 。 > 10 和\
用户。 and(user.sex == ''.level > 3):
用户。(10000)
对于这样的代码,我们可以考虑将具体的分支逻辑封装成函数或者方法来简化代码:
.().tion():
用户。(10000)
事实上,重写代码后,原来的注释文字其实是可以去掉的。因为下面的代码已经达到了自解释的目的。至于具体哪些用户符合活动条件?这类问题应该通过具体的()方法来回答。
提示:适当的封装不仅直接提高了代码的可读性,事实上,如果上述活动判断逻辑在代码中出现不止一次,封装就更有必要了。否则,重复代码会极大地损害这个逻辑的可维护性。
3.注意不同分支下的重复代码
重复代码是代码质量的天敌,条件分支语句很容易成为重复代码的重灾区。因此,我们在编写条件分支语句时,需要特别注意,不要产生不必要的重复代码。让我们看一下这个例子:
# 对于新用户,创建新的用户配置文件,否则更新旧的配置文件
.:
(
=用户.,
电子邮件=用户.电子邮件,
年龄=用户.年龄,
=用户.,
# 对于新用户,将用户积分设置为0
=0,
=现在(),
别的:
(
=用户.,
电子邮件=用户.电子邮件,
年龄=用户.年龄,
=用户.,
=现在(),
在上面的代码中,我们一眼就可以看出,在不同的分支下,程序调用了不同的函数,做了不同的事情。但由于重复代码的存在,我们很难简单地区分两者的区别。事实上,得益于 的动态特性,我们可以简单地重写上面的代码,这样可读性就可以得到明显的提升:
.:
=
= {'': 0,'': 现在()}
别的:
=
= {'': 现在()}
(
=用户.,
电子邮件=用户.电子邮件,
年龄=用户.年龄,
=用户.,
**
当您编写分支代码时,请特别注意分支创建的重复代码块。如果你能简单地消除它们,请不要犹豫。
4.谨慎使用三元表达式
三元表达式语法仅在 2.5 版本之后支持。在此之前,社区一度认为三元表达式是不必要的,我们需要使用 x 和 a 或 b 来模拟它。 [注意]事实是,在很多情况下,使用普通的 if/else 语句确实使代码更具可读性。盲目追求三元表达式很容易诱惑你编写复杂、可读性差的代码。因此,请记住仅对简单逻辑分支使用三元表达式。
= "" if you.favor("") else ""
对于大多数情况,使用普通的 if/else 语句。
常见技巧1.使用“德摩根定律”
在进行分支判断时,我们有时会写这样的代码:
# 如果用户未登录或者用户没有使用,则拒绝提供服务
。 .:
“我们的仅供用户使用”
当你第一次看到代码的时候,是不是需要思考一会儿才能明白它想要做什么?这是因为上面的逻辑表达式中有2个not和1个or。而我们人类恰好不善于处理太多的“否定”和“或”逻辑关系。这个时候,德摩根定律就该出场了。通俗地说,德摩根定律就是not A或not B等价于not(A和B)。通过这样的转换,上面的代码可以改写如下:
如果不是(用户..):
“我们的服务仅对用户开放”
怎么样?代码是不是更容易阅读了?请记住德摩根定律,该定律对于简化条件分支中的代码逻辑通常非常有用。
2. 自定义对象的“布尔真假”
我们常说,在互联网中,“万物皆对象”。事实上,不仅仅是“一切皆对象”,我们还可以使用很多神奇的方法(文档中称为:user-)来定制对象的各种行为。我们可以通过许多神奇的方式影响代码的执行,而这在其他语言中是不可能做到的。例如,所有对象都有自己的“布尔真或假”:
通过内置函数 bool(),您可以轻松检查对象的布尔值 true 或 false。在进行条件分支判断时也会使用该值:
>>> 布尔(())
真的
重要的是,尽管所有用户类实例的布尔值都是 true。但有一种方法可以改变这种行为:自定义类的魔术方法(2.X 版本中)。当类定义方法时,其返回值将被视为类实例的布尔值。此外,布尔值并不是影响实例真假的唯一方法。如果类没有定义方法,它也会尝试调用该方法(即对任意序列对象调用len函数),通过结果是否为0来判断实例是true还是false。那么什么是这个功能的使用?看一下下面的代码:
():
def(自身,用户):
自己。 = 用户
用户 = ([,])
iflen(用户。) > 0:
print("有一些用户在!")
上面的代码中,users的长度。用于判断是否有内容。事实上,上面的分支可以通过添加魔术方法来变得更简单:
:
def(自身,用户):
自己。 = 用户
定义(自我):
(自己。)
用户 = ([,])
# 定义方法后,可以利用对象本身进行布尔判断
:
print("有一些用户在!")
通过定义魔术方法 and ,我们可以让类控制我们想要表达的布尔 true 和 false 值,使代码更加简洁。
3.条件判断中使用all()/any()
all()和any()这两个函数非常适合用在条件判断中。这两个函数接受一个可迭代对象并返回一个布尔值,其中:
假设我们有以下代码:
定义():
"""仅当序列中的所有数字都大于 10 时才返回 True
”“”
:
:
干扰素)
简单高效且不牺牲可用性。
4.在try/while/for中使用else分支
我们来看看这个函数:
定义():
d = 假
尝试:
()
d=真
酶:
print("错误时")
# 只有成功完成才做第二件事
sed:
事物()
在函数中,我们希望只有()调用成功(即不抛出异常),才能继续进行第二次函数调用。为此,我们需要定义一个附加变量 d 作为标记。其实我们可以用更简单的方法来达到同样的效果:
定义():
尝试:
()
酶:
print("错误时")
别的:
事物()
else分支追加到try语句块的末尾后,只有当try下的所有语句都正常执行后(即不存在异常、no、break等)后,才会执行该分支下的() 。同样,for/while循环中也支持添加else分支,这意味着只有当循环使用的迭代对象正常耗尽,或者while循环使用的条件变量变为False时,才会执行else分支下的代码。
常见陷阱 1. 与 None 值比较
在 中,有两种比较变量的方式: == 和 is,它们在含义上有根本的区别:
None 是语言中的单例对象。如果要判断一个变量是否为None,记得使用is而不是==,因为只有is才能表达严格意义上的变量是否为None。否则,可能会出现以下情况:
>>> ():
... def(自己,其他):
...
...
>>> foo = Foo()
>>> foo == 无
真的
上面的代码中,Foo类通过自定义magic方法,轻松满足条件== None。所以,当你想判断一个变量是否为None时,使用is而不是==。
2、注意and和or的运算优先级
看下面两个表达式。猜猜它们是否具有相同的值?
>>> ()
>>>
答案是:不是,它们的值分别是False和True。你猜对了吗?问题的关键是:and运算符的优先级大于or。所以上面的第二个表达式实际上看起来是 True 或 (False and False)。所以结果是 True 而不是 False。在编写包含多个and 和or 的表达式时,请特别注意and 和or 的运算优先级。即使执行优先级正是您所需要的,您也可以添加额外的括号以使代码更清晰。
结论
以上是“工匠”系列文章的第二篇。不知道文章的内容是否合你的胃口。代码中的分支语句是不可避免的。我们在写代码的时候,需要特别注意它的可读性,避免给其他看到代码的人带来麻烦。看完文章,你有什么想吐槽的吗?请留言告诉我。
注解
事实上,x 和 a 或 b 并不总是给出正确的结果。这个表达式只有当a和b的布尔值为true时才能正常工作。这是由逻辑运算的短路特性决定的。您可以在命令行上尝试 True 和 None 或 0,结果将是 0 而不是 None。
扫一扫在手机端查看
我们凭借多年的网站建设经验,坚持以“帮助中小企业实现网络营销化”为宗旨,累计为4000多家客户提供品质建站服务,得到了客户的一致好评。如果您有网站建设、网站改版、域名注册、主机空间、手机网站建设、网站备案等方面的需求,请立即点击咨询我们或拨打咨询热线: 13761152229,我们会详细为你一一解答你心中的疑难。