1. `FooOrManyFoos` - 这表达了发生的事情,但我可以想象,对于不习惯它的人来说,可能很难立即理解它的含义。 2. `param` - 一些通用名称。这表明它可以是几种东西,但不说明参数的用途。
通常,我将对象的可迭代形式称为单个对象的复数形式。我知道这可能看起来有点强迫症,但 Python 应该是(在其他方面)易读性的。
http://docs.python.org/library/functions.html#isinstance
isinstance(object, classinfo)
它被称为"classinfo",可以是一个类或类元组。
您也可以这样做。
您必须考虑常见用例和异常情况。遵循80/20规则。
80%的情况下,您可以使用可迭代对象替换它而不会出现问题。
在剩余的20%情况下,您需要添加另一种情况,因为软件已经建立在假设之上(无论是可迭代还是单个项)。不要更改名称,只需更改文档。如果以前说“foo”,现在仍然说“foo”,但您使其接受“foo”的可迭代对象,而不对参数进行任何更改。如果以前说“foo_list”或“foo_iter”,现在仍然说“foo_list”或“foo_iter”,但它会静默地容忍单个对象而不会中断。
80%的代码是遗留代码(“foo”或“foo_list”)
20%的代码是新功能(“foo”可以是可迭代对象或“foo_list”可以是单个对象)
我想我来晚了,但我很惊讶没有人建议使用装饰器。
这与IT技术有关。def withmany(f):
def many(many_foos):
for foo in many_foos:
yield f(foo)
f.many = many
return f
@withmany
def process_foo(foo):
return foo + 1
processed_foo = process_foo(foo)
for processed_foo in process_foo.many(foos):
print processed_foo
我在Alex Martelli的帖子中看到了类似的模式,但我不记得链接了。
听起来你正在为像这样的代码丑陋而苦恼:
def ProcessWidget(widget_thing):
# Infer if we have a singleton instance and make it a
# length 1 list for consistency
if isinstance(widget_thing, WidgetType):
widget_thing = [widget_thing]
for widget in widget_thing:
#...
我的建议是避免过度使用接口来处理两种不同的情况。我倾向于编写更加重视方法复用和清晰命名的代码,而非使用参数的巧妙动态。
def ProcessOneWidget(widget):
#...
def ProcessManyWidgets(widgets):
for widget in widgets:
ProcessOneWidget(widget)
通常,我会从这个简单的模式开始,但是当有机会获得效率优势并抵消额外代码复杂性和部分功能重复时,就可以优化“很多”情况。 如果这种约定看起来过于冗长,可以选择像“ProcessWidget”和“ProcessWidgets”这样的名称,尽管两者之间的区别仅为一个容易忽略的字符。
您可以使用*args魔法(可变参数)使您的参数始终可迭代。
像func(arg1,arg2,...)这样将单个项目或多个已知项目作为普通函数参数传递,并在可迭代参数前加上星号,例如func(*args)
示例:
# magic *args function
def foo(*args):
print args
# many ways to call it
foo(1)
foo(1, 2, 3)
args1 = (1, 2, 3)
args2 = [1, 2, 3]
args3 = iter((1, 2, 3))
foo(*args1)
foo(*args2)
foo(*args3)
你能用高层次的方式命名你的参数吗?读代码的人更想知道参数代表什么(“客户端”)而不是它们的类型是什么(“元组列表”);类型可以在函数文档字符串中定义,这是一件好事,因为它可能会在将来改变(类型有时是实现细节)。
clients
这样的名称暗示了多个对象。如果调用者只想传递一个对象,他可能会认为需要将其包装成某个可迭代对象(这是我的直觉)。我正在寻找一种表达单个对象也可以的方法。 - Björn Pollexfile_lines
的列表。没有人会希望你将其命名为 file_line_or_file_lines
。因此,我建议如果你使用一个简短且高层次、描述性的名称,你的代码将会非常易读。:) - Eric O. Lebigot我会做一件事,
def myFunc(manyFoos):
if not type(manyFoos) in (list,tuple):
manyFoos = [manyFoos]
#do stuff here
因此,您不再需要担心它的名称。
在函数中,您应该尝试实现1个操作,接受相同的参数类型并返回相同的类型。
不要填充函数,而是使用2个函数。
既然您不关心获取哪种可迭代对象,可以尝试使用iter()获取参数的迭代器。如果iter()引发TypeError异常,则该参数不可迭代,因此您可以创建一个包含一个元素的列表或元组,这是可迭代的,就像Bob's your uncle一样简单。
def doIt(foos):
try:
iter(foos)
except TypeError:
foos = [foos]
for foo in foos:
pass # do something here
def iterfy(iterable):
if isinstance(iterable, basestring):
iterable = [iterable]
try:
iter(iterable)
except TypeError:
iterable = [iterable]
return iterable
def doIt(foos):
for foo in iterfy(foos):
pass # do something
与一些回答者不同,我喜欢这样做,因为它消除了调用方在使用您的API时可能出错的一件事情。"在生成内容时要保守,在接受内容时要开放。"
回答您最初的问题,即您应该如何命名参数,我仍然会选择“foos”,即使您只接受一个项目,因为您的意图是接受列表。如果它不可迭代,那就是技术上的错误,尽管您将为调用方纠正这个错误,因为处理单个项目可能是他们想要的。此外,如果调用方认为他们必须传入一个可迭代对象,即使只有一个项目,那当然也可以正常工作,并且需要非常少的语法,所以为什么要担心纠正他们的误解呢?
one_or_more_Foo_objects
。我认为这比单调无味的 param
更好。我现在正在处理一个相当大的项目,我们正在传递地图并仅调用我们的参数map
。地图内容取决于被调用的函数。这可能不是最好的情况,但我们在地图上重复使用了很多相同的代码,因此复制和粘贴更容易。
我认为,与其按其名称命名它,不如按其用途命名它。另外,请注意,您不能对不可迭代的对象使用in
。
map
作为参数名称可能会令人感到困惑,因为人们期望map
指的是Python中的map
函数。 - Eric O. Lebigot
[foo]
而不是foo
)。 - Katriel