引言
从今天开始,我们将开始写一个新的栏目,主要内容是读书的一些笔记。今天要阅读的是《「Python工匠:案例、技巧与工程实践」》中的 「第七章函数-基础知识 」。有兴趣的同学,一起参与进来吧。
「内容简介」
本书基于广受好评的“Python工匠”系列开源文章。全书从工程实践角度出发,通过剖析核心知识、展示典型案例与总结实用技巧,帮助大家系统进阶Python,写好工程代码,做好实践项目。
本书共计13章,分为五大部分:「变量与基础类型、语法结构、函数与装饰器、面向对象编程、总结与延伸,涵盖Python编程的方方面面。」本书的写作方式别具一格,核心知识点都会通过三大板块来阐述:基础知识、案例故事、编程建议。其中基础知识帮助大家快速回顾Python基础;案例故事由作者经历的编程项目与案例改编而来,兼具实战性与趣味性;编程建议以大家喜闻乐见的条目式知识点呈现,短小精悍,可直接应用于自己的编程实践中。
函数的常用技巧
01.别将可变类型作为参数默认值
这样的函数定义,看起来没有什么问题,但当你多次调用就会发现和你预期的不同了:
def append_value(value,items=[]):
items.append(value)
return items
print(append_value("fool"))
print(append_value("cool"))
运行代码发现,第一次调用,值为“['fool']”
,第二次调用返回的是:“['fool', 'cool']”
。之所以出现这样的问题是因为 「python函数的参数默认值在函数定义的阶段被创建一次,之后不管调用的次数为多少,函数内拿到的默认值都是同一个对象。」
通过__defaults__
属性可以直接获取函数的参数默认值
print(append_value.__defaults__[0])
修改这个参数的默认值,可以直接影响函数的调用结果。
append_value.__defaults__[0].append('baz')
print(append_value('value'))
['fool', 'cool', 'baz', 'value']
为了规避这个问题,使用None
来替代可变类型默认值是比较常见的做法:
def append_value(value, items=None):
if items is None:
items = []
items.append(value)
return item
这样修改后,假如调用方没有提供items
参数,函数每次都会构建一个新列表,不会再出现之前的问题。
02.定义特殊对象来区分是否提供了默认参数
当我们为函数参数设置了默认值,不强制要求调用方提供这些参数以后,会引入另一件麻烦事儿:无法严格区分调用方是不是真的提供了这个默认参数。
def dump_value(value,extra=None):
if extra is None:
...
# 两种调用方式
dump_value("one")
dump_value("two",extra=None)
对于dump_value()
函数来说,当调用方使用上面两种方式来调用它时,它其实无法分辨。因为在这两种情况下,函数内拿到的extra
参数的值都是None
.
#定义标记变量
# object 通常不会单独使用,但是拿来做这种标记变量刚刚好
_not_set = object()
def dump_value(value, extra=_not_set):
if extra is _not_set:
# 调用方没有传递 extra 参数
print("w")
else:
print("cc")
dump_value("111")
dump_value("111",extra={})
dump_value("111",extra=None)
# w
# cc
# cc
相比None,_not_set是一个独一无二、无法随意获取的标记值.假如函数在执行时判断extra的值等于_not_set,那我们基本可以认定:调用方没有提供extra参数。
03.定义仅限关键字参数
「Python里的函数不光支持通过有序位置参数(positional argument
)调用,还能指定参数名,通过关键字参数(keyword argument
)的方式调用。」
虽然关键字参数调用模式很有用,但是它并不是强制使用的。那么怎么办呢?我们可以通过在参数列表中插入“*”号,该符号后的所有参数都变成了仅限关键字参数。
#注意参数列表中的* 符号
def query_users(limit, offset, *, min_followers_count, include_profile):
...
query_users(10,1,3,100,"hello")
# Traceback (most recent call last):
# File "/Users/milong/Desktop/codes/python/daydayup/day1104/test_3.py", line 4, in <module>
# query_users(10,1,3,100,"hello")
# TypeError: query_users() takes 2 positional arguments but 5 were given
# 正确的调用方式
query_users(20, 0, min_followers_count=100, include_profile=True)
「注意:这是一个新的知识点,我们会专门写一篇文章来介绍。」
「当参数较多时,通过这种方式把部分参数变为仅限关键字参数,可以强制调用方提供参数,提升代码可读性。」
函数返回的常见模式
01.尽量只返回一种类型
def get_users(user_id=None):
if user_id is not None:
return User.get(user_id)
else:
return User.filter(is_active=True)
# 返回单个用户
user = get_users(user_id=1)
# 返回多个用户
users = get_users()
虽然这样的“多功能函数”看上去很实用,像瑞士军刀一样“多才多艺”,但在现实世界里,这样的函数只会更容易让调用方困惑——“明明get_users()
函数名字里写的是users
,为什么有时候只返回了单个用户呢?”
像上面的例子,更好的做法是将它拆分为两个独立的函数。
(1) get_user_by_id(user_id):返回单个用户。
(2) get_active_users():返回多个用户列表。
02.谨慎返回None值
操作类函数的默认返回值
def close_ignore_errors(fp):
# 操作类函数,默认返回 None
try:
fp.close()
except IOError:
logger.warning('error closing file')
意料之中的缺失值
在标准库中,正则表达式模块re
下的re.search()、re.match()
函数均属于此类。这两个函数在找到匹配结果时,会返回re.Match
对象,否则返回None
。
执行失败时代表“错误”
def create_user_from_name(username):
"""通过用户名创建一个 User 实例"""
if validate_username(username):
return User.from_username(username)
else:
return None
user = create_user_from_name(username)
if user is not None:
user.do_something()
从上面的分析来看,适合返回None
的函数需要满足以下两个特点:
「(1)函数的名称和参数必须表达“结果可能缺失”的意思;」
「(2)如果函数执行无法产生结果,调用方也不关心具体原因。」
对上面的代码进行一个改进,代码如下:
class UnableToCreateUser(Exception):
"""当无法创建用户时抛出"""
def create_user_from_name(username):
"""通过用户名创建一个 User 实例
:raises: 当无法创建用户时抛出 UnableToCreateUser
"""
if validate_username(username):
return User.from_username(username)
else:
raise UnableToCreateUser(f'unable to create user from {username}')
try:
user = create_user_from_name(username)
except UnableToCreateUser:
# 此处编写异常处理逻辑
else:
user.do_something()
03.早返回,多返回
「对于读代码的人来说,return是一种有效的思维减负工具。当我们自上而下阅读代码时,假如遇到了return,就会清楚知道:“这条执行路线已经结束了”。」这部分逻辑在大脑里占用的空间会立刻得到释放,让我们可以专注于下一段逻辑。因此,「在编写函数时,请不要纠结函数是不是应该只有一个return,只要尽早返回结果可以提升代码可读性,那就多多返回吧。」
总结
感兴趣的小伙伴,赠送电子书和全套Python学习资料,具体看下方。
一、Python所有方向的学习路线
Python所有方向的技术点做的整理,形成各个领域的知识点汇总,它的用处就在于,你可以按照下面的知识点去找对应的学习资源,保证自己学得较为全面。
二、Python必备开发工具
工具都帮大家整理好了,安装就可直接上手!
三、最新Python学习笔记
当我学到一定基础,有自己的理解能力的时候,会去阅读一些前辈整理的书籍或者手写的笔记资料,这些笔记详细记载了他们对一些技术点的理解,这些理解是比较独到,可以学到不一样的思路。
四、Python视频合集
观看全面零基础学习视频,看视频学习是最快捷也是最有效果的方式,跟着视频中老师的思路,从基础到深入,还是很容易入门的。
五、实战案例
纸上得来终觉浅,要学会跟着视频一起敲,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。
六、面试宝典