将“集合”(列表,集合,单个对象,...)转换为Python中相同类型的新集合

时间:2016-04-08 10:17:59

标签: python python-3.x

首先,我以非常一般的方式使用术语“容器”和“集合”,而不是与任何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)并返回相同的“容器”类型?

3 个答案:

答案 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;,即只是使用该对象就好像它是正确的类型,如果它引发异常回退来做其他事情。在某些情况下,首先检查某些操作的支持是否更简单,在这种情况下,您可以将isinstancecollections.abc中的抽象基类一起使用,以查看对象是否为{{3 }},IterableSequence等。

答案 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