2013-06-27 2 views
0

Я работаю над проектом, где мне нужен метод, который принимает произвольный объект Python, и если этот объект действует как dict, list или tuple, что означает, что он поддерживает идею доступа к членам коллекции через key или index - мой метод должен возвращать итератор, который может пересекать пары ключ-значение или пары индекс-значение. Это также было бы хорошо для моих целей, если итератор просто пересекал ключи или индексы объектов. Вот код, который я получил до сих пор:Как получить итератор по ключам/индексам произвольного объекта Python?

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

Вы хотите использовать азбуку, определенную в collections module только обнаружить отображение (потому что вы хотите перебрать ключ-значение пары вместо ключей), а также использовать стандартные 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__() метод:

[...] о должна быть объектом коллекции, который поддерживает протокол итераций (метод __iter__()), или он должен поддерживать протокол последовательности (метод __getitem__() с целыми аргументами, начинающийся с 0).

Так где collections.Sequence ищет одновременно __getitem__ и метод __len__, iter() ищет только __getitem__.

Обратите внимание, что вы не должны заходить за борт, принимая и обрабатывая слишком много разных типов; например, для строк здесь не должно быть исключения. Пересмотрите свой код, возможно, более строгим в том, что вы обещаете обработать.

+0

@FMc: Итак, вы есть; моя точка зрения заключается в том, что любое итеративное значение должно поддерживать 'iter()'. –

+0

@FMc: и 'enumerate()' on 'iter()' не работает? –

+0

Есть * всегда * пределы того, что может быть достигнуто. Если объект не предоставляет все методы, которые должны быть распознаны как сопоставление, это последовательность, если только у нее нет метода '__getitem__'. Вот где это должно закончиться. –