首先,我以非常一般的方式使用术语“容器”和“集合”,而不是与任何Python术语相关联。
我在Python中有以下功能。它需要一个id idlist
列表,并返回objs
对应于这些ID的对象列表。这很好用:
def findObj(idlist, objs):
return [next(o for o in objs if id == o.id) for id in idlist]
问题是:idlist
并且返回值不一定需要是Python list
。我希望idlist
也可以是一个普通的id,函数将返回一个对象。或者idlist
将是Python set
,该函数将返回set
个对象。
我怎样才能实现idlist
可以使用各种“容器”类型(包括普通ID)并返回相同的“容器”类型?
答案 0 :(得分:2)
我认为你的想法是不一个好的API。 它使函数返回特定类型并让用户处理最终转换时更简单,更健壮,更不容易出错。
特别是我更喜欢使用生成器使函数变得懒惰:
def find_obj(ids, objs):
try:
for id in ids:
yield next(o for o in objs if o.id == id)
except TypeError:
# ids not iterable? assume it is a plain id
yield next(o for o in objs if o.id == ids)
现在用户可以这样做:
list(find_obj(...))
set(find_obj(...))
next(find_obj(...)) # 1 element
获得他想要的东西。
增加了好处:
find_obj(some_var_defined_elsewhere, objects)
的图像代码现在,如果输入的定义不在那附近,您如何知道将返回哪种类型?X
作为输入并转换为类型Y
,而不会浪费中间空间并进行不必要的转换替代特殊情况下的单个id案例:
def find_obj(ids, objs):
try:
return (next(o for o in objs if o.id == id) for id in ids)
except TypeError:
for o in objs:
if o.id == id:
return o
上面给出一个ids
序列时返回一个生成器,并在单个id
中传递时返回一个普通对象(而不是1个元素生成器)。
最后:大部分时间(但不总是)序列有一个构造函数,它接受一个iterable并使用这些元素构建容器。这意味着:
type(some_iterable)(something for el in some_iterable)
将生成与some_iterable
类型相同的容器。
请注意,某些类需要list
而不是通用的可迭代,因此您必须使用type(some_iterable)([<as-before>])
而其他容器不具有此类构造函数。在最后一种情况下,只有调用者才能执行转换。第一个解决方案处理得很好,没有任何特殊情况。
您可以通过添加参数来概括更多功能以执行转换:
def find_obj(ids, objs, converter=None):
if converter is None:
converter = type(ids)
try:
return converter(next(o for o in objs if o.id == id) for id in ids)
except TypeError:
return next(o for o in objs if o.id == ids)
通过这种方式,调用者可以自定义转换,如果他处理奇怪的类型。
补充说明:在python中我们使用&#34; duck typing&#34;,即只是使用该对象就好像它是正确的类型,如果它引发异常回退来做其他事情。在某些情况下,首先检查某些操作的支持是否更简单,在这种情况下,您可以将isinstance
与collections.abc
中的抽象基类一起使用,以查看对象是否为{{3 }},Iterable
,Sequence
等。
答案 1 :(得分:1)
您可以将代码更改为:
import collections
def findObj(idlist, objs):
if isinstance(idlist, collections.Iterable):
return type(idlist)([next(o for o in objs if id == o.id) for id in idlist])
else:
#cover case when idlist is just plain object
pass
答案 2 :(得分:1)
如果您的普通对象的id作为int(不是字符串)传递(对于idliist),这应该有效
def findObj(idlist, objs):
t = type(idlist)
try:
iter(idlist)
return t([next(o for o in objs if id == o.id) for id in idlist])
except TypeError, te: #not iterator. So single id and object
return idlist if idlist == objs.id else False