Python 编程技巧

在开发解决方案时,我们倾向于将复杂的实际问题提炼为更小、更易于管理的子问题,然后使用函数来解决这些问题。函数是冗余代码的克星,也是我们抵御代码复杂性的最强防线。

大多数函数在编写过程中,关键是其返回值。函数产生结果的方式会极大地影响用户调用函数时的体验。掌握设计优雅返回结果的函数的艺术是制作高质量函数的基础。

不要返回多种类型

Python 是如此的灵活,以至于我们可以很容易地做到在其他语言中很难做到的事情。例如:让函数同时返回不同类型的结果。就像下面这样:

def get_users(user_id=None):if user_id is not None:return User.get(user_id)else:return User.filter(is_active=True)# Return single user
get_users(user_id=1)
# Return all users
get_users()

在上面的代码片段中,当我们需要获取单个用户时,我们传递一个 "user_id " 参数,否则,如果我们传递一个 None 值,它将返回所有活跃用户的列表。乍一看,这种设计似乎很合理。

但是,编写类似功能强大的函数并不是一件好事。这是因为优秀的函数必须具有 单一职责。所谓 “单一职责”,是指一个函数只做好一件事,而且目的明确。这样的函数将来也不太可能随着需求的变化而修改,同时也非常方便编写单元测试。

返回多种类型的函数违反了 "单一职责 "原则。一个好的函数应始终提供一个稳定的返回值,以尽量减少调用者的处理成本。就像上面的例子,我们应该编写两个独立的函数 get_active_users()get_user_by_id(user_id)

使用类型提示定义返回类型

使用类型提示定义返回类型和显式参数声明。这样,集成开发环境就能帮助您进行自动补全和类型检查,从而在编辑的时候就发现错误。

例如:

def say_hello(name: str) -> str:return "Hello, " + name

-> 语法表示 say_hello() 函数将返回一个字符串。

使用部分函数构造新函数

假设在这种情况下,您的代码中有一个带有很多参数的函数 A,它非常适用。另一个函数 B 调用 A 做一些工作,就像下面这样:

def add(x, y):return x + ydef sum(value):# Calling addreturn add(100, value)

在上述示例中,我们可以使用 functools 模块中的 partial() 函数来简化它。

import functoolssum = functools.partial(add, 100)
sum(200)  # Output is 300

partial(func,*args,**kwargs) 以传入的函数为基础,使用变量参数构造一个新函数。在合并当前调用参数和构造参数后,对新函数的所有调用都将委托给原始函数。

因此,在使用部分函数时,可以将上面的求和函数定义修改为单行表达式,这样会更加简洁和直接。

抛出异常而不是返回错误

有时,您可能需要编写同时返回结果和错误信息的函数:

def create_user(name):if len(name) > MAX_LENGTH_OF_NAME:return None, 'name of user is too long'if len(CURRENT_USERS) > MAX_USERS_QUOTA:return None, 'too many users'return User(name=name), ''def create_from_input():name = input()user, err_msg = create_user(name)if err_msg:print(f'create user failed: {err_msg}')else:print(f'user<{name}> created')

在上例中,create_user 函数的作用是创建一个新的用户对象。同时,为了在发生错误时向调用者提供错误详细信息,它利用了多返回值特性,将错误信息作为第二个结果返回。

但在 Python 中,这并不是解决此类问题的最佳方法。因为这种做法会增加调用者处理错误的成本,尤其是当许多函数都遵循这种规范,并且存在多层调用时。

在这种情况下,使用异常来处理错误过程是更习以为常的做法。因此,上述代码可以重写为:

class CreateUserError(Exception):"""Exception for user creation failure"""passdef create_user(name):"""Create new user:raises: CreateUserError"""if len(name) > MAX_LENGTH_OF_NAME:raise CreateUserError('name of user is too long')if len(CURRENT_USERS) > MAX_USERS_QUOTA:raise CreateUserError('Too many users')return User(name=name)def create_for_input():name = input()try:user = create_user(name)except CreateUserError as e:print(f'create user failed: {e}')else:print(f'user<{name}> created')

在使用抛出异常而不是返回结果、错误信息后,整个错误处理过程乍一看变化不大,但实际上在一些细节上有很大不同:

  • 新版函数的返回值类型更加稳定,它将始终只返回用户类型或抛出异常。
  • 异常与返回值的不同之处在于,异常在被捕获之前会不断向调用栈的上层报告。因此,create_user 的一级调用者可以完全省略异常处理,将其留给上层处理。

尽量少返回 None

None 常被用来表示应该存在但缺少的东西,在 Python 中是独一无二的。由于 None 独特的虚无主义特质,它经常被用作函数返回值。

当我们使用 None 作为函数的返回值时,通常有以下三种情况。

  • 作为操作类函数的默认返回值:当操作类函数不需要任何返回值时,通常会返回 None。此外,对于没有任何返回语句的函数,None 也是默认返回值。例如,list.append()
  • 作为某个可能不存在的预期值:在 Python 标准库中,正则表达式模块下的函数 re 就属于这一类。例如,re.searchre.match
  • 作为代表错误结果的值:有时,当函数调用失败时,我们经常使用 None 作为默认返回值。如果是这种情况,请确保您的函数名称更有意义,例如 create_user_or_none()

限制递归的使用

当函数返回调用自身时,这就是递归。递归在某些情况下是非常有用的编程技巧,但 Python 对递归的支持非常有限。

Python 语言不支持尾部递归优化。此外,Python 对递归级别的最大数量也有严格限制。所以要尽可能少写递归。如果您想用递归来解决问题,首先要考虑是否可以用循环来轻松替代递归。如果答案是肯定的,那就用循环重写。如果绝对必须使用递归,请考虑以下几点:

  • 确保递归层小于 sys.getrecursionlimit()
  • 尽可能使用缓存,例如 functools.lru_cache

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653364.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

邻接矩阵、关联矩阵

邻接矩阵&#xff1a; 邻接矩阵是一种用来表示图中顶点间相互连接关系的矩阵。在邻接矩阵中&#xff0c;矩阵的行和列都代表图中的顶点。 对于无权图&#xff0c;如果顶点 i 和顶点 j 之间有一条边&#xff0c;则矩阵中的元素 Aij​&#xff08;位于第 i 行和第 j 列&#xff…

编译Opencv3.3 版本遇到的Cuda版本变更导致:CUDA_nppicom_LIBRARY (ADVANCED)链接找不到的问题根本解法:

前言&#xff1a; Opencv 开源库的使用是必须的&#xff0c;但是&#xff0c;开源项目的特性&#xff0c;造成&#xff0c;版本的依赖性比较复杂&#xff0c; 尤其是针对某一款老硬件的SDK&#xff0c;往往随着某个开源库的使用&#xff0c;导致&#xff0c;无法编译的问题&am…

有一段时间没更新了

OK呀兄弟们&#xff0c;也是好久没更新了&#xff0c;这不是二级学完了&#xff0c;准备搞二级了吗&#xff0c;所以这两天我在做二级的往期考题&#xff0c;没发博客&#xff0c;请见谅&#xff0c;如果你们也在为等级考试做准备&#xff0c;那不妨去看看吧&#xff0c;往期考…

(bean的创建图)学习Spring的第十天(很重要)

大致框架按如下 第一次细分 bean对象还未创建 操作第一个map 引入BeanFactoryPostProcessor , 即Bean工厂后处理器 , 为Spring很重要的扩展点 BeanFactoryPostProcessor内部的方法 可以对BeaDefinition进行修改 , 也可进行BeanDefinition的注册 ( 原有在xml文件配置的bean…

AI之DL:人工智能领域—深度学习的发展历程之深度学习爆发的三大因素、探究DL为什么耗算力

AI之DL:人工智能领域—深度学习的发展历程之深度学习爆发的三大因素、探究DL为什么耗算力 目录 深度学习的发展历程之深度学习爆发的三大因素

从零学习Linux操作系统 第二十部分 mariadb数据库的管理

一、对于数据库的基本介绍 1.什么是数据库 数据库就是个高级的表格软件 2.常见数据库 Mysql Oracle mongodb db2 sqlite sqlserver … 3.Mysql (SUN -----> Oracle) 4.mariadb (Mysql的一种&#xff09; 数据库中的常用名词 1.字段 &#xff1a;表格中的表头 2.表 &…

Day01_变量和数据类型(注释,关键字,标识符,数据类型,字面量,变量,常量,进制,计算机存储单位,Java的基本数据类型的存储范围,计算机如何表示数据)

文章目录 JavaSE_Day01 变量和数据类型学习目标1.1 注释&#xff08;*comment*&#xff09;&#xff08;掌握&#xff09;1.2 关键字&#xff08;*keyword*&#xff09;&#xff08;掌握&#xff09;1.3 标识符( identifier)&#xff08;掌握&#xff09;1.3.1 标识符的命名规则…

构造器模式

构造器模式 意图 将一个复杂对象的构建和表示分离&#xff0c;使得相同的构建能创建不同的表示。 解释 案例&#xff1a;想象一个角色扮演游戏的特征生成器。最简单的选择是让计算机为你创建角色。如果你想手动选择特征的细节像职业、性别、头发的颜色等。特征的产生是一个循…

js实现动漫拼图1.0版

文章目录 1 实现效果视频2 功能实现思路3代码实现 1 实现效果视频 拼图1.0 2 功能实现思路 布局忽略&#xff08;小白学前端&#xff0c;不献丑了&#xff09; 左侧拼图格 左侧4*4的拼图小格子 利用表格实现&#xff0c;规划好td的大小&#xff0c;给每个格子加上背景图片&…

Flink问题解决及性能调优-【Flink不同并行度引起sink2es报错问题】

最近需求&#xff0c;仅想提高sink2es的qps&#xff0c;所以仅调节了sink2es的并行度&#xff0c;但在调节不同算子并行度时遇到一些问题&#xff0c;找出问题的根本原因解决问题&#xff0c;并分析整理。 实例代码 --SET table.exec.state.ttl86400s; --24 hour,默认: 0 ms …

MySQL查询—联合查询、子查询

关于表格的创建&#xff0c;请看上一篇文章——MySQL查询—连接查询 1、联合查询&#xff1a;把多次查询的结果合并&#xff0c;形成一共新的查询集。 UNION&#xff0c;UNION ALL 语法&#xff1a; SELECT 字段列表 FROM 表&#xff21;&#xff0e;&#xff0e;&#…

【MySQL】MySQL内置函数--日期函数/字符串函数/数学函数/其他相关函数

文章目录 1.日期函数2.字符串函数3.数学函数4.其它函数 1.日期函数 MySQL中内置了一下函数&#xff1a; 函数名称描述current_date()当前日期current_time()当前时间current_timestamp()当前时间戳date(datetime)返回datetime参数的日期部分date_add(date,interval d_value_t…

qt-C++笔记之使用信号和槽实现跨类成员变量同步响应

qt-C笔记之使用信号和槽实现跨类成员变量同步响应 —— 杭州 2024-01-24 code review! 文章目录 qt-C笔记之使用信号和槽实现跨类成员变量同步响应1.运行2.main.cpp3.test.pro4.编译 1.运行 2.main.cpp 代码 #include <QCoreApplication> #include <QObject> #…

Linux下Docker搭建部署Typecho博客【详细版】

Linux下Docker搭建部署Typecho博客【详细版】 一、环境准备1.1.准备阿里云服务器【新用户免费使用三个月】1.2.准备远程工具【FinalShell】1.3.系统信息1.4.安装所需软件包1.5.设置docker镜像源1.6.更新yum软件包索引1.7.确认停用selinux 二、安装Docker2.1.安装Docker-Ce2.2.查…

【RTP】webrtc 学习3: webrtc对h264的rtp解包

rtp_rtcp\source\video_rtp_depacketizer_h264.cc【RTP】webrtc 学习2: webrtc对h264的rtp打包 中分析了打包过程的代码,这样再来看解析过程的源码就容易多了:本代码主要基于m79,m98类似。这里注明了jitterbuffer 会再次 做 解析stap-a 变为NAL units解析ParseFuaNalu 第一…

ACL、VLAN、NAT笔记

一、ACL ---访问控制列表 1.ACL的作用 1&#xff0c;访问控制&#xff1a;在路由器流量流入或流出的接口上&#xff0c;匹配流量&#xff0c;然后 执行设定好的动作。 ---- permit 允许 , deny 拒绝 2&#xff0c;抓取感兴趣流&#xff1a;ACL可以和其他服务结合使用。ACL只…

MyBatis 如何整合 Druid 连接池?

Mybatis 如何整合 Druid 数据连接池呢&#xff1f;首先打开创建的 Maven 工程&#xff0c;找到 pom.xml 文件&#xff0c;添加 Druid 依赖。 <!--druid连接池--> <dependency><groupId>com.alibaba</groupId><artifactId>druid</artifactId&…

【C语言】数组的应用:三子棋游戏

由于代码较长&#xff0c;为了增加可读性&#xff0c;我们把代码分别写到game.h&#xff0c;game.c&#xff0c;test.c&#xff0c;里面&#xff0c;其中game.h用来声明函数&#xff0c;实现函数功能的代码在game.c&#xff0c;测试游戏的代码在test.c 为了方便后续的更改&…

ThreadLocal学习笔记

ThreadLocal类图 ThreadLocal/InheritableThreadLocal/ \TransmittableThreadLocal(阿里巴巴) TransmissibleThreadLocal(阿里巴巴)ThreadLocal 这是Thread类的局部变量&#xff0c;每个线程私有。 它主要用于解决多线程中的数据共享问题&#xff0c;保…

k8s 版本发布与回滚

一、实验环境准备&#xff1a; kubectl get pods -o wide kubectl get nodes -o wide kubectl get svc 准备两个nginx镜像&#xff0c;版本号一个是V3&#xff0c;一个是V4 二、准备一个nginx.yaml文件 apiVersion: apps/v1 kind: Deployment metadata:name: nginx-deploylab…