Python 高手编程系列五百四十 :小心使用*args 和**kwargs 魔法参数

*args 和**kwargs 参数可能会破坏函数或方法的鲁棒性。它们会使签名变得模糊,
而且代码常常在不应该出现的地方构建小型的参数解析器,如下所示:
def fuzzy_thing(**kwargs):
if ‘do_this’ in kwargs:
print(‘ok i did’)
if ‘do_that’ in kwargs:
print(‘that is done’)
print(‘errr… ok’)

fuzzy_ thing(do this=1)
ok i did
errr… ok
fuzzy
thing(do that=1)
that is done
errr… ok
fuzzy
thing(hahaha=1)
errr… ok
如果参数列表变得很长而且很复杂,那么添加魔法参数是很吸引人的。但这更表示它
是一个脆弱的函数或方法,应该被分解或重构。
如果*args 被用于处理元素序列(在函数中以相同方式处理),那么要求传入唯一的
容器参数(例如 iterator)会更好些,如下所示:
def sum(args): # 可行
total = 0
for arg in args:
total += arg
return total
def sum(sequence): # 更好!
total = 0
for arg in sequence:
total += arg
return total
**kwargs 适用于同样的规则。最好固定命名参数,使方法签名更有意义,如下所示:
def make_sentence(**kwargs):
noun = kwargs.get(‘noun’, ‘Bill’)
verb = kwargs.get(‘verb’, ‘is’)
adj = kwargs.get(‘adjective’, ‘happy’)
return ‘%s %s %s’ % (noun, verb, adj)
def make_sentence(noun=‘Bill’, verb=‘is’, adjective=‘happy’):
return ‘%s %s %s’ % (noun, verb, adjective)
另一种有趣的方法是创建一个容器类,将多个相关参数分组以提供执行上下文。这种
结构与
args 或**kwargs 不同,因为它可以提供能够操作数值并且能够独立发展的内部
构件(internals)。使用它作为参数的代码将不必处理其内部构件。
例如,传入函数的 Web 请求通常由一个类实例表示。这个类负责保存 Web 服务器传入
的数据,代码如下:
def log_request(request): # 版本 1
print(request.get(‘HTTP_REFERER’, ‘No referer’))
def log_request(request): # 版本 2
print(request.get(‘HTTP_REFERER’, ‘No referer’))
print(request.get(‘HTTP_HOST’, ‘No host’))
魔法参数有时是无法避免的,特别是在元编程中。例如,想要创建能够处理任何类型
签名的函数的装饰器,它是不可或缺的。更普遍地说,在处理对函数进行遍历的未知数据
时,魔法参数都很好用,代码如下:
import logging
def log(**context):
logging.info(‘Context is:\n%s\n’ % str(context))
类的名称
类的名称必须简明、精确,并足以使人理解类的作用。常见的做法是使用后缀来表示
其类型或特性。例如:
• SQLEngine;
• MimeTypes;
• StringWidget;
• TestCase。
对于基类或抽象类,可以使用一个 Base 或 Abstract 前缀,如下所示:
• BaseCookie;
• AbstractFormatter。
最重要的是要和类属性保持一致。例如,尽量避免类及其属性名称之间的冗余:
SMTP.smtp
_send() # 命名空间中存在冗余信息
SMTP.send() # 可读性更强,也更易于记忆
模块和包的名称
模块和包的名称应体现其内容的目的。其名称应简短、使用小写字母、并且不带下划线:
• sqlite;
• postgres;
• sha1。
如果它们实现一个协议,那么通常会使用 lib 后缀,代码如下:
import smtplib
import urllib
import telnetlib
它们还需要在命名空间中保持一致,这样使用起来更加简单,代码如下:
from widgets.stringwidgets import TextWidget # 不好
from widgets.strings import TextWidget # 更好
同样,应该始终避免使用与标准库模块相同的名称。
如果一个模块开始变得复杂,并且包含许多类,那么好的做法是创建一个包并将模块
的元素划分到其他模块中。
__init__模块也可以用于将一些 API 放回顶层,因为它不会影响使用,但有助于将代码
重新组织为更小的部分。例如,考虑 foo 包中的__init__模块,其内容如下所示:
from .module1 import feature1, feature2
from .module2 import feature3
这将允许用户直接导入特性,如下列代码所示:
from foo import feature1, feature2, feature3
但要注意,这可能会增加循环依赖的可能性,并且在__init__模块中添加的代码将被
实例化。所以要小心使用。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值