互斥关键字参数的优雅模式?
有时在我的代码中,我有一个函数可以通过两种方式之一接受参数。比如:
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)
有没有更优雅的方法来做到这一点?如果 arg 可以通过三种方式之一出现怎么办?如果类型不同,我可以这样做:
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) 那样调用它,这很奇怪。
Sometimes in my code I have a function which can take an argument in one of two ways. Something like:
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)
Is there any more elegant way to do this? What if the arg could come in one of three ways? If the types are distinct I could do:
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))
But what if they are not? This alternative seems silly:
def func(objnameOrType, isName=True):
if isName:
getObjByName(objnameOrType)
else:
getObjByType(objnameOrType)
cause then you have to call it like func(mytype, isName=False)
which is weird.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
如何使用命令调度模式之类的东西:
其中
type1
、type2
等是实际的 Python 类型(例如 int、float 等)。How about using something like a command dispatch pattern:
where
type1
,type2
, etc are actual python types (e.g. int, float, etc).听起来应该去 https://codereview.stackexchange.com/
无论如何,保持相同的界面,我可以尝试
或简单地创建2个功能?
Sounds like it should go to https://codereview.stackexchange.com/
Anyway, keeping the same interface, I may try
or simply create 2 functions?
让它稍微短一点的一种方法是
我还没有决定是否称其为“优雅”。
请注意,如果给出了错误数量的参数,您应该引发
TypeError
,而不是ValueError
。One way to make it slightly shorter is
I have not yet decided if I would call this "elegant".
Note that you should raise a
TypeError
if the wrong number of arguments was given, not aValueError
.不管它的价值如何,类似的事情在标准库中也发生过。例如,参见 gzip.py 中 GzipFile 的开头(此处显示已删除文档字符串):
当然,这接受
filename
和fileobj
关键字,并在中定义特定行为两者都收到的情况;但总体方法似乎几乎相同。For whatever it's worth, similar kinds of things happen in the Standard Libraries; see, for example, the beginning of GzipFile in gzip.py (shown here with docstrings removed):
Of course this accepts both
filename
andfileobj
keywords and defines a particular behavior in the case that it receives both; but the general approach seems pretty much identical.我使用装饰器:
用作:
请注意,这仅考虑作为关键字参数传递的非
None
值。例如,如果除其中一个之外的所有kwarg_names
的值为None
,则仍可以传递多个kwarg_names
。为了允许不传递任何 kwargs,只需断言计数 <= 1。
I use a decorator:
Used as:
Note that this only accounts for non-
None
values that are passed as keyword arguments. E.g. multiple of thekwarg_names
may still be passed if all but one of them have a value ofNone
.To allow for passing none of the kwargs simply assert that the count is <= 1.
听起来您正在寻找 函数重载,但尚未实现在 Python 2 中。在 Python 2 中,您的解决方案几乎与您期望的一样好。
您可以通过允许函数处理多个对象并返回生成器来绕过额外参数问题:
测试:
It sounds like you're looking for function overloading, which isn't implemented in Python 2. In Python 2, your solution is nearly as good as you can expect to get.
You could probably bypass the extra argument problem by allowing your function to process multiple objects and return a generator:
Test:
内置的
sum()
可用于布尔表达式列表。在Python中,bool
是int
的子类,在算术运算中,True
表现为1,而False
行为为 0。这意味着这段相当短的代码将测试任意数量的参数的互斥性:
也可能有变化:
The built-in
sum()
can be used to on a list of boolean expressions. In Python,bool
is a subclass ofint
, and in arithmetic operations,True
behaves as 1, andFalse
behaves as 0.This means that this rather short code will test mutual exclusivity for any number of arguments:
Variations are also possible:
我偶尔也会遇到这个问题,很难找到一个容易通用的解决方案。假设我有更复杂的参数组合,这些参数组合由一组互斥的参数描述,并且希望为每个参数支持附加参数(其中一些可能是必需的,一些可能是可选的),如以下签名所示:
我将使用面向对象来将参数包装在一组描述符中(名称取决于参数的业务含义),然后可以通过 pydantic:
您可以使用工厂实例化这些描述:
然后您可以像这样包装实际的函数实现,并使用类型检查来查看提供了哪些参数:
并调用
func(mutex1='' 、 arg1=True)
、func(mutex2='')
、func(mutex3=123)
等等。这并不是整体上较短的代码,但它根据您的规范以非常描述性的方式执行参数验证,在验证失败时引发有用的 pydantic 错误,并在函数实现的每个分支中产生准确的静态类型。
请注意,如果您使用的是 Python 3.10+,结构模式匹配可以简化其中的某些部分。
I occasionally run into this problem as well, and it is hard to find an easily generalisable solution. Say I have more complex combinations of arguments that are delineated by a set of mutually exclusive arguments and want to support additional arguments for each (some of which may be required and some optional), as in the following signatures:
I would use object orientation to wrap the arguments in a set of descriptors (with names depending on the business meaning of the arguments), which can then be validated by something like pydantic:
You could instantiate these descriptions with a factory:
Then you can wrap your actual function implementation like this and use type checking to see which arguments were provided:
and call as
func(mutex1='', arg1=True)
,func(mutex2='')
,func(mutex3=123)
and so on.This is not overall shorter code, but it performs argument validation in a very descriptive way according to your specification, raises useful pydantic errors when validation fails, and results in accurate static types in each branch of the function implementation.
Note that if you're using Python 3.10+, structural pattern matching could simplify some parts of this.