函数返回值尽量不要超过三个
局限性:当返回参数过多时,有时会搞混哪个是哪个,可能返回的两个值反了
解决方法:如果参数过多,可以组装*变量返回,或者自定义轻量类型或namedtuple返回
有意外情况时尽量抛异常而不是返回None
例子:当if判断除法运算结果时,即使结果为0运算成功,if也会判断为False
解决方法:1用二元组表示函数返回结果,第一个元素是运算是否成功,第二个元素是运算结果;
在闭包里使用外围作用域变量
py函数作用域可分为4个,从小到大依次是函数作用域,函数外围作用域(最内层函数以外,最外层函数以内,最内层函数也叫闭包),全局作用域,内置作用域(len str内置函数所在作用域)
若想给闭包外的外围作用域里的变量赋值,需要使用nonlocal,这样赋值时变量就先从外围作用域找变量。如果不用nonlocal则赋值会被当成在最内层作用域(闭包)内的新变量定义,而不会将值赋给外围作用域的同名变量
尽量少用nonlocal,因为可能会有潜在副作用,且较难看出问题所在
和nonlocal互补的一个是global,作用是声明当前变量作用域为全局作用域
用数量可变的位置参数给函数清晰的参数列表
位置参数形如*args,写法类似于解包
缺点1:传参时,函数会将多于的参数组成一个元组传给函数的位置参数,而组元组需要遍历元素。当元素数量过多时还会占用内存和时间
缺点2:当需要更新函数参数时,函数内对参数调用逻辑存在耦合,需要大量同步修改,且入参时如果参数数量变化,会导致位置参数前面的默认参数类别因为位置变化而类型变化,引起潜在bug。为解决此种问题,可以考虑添加参数类型为关键字参数
优点:接受可变个数的参数
适用性:适用于参数数量不是很多,可以提高可读性
关键字参数表示可选行为
函数可同时使用位置参数和关键字参数,当定义参数和入参时,位置参数需要在关键字参数前面,否则报错
调用函数时,关键字参数的key不可以重复,否则报错
优点:1可读性号,易懂 2可以在函数定义时设置默认值 3关键字参数支持灵活添加新参数,有向后兼容作用
用None和docstring描述默认值会变的参数
背景:比如log写日志需要日志时间戳,如果没传入时间则设为当前时间,但如果将datetime.datetime.now()赋值为默认参数,这个表达式只会在定义时计算结果,是个固定值,不会每次调用都是当前时间
解决方法:对于需要变化的函数默认值,用None替代,然后docstring写清楚怎么处理,在函数逻辑里处理此种情况
设置参数只能通过位置参数传递或关键字参数传递
函数定义时,当只定义了位置参数和关键字参数,调用函数传参时,其实一个参数既可以以位置参数传入也可以以关键字参数传入,如果需要固定一些参数传入形式,比如只能以位置参数形式传入,不能以关键字参数传入,可在定义函数时使用*。这样做了以后,如果以错误传参形式传参就会报错
怎么做:函数定义时,可以通过*实现*左边的参数都只能通过位置参数传入而*右边的参数都只能通过关键字参数传入,例如def test(a, b, *, c='c', d='d')
这样实际上位置参数也可以接受关键字参数方式传入,因为*只保证*右边的参数强制以关键字参数传入否则报错,但无法保证*左边的以位置参数传入,py3.8及以后版本实现了对位置参数类似的支持,即/号。/左边的参数强制以位置参数传入,否则报错。当/ 和 *同时出现在函数参数定义中,/和*中间的参数可以位置参数和关键字参数形式传入
用functools.wraps定义函数修饰器
会导致什么问题:之前提过,用装饰器修饰函数后,函数的__name__会变成装饰器内层的函数名,还会在pickle.dumps时报错,报错无法找到原始函数的位置,不光如此,函数还有一些其他内置属性,被装饰时候会被装饰函数取代
解决方案:使用functools.wraps装饰装饰器里的内层函数,会保留函数大部分原始的属性不被替换