避免条件语句的智慧

2024-08-14 0 758

避免条件语句的智慧

循环复杂度是衡量代码复杂性和混乱程度的指标。

高圈复杂度并不是一件好事,恰恰相反。

简单来说,圈复杂度与程序中可能的执行路径的数量成正比。换句话说,圈复杂度和条件语句的总数(尤其是它们的嵌套)密切相关。

所以今天我们来谈谈条件语句。

反如果

2007年,francesco cirillo发起了一场名为anti-if的运动。

francesco cirillo 是发明番茄工作法的人。我现在正在“番茄钟下”写这篇博文。

我想我们都很快从它的名字就明白了这个活动的意义。有趣的是,该运动的追随者中有不少计算机科学家。

他们的论点坚如磐石——如果语句是邪恶的,会导致程序执行路径呈指数级增长。

简而言之,这就是圈复杂度。它越高,不仅阅读和理解代码就越困难,而且用测试来覆盖它也就越困难。

当然,我们有一种“相反”的指标——代码覆盖率,它显示了测试覆盖了多少代码。但是这个指标以及我们编程语言中用于检查覆盖率的丰富工具是否证明忽略圈复杂度并仅基于“本能”散布 if 语句是合理的?

我觉得不是。


几乎每次我发现自己要将一个 if 嵌套在另一个 if 中时,我都会意识到我正在做一些非常愚蠢的事情,可以用不同的方式重写 – 要么没有嵌套的 if ,要么根本没有 if 。

你确实注意到了“几乎”这个词,对吧?

我并没有立即开始注意到这一点。如果您查看我的 Github,您会发现不止一个旧代码示例,它们不仅具有高圈复杂度,而且具有直接的圈复杂度。

是什么帮助我更加意识到这个问题?可能是我一年前学到和接受的经验和一些聪明的事情。这就是我今天要跟大家分享的内容。


破坏 if 语句的两种神圣技术

  1. padawan,将未知值的每个条件检查移动到该值已知的地方。
  2. 学徒,改变你的编码逻辑思维模型,让它不再需要条件检查。

1. 让未知为人所知

在我们还不“知道”某事时检查它可能是使用基于“本能”的条件语句的最常见来源。

例如,假设我们需要根据用户的年龄做一些事情,并且我们必须确保年龄有效(在合理范围内)。我们最终可能会得到这样的代码:

1

2

3

4

5

6

7

from typing import optional

def process_age(age: optional[int]) -> none:

    if age is none:

        raise valueerror("age cannot be null")

    if age  150:

        raise valueerror("age must be between 0 and 150")

我们都见过并且可能写过数百次类似的代码。

我们如何通过遵循所讨论的元原则来消除这些条件检查?

在我们特定的年龄情况下,我们可以应用我最喜欢的方法——摆脱原始的痴迷,转向使用自定义数据类型。

1

2

3

4

5

6

7

8

9

10

11

class age:

    def __init__(self, value: int) -> none:

        if value  150:

            raise valueerror("age must be between 0 and 150")

        self.value = value

    def get_value(self) -> int:

        return self.value

def process_age(age: age) -> none:

    # age is guaranteed to be valid, process it directly

万岁,少一个如果!年龄的验证验证现在始终在“已知年龄的地方”——在单独类的职责和范围内。

如果我们想删除 age 类中的 if ,我们可以走得更远/不同,也许可以使用带有验证器的 pydantic 模型,甚至用断言替换 if — 现在没关系。


有助于摆脱同一元思想中的条件检查的其他技术或机制包括用多态性(或匿名 lambda 函数)替换条件以及分解具有偷偷摸摸的布尔标志的函数等方法。

例如,这段代码(可怕的拳击,对吧?):

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

class paymentprocessor:

    def process_payment(self, payment_type: str, amount: float) -> str:

        if payment_type == "credit_card":

            return self.process_credit_card_payment(amount)

        elif payment_type == "paypal":

            return self.process_paypal_payment(amount)

        elif payment_type == "bank_transfer":

            return self.process_bank_transfer_payment(amount)

        else:

            raise valueerror("unknown payment type")

    def process_credit_card_payment(self, amount: float) -> str:

        return f"processed credit card payment of {amount}."

    def process_paypal_payment(self, amount: float) -> str:

        return f"processed paypal payment of {amount}."

    def process_bank_transfer_payment(self, amount: float) -> str:

        return f"processed bank transfer payment of {amount}."

如果你用 match/case 替换 if/elif 也没关系——都是同样的垃圾!

很容易将其重写为:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

from abc import abc, abstractmethod

class paymentprocessor(abc):

    @abstractmethod

    def process_payment(self, amount: float) -> str:

        pass

class creditcardpaymentprocessor(paymentprocessor):

    def process_payment(self, amount: float) -> str:

        return f"processed credit card payment of {amount}."

class paypalpaymentprocessor(paymentprocessor):

    def process_payment(self, amount: float) -> str:

        return f"processed paypal payment of {amount}."

class banktransferpaymentprocessor(paymentprocessor):

    def process_payment(self, amount: float) -> str:

        return f"processed bank transfer payment of {amount}."

对吗?


将带有布尔标志的函数分解为两个独立函数的示例与时间一样古老,令人痛苦地熟悉,并且令人难以置信地烦人(在我看来)。

1

2

3

4

5

6

7

8

9

def process_transaction(transaction_id: int,

                                                amount: float,

                                                is_internal: bool) -> none:

    if is_internal:

        # process internal transaction

        pass

    else:

        # process external transaction

        pass

无论如何,两个函数会好得多,即使其中 2/3 的代码是相同的!这是其中一种场景,其中与 dry 的权衡是常识的结果,使代码变得更好。


这里最大的区别是,从机械角度来说,在自动驾驶仪上,我们不太可能使用这些方法,除非我们已经内化并养成了通过这一原则进行思考的习惯

否则,我们会自动陷入 if: if: elif: if…

2. 释放你的思想,尼奥

事实上,第二个技巧才是真正的技巧,而之前的“第一个”技巧只是准备练习,是到位的捷径🙂

事实上,实现更简单的代码、降低圈复杂度和减少条件检查的唯一最终方式、方法(无论你怎么称呼它)是改变我们在头脑中建立的用于解决特定问题的心理模型 .

我保证,今天最后一个愚蠢的例子。

考虑一下,我们正在紧急为一些在线商店编写一个后端,用户可以在不注册或使用注册的情况下进行购买。

当然,系统有一个 user 类/实体,完成这样的事情很容易:

1

2

3

4

5

6

7

8

def process_order(order_id: int,

                                  user: optional[user]) -> none:

    if user is not none:

        # process order for a registered user

       pass

    else:

        # process order for a guest user

           pass

但是注意到这些废话,由于我们的思维已经转向正确的方向(我相信),我们将回到 user 类的定义位置并重写部分代码,如下所示:

1

2

3

4

5

6

7

8

9

10

11

12

13

class User:

    def __init__(self, name: str) -> None:

        self.name = name

    def process_order(self, order_id: int) -> None:

        pass

class GuestUser(User):

    def __init__(self) -> None:

        super().__init__(name="Guest")

    def process_order(self, order_id: int) -> None:

        pass


因此,这一切的本质和美妙之处在于,我们不会用各种模式和编码技术来消除条件语句等,从而使我们的头脑变得混乱。

通过将我们的注意力转移到元级别,转移到更高的抽象级别,而不仅仅是对代码行的推理级别,并遵循我们今天讨论的想法,消除条件检查的正确方法,并且一般来说,更正确的代码将会自然出现.


我们代码中的很多条件检查都是由于被诅咒的 none/null 泄漏到我们的代码中而产生的,所以值得一提的是非常流行的 null 对象模式。

执着于文字,而不是意义

当遵循反如果时,你可能会走上错误的道路,因为执着于文字而不是意义,并盲目地遵循“如果是坏的,如果必须被删除”的想法。

由于条件语句是语义而不是语法元素,因此有无数种方法可以从代码中删除 if 标记,而无需更改我们喜爱的编程语言中的底层逻辑

我在这里讨论的不是用 match/case 替换 Python 中的 elif 链。

逻辑条件源于系统的心理“模型”,并且没有通用方法可以完全“删除”条件。

换句话说,圈复杂度和整体代码复杂度与代码的物理表示(文件中写入的字母和符号)无关。

复杂性来自于形式表达口头或文字解释特定代码为何以及如何工作

因此,如果我们更改代码中的某些内容,并且 if 语句更少或根本没有,但相同代码的口头解释保持不变,我们所做的只是更改代码的表示,并且更改本身并没有真正的意义或做出任何改进。

收藏 (0) 打赏

感谢您的支持,我会继续努力的!

打开微信/支付宝扫一扫,即可进行扫码打赏哦,分享从这里开始,精彩与您同在
点赞 (0)

免责声明
1. 本站所有资源来源于用户上传和网络等,如有侵权请邮件联系本站整改team@lcwl.fun!
2. 分享目的仅供大家学习和交流,您必须在下载后24小时内删除!
3. 不得使用于非法商业用途,不得违反国家法律。否则后果自负!
4. 本站提供的源码、模板、插件等等其他资源,都不包含技术服务请大家谅解!
5. 如有链接无法下载、失效或广告,请联系本站工作人员处理!
6. 本站资源售价或VIP只是赞助,收取费用仅维持本站的日常运营所需!
7. 如遇到加密压缩包,请使用WINRAR解压,如遇到无法解压的请联系管理员!
8. 因人力时间成本问题,部分源码未能详细测试(解密),不能分辨部分源码是病毒还是误报,所以没有进行任何修改,大家使用前请进行甄别!
9.本站所有源码资源都是经过本站工作人员人工亲测可搭建的,保证每个源码都可以正常搭建,但不保证源码内功能都完全可用,源码属于可复制的产品,无任何理由退款!

网站搭建学习网 Python 避免条件语句的智慧 https://www.xuezuoweb.com/13038.html

常见问题
  • 本站所有的源码都是经过平台人工部署搭建测试过可用的
查看详情
  • 购买源码资源时购买了带主机的套餐是指可以享受源码和所选套餐型号的主机两个产品,在本站套餐里开通主机可享优惠,最高免费使用主机
查看详情

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务

Fa快捷助手
手机编程软件开发

在手机上用手点一点就能轻松做软件

去做软件
链未云主机
免备案香港云主机

开通主机就送域名的免备案香港云主机

去使用
链未云服务器
免备案香港云服务器

支持售后、超低价、稳定的免备案香港云服务器

去使用