5

我想要一个函数,它通常接受 X 类型的参数,其中 X 是标量、列表或字典,并根据其他信息返回具有相同键值的 X 列表。

def foo(info, k):
   return [bar(item,k) for item in processInfo(info)]

def bar(item, keydata):
   # pseudocode follows.
   # What we want to do is return an output of parallel type to the input key k,
   # using the key data to lookup information from the input item.
   if keydata is a scalar:
      return item[keydata]
   elif keydata is a list:
      return [item[k] for k in keydata]
   elif keydata is a dict:
      return dict((k,item[v]) for (k,v) in keydata.iteritems())
   else:
      raise ValueError('bar expects a scalar, list, or dict')

我的问题是,如何在这三种类型之间进行调度?


编辑:字符串将被解释为标量,而不是列表/可迭代。元组将被解释为可迭代的。

编辑 2:我想要鸭式打字,而不是严格打字。

4

7 回答 7

5

你需要以正确的顺序做事,因为str类型dict是可迭代的。

from collections import Iterable, Mapping  # in Python 3 use from collections.abc

def bar(item, keydata):
    if isinstance(keydata, Mapping):
        return {k: item[v] for (k,v) in keydata.iteritems()}
    elif isinstance(keydata, Iterable) and not isinstance(keydata, str):
        return [item[k] for k in keydata]
    return item[keydata]
于 2013-04-24T21:29:59.447 回答
3

这取决于您对输入的要求有多严格。该isinstance方法强制您指定要接受的类型(即,没有鸭子类型)。只要您的用户只传入这些类或这些类的子类型,它就可以工作。您也可以尝试通过参数支持的方法来区分参数。这方面的一个例子是

编辑:添加了字符串的特殊情况

if isinstance(keydata, basestring):
    # special case to avoid considering strings as containers
    # for python 3.x use str instead of basestring
    return item[keydata]
try:
    return dict((k,item[v]) for (k,v) in keydata.iteritems())
except AttributeError:
    # it's not a dict-like
    pass
try:
    return [item[k] for k in keydata]
except TypeError:
    # it's not iterable
return item[keydata]

控制流的选择取决于您想要的灵活性,以及​​您希望如何处理模棱两可的情况。例如,字符串被认为是字符序列还是标量?

于 2013-04-24T21:03:45.677 回答
2

通过导入集合使用新的花哨的东西:)

>>> isinstance([], collections.Sequence)
True
>>> isinstance({}, collections.Mapping)
True

您还应该考虑查看类型模块

于 2013-04-24T20:57:58.243 回答
0
if isinstance(keydata,(int,float,str)): #scalar

elif isinstance(keydata,(list,tuple)):#iterable

elif isinstance(keydata,dict):#dictionary

也许??(我可能遗漏了几种类型)...

于 2013-04-24T20:53:42.943 回答
0

或者你可以使用type,但是根据http://docs.python.org/2/library/functions.html#type推荐使用isinstance

if type(asd) in (int, str):
    print 'asd is int or str'
于 2013-04-24T21:05:35.230 回答
0

感谢您所提供的所有信息!

我最终这样做了,因为我必须keydata在迭代信息之前对 进行一些预处理:

def asKVlists(keydata):
    # return a tuple (keys, values, isScalar)
    # where keys and values are iterable
    if not isinstance(keydata, basestring):
        # check for dict behavior:
        try:
            return zip(*keydata.iteritems()) + [False]
        except AttributeError:
            pass

        # otherwise check for list behavior
        # make sure we can iterate over it
        try:
            iter(keydata)
            return (None, keydata, False)
        except TypeError:
            pass
    return (None, (keydata,), True)
于 2013-04-24T21:59:25.210 回答
0
def is_scalar(x):
    return hasattr(x, 'lower') or not hasattr(x, '__iter__')

然后,您永远不必担心失败,isinstance因为有人实现了从意外事件中继承的可迭代子类。

于 2014-03-09T21:48:23.213 回答