如何在任意Python对象的键/索引上获得迭代器?

时间:2013-06-27 15:45:29

标签: python indexing iterator key

我正在开发一个项目,我需要一个接受任意Python对象的方法,如果该对象的作用类似于dictlisttuple - 含义它支持通过键或索引访问集合成员的想法 - 我的方法应该返回一个可以遍历对象的键值或索引值对的迭代器。如果迭代器只是遍历对象键或索引,那么对我的目的来说也没问题。这是我到目前为止的代码:

from collections import Mapping, Sequence

# Tuple used to identify string-like objects in Python 2 or 3.
STRINGS = (str, unicode) if str is bytes else (str, bytes)

def get_keyval_iter(obj):
    if   isinstance(obj, STRINGS):  return None
    elif isinstance(obj, Sequence): return enumerate(obj)
    elif isinstance(obj, Mapping):  return getattr(obj, 'iteritems', obj.items)()
    else:                           return None

# For example:
print list(get_keyval_iter([0, 11, 22]))        # [(0, 0), (1, 11), (2, 22)]
print list(get_keyval_iter(dict(a = 1, b = 2))) # [('a', 1), ('b', 2)]
print [ get_keyval_iter("foobar") ]             # [None]
print [ get_keyval_iter(1234) ]                 # [None]

我不喜欢这个解决方案有两个原因:(1)在一般原则上,我宁愿查询对象的接口而不是检查它的类型; (2)我的代码将返回None用于用户定义的类,这些类的对象未通过isinstance测试,但仍然支持__getitem__协议,理论上可以给我一个迭代器相关的键或索引。

以下是我想写的代码:return obj.__getitemiter__() - 或类似内容。

我是否忽略了一种明显的方式来获得我需要的东西 - 即,对任意对象的键或索引(或其键值或索引值对)的迭代器?

1 个答案:

答案 0 :(得分:1)

您希望仅使用collections module中定义的ABC来检测映射(因为您希望迭代键值对而不是键),并将标准iter() function用于其他所有内容:

import collections

def get_keyval_iter(obj):
    if isinstance(obj, collections.Mapping):
        return obj.iteritems()
    try:
        return enumerate(iter(obj))
    except TypeError:
        # not iterable
        return None

注意iter()电话;它接受任何可迭代的序列对象并返回将对其进行操作的迭代器对象。它支持实现iterator protocol的对象和支持.__getitem__()方法的对象:

  

[...] o 必须是支持迭代协议(__iter__()方法)的集合对象,或者它必须支持序列协议(__getitem__()整数参数从0开始的方法。

因此,collections.Sequence查找__getitem____len__方法,iter()仅查找__getitem__

请注意,接受和处理太多不同类型不应过分;例如,这里的字符串不应该有例外。重新思考你的代码可能会更严格地遵守你的承诺。