2010-11-23 2 views
6

Запуск этого:Как переопределить поведение в списке Python (итератор)?

class DontList(object): 
    def __getitem__(self, key): 
     print 'Getting item %s' % key 
     if key == 10: raise KeyError("You get the idea.") 
     return None 

    def __getattr__(self, name): 
     print 'Getting attr %s' % name 
     return None 

list(DontList()) 

Производит это:

Getting attr __length_hint__ 
Getting item 0 
Getting item 1 
Getting item 2 
Getting item 3 
Getting item 4 
Getting item 5 
Getting item 6 
Getting item 7 
Getting item 8 
Getting item 9 
Getting item 10 
Traceback (most recent call last): 
    File "list.py", line 11, in <module> 
    list(DontList()) 
    File "list.py", line 4, in __getitem__ 
    if key == 10: raise KeyError("You get the idea.") 
KeyError: 'You get the idea.' 

Как я могу изменить это так, что я буду получать [], в то же время предоставляя доступ к этим ключам [1] и т.д.?

(я попытался положить в def __length_hint__(self): return 0, но это не помогает.)

Мой реальный случай использования: (для прочтения, если это будет полезно, не стесняйтесь игнорировать прошлое этой точки)

После применения a certain patch к неглазу, я нашел неприятный побочный эффект для моего патча. Имея __getattr__, установите на мой класс Undefined, который возвращает новый объект Undefined. К сожалению, это означает, что list(iniconfig.invalid_section) (где isinstance(iniconfig, iniparse.INIConfig)) делает это (положить в простых print с в __getattr__ и __getitem__):

Getting attr __length_hint__ 
Getting item 0 
Getting item 1 
Getting item 2 
Getting item 3 
Getting item 4 

И так далее до бесконечности.

+0

Обратите внимание, что некоторые из этих ответов игнорировать некоторые аспекты списка (х) - это первый называют __iter__, а затем __len__ на том, что если существует, и затем будет проходить через итератор. Убедитесь, что, если __len__ имеет какие-либо побочные эффекты (на одном из моих объектов ему нужно пройти через сам итератор, чтобы узнать, сколько у него элементов), что он сбрасывается, когда это делается. – 2015-09-28 17:56:23

ответ

7

Если вы хотите переопределить итерации, то просто определить метод __iter__ в классе

+0

Я думал очень смутно об этом в какой-то момент, но не пытался его попробовать, поскольку не показывал «Getting attr __iter__`; очевидно, `hasattr` или что-то, что он использует, не делает этого. Что бы вы порекомендовали тогда создать `[]` - `def __iter __ (self): return iter ([])` (он работает, но это правильный способ)? Или, может быть, я хочу, чтобы обычный объект TypeError: Undefined не был итерирован` - есть ли способ, которым я мог бы получить его таким образом (без обмана, создав эту ошибку сам)? – 2010-11-23 13:33:51

+0

Ну, он обычно выдает хранилище следующего элемента, следовательно, почему генераторам трудно вернуться назад в предыдущее состояние. У вас всегда может быть def __iter __ (self): raise TypeError – 2010-11-23 13:39:53

+1

Подумав об этом и ответе Свена, я думаю, что я хочу переопределить `__iter__` с` return; yield` вместо того, чтобы поднимать `IndexError` на` int` в `__getitem__`, чтобы секция INO Undefined`а вела себя как определенный раздел - такие вещи, как` для раздела iniconfig`, `для ключа в разделе`,` if section в iniconfig` и `if key in section` должен работать. – 2010-11-23 21:29:28

3

Просто поднимите IndexError вместо KeyError. KeyError предназначен для картографических классов (например, dict), а IndexError предназначен для последовательностей.

Если вы определяете метод __getitem__() в своем классе, Python автоматически генерирует из него итератор. Итератор заканчивается на IndexError - см. PEP234.

1

Переопределите способ повторения вашего класса с помощью метода __iter__(). Итератор, когда они закончены, вызывая исключение StopIteration, которое является частью обычного протокола итератора и не распространяется дальше. Вот один из способов применения, что ваш пример класса:

class DontList(object): 
    def __getitem__(self, key): 
     print 'Getting item %s' % key 
     if key == 10: raise KeyError("You get the idea.") 
     return None 

    def __iter__(self): 
     class iterator(object): 
      def __init__(self, obj): 
       self.obj = obj 
       self.index = -1 
      def __iter__(self): 
       return self 
      def next(self): 
       if self.index < 9: 
        self.index += 1 
        return self.obj[self.index] 
       else: 
        raise StopIteration 

     return iterator(self) 

list(DontList()) 
print 'done' 
# Getting item 0 
# Getting item 1 
# ... 
# Getting item 8 
# Getting item 9 
# done 
0

Я думаю, что с помощью return iter([]) это правильный путь, но давайте начнем думать, как list() работы:

Получить элемент из __iter__; если появляется сообщение об ошибке StopIrteration stops..then получить этот элемент ..

Итак, вы просто yield пустой генератор в __iter__, например (x for x in xrange(0, 0)), или просто iter([]))

3

Как @Sven говорит, что это неправильно ошибка поднять. Но дело не в этом, дело в том, что это не так, потому что это не то, что вы должны делать: предотвращение __getattr__ от повышения AttributeError означает, что у вас есть переопределенная методология по умолчанию для Python для проверки того, имеет ли объект атрибут и заменил его новым один (ini_defined(foo.bar)).

Но Python уже имеет hasattr! Почему бы не использовать это?

>>> class Foo: 
...  bar = None 
... 
>>> hasattr(Foo, "bar") 
True 
>>> hasattr(Foo, "baz") 
False 
Смежные вопросы