优雅的互斥关键字参数模式?
有时候在我的代码里,有一个函数可以用两种方式来接收参数。就像这样:
def func(objname=None, objtype=None):
if objname is not None and objtype is not None:
raise ValueError("only 1 of the ways at a time")
if objname is not None:
obj = getObjByName(objname)
elif objtype is not None:
obj = getObjByType(objtype)
else:
raise ValueError("not given any of the ways")
doStuffWithObj(obj)
有没有更优雅的方法来实现这个?如果这个参数可以有三种不同的方式呢?如果这些类型是完全不同的,我可以这样做:
def func(objnameOrType):
if type(objnameOrType) is str:
getObjByName(objnameOrType)
elif type(objnameOrType) is type:
getObjByType(objnameOrType)
else:
raise ValueError("unk arg type: %s" % type(objnameOrType))
但是如果它们不是完全不同的类型呢?这种替代方案看起来就有点傻:
def func(objnameOrType, isName=True):
if isName:
getObjByName(objnameOrType)
else:
getObjByType(objnameOrType)
因为那样的话你就得像这样调用它:func(mytype, isName=False)
,这听起来有点奇怪。
8 个回答
3
有一种方法可以让它稍微简短一些:
def func(objname=None, objtype=None):
if [objname, objtype].count(None) != 1:
raise TypeError("Exactly 1 of the ways must be used.")
if objname is not None:
obj = getObjByName(objname)
else:
obj = getObjByType(objtype)
我还没决定这是否算是“优雅”。
请注意,如果传入的参数数量不对,应该抛出一个 TypeError
错误,而不是 ValueError
错误。
3
无论如何,类似的事情在标准库中也会发生;比如,在gzip.py文件中,GzipFile的开头就有这样的情况(这里去掉了文档字符串):
class GzipFile:
myfileobj = None
max_read_chunk = 10 * 1024 * 1024 # 10Mb
def __init__(self, filename=None, mode=None,
compresslevel=9, fileobj=None):
if mode and 'b' not in mode:
mode += 'b'
if fileobj is None:
fileobj = self.myfileobj = __builtin__.open(filename, mode or 'rb')
if filename is None:
if hasattr(fileobj, 'name'): filename = fileobj.name
else: filename = ''
if mode is None:
if hasattr(fileobj, 'mode'): mode = fileobj.mode
else: mode = 'rb'
当然,这里接受filename
和fileobj
这两个参数,并且如果同时收到这两个参数时,会有特定的处理方式;不过总体的方法看起来几乎是一样的。
9
你可以考虑使用一种叫做命令分发模式的东西:
def funct(objnameOrType):
dispatcher = {str: getObjByName,
type1: getObjByType1,
type2: getObjByType2}
t = type(objnameOrType)
obj = dispatcher[t](objnameOrType)
doStuffWithObj(obj)
这里的 type1
、type2
等等,实际上是指Python中的数据类型,比如整数(int)、浮点数(float)等等。