How to override Python list behavior (iterator)?

Launch:

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()) 

Produces the following:

 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.' 

How can I change this to get [] , while maintaining access to these keys [1] , etc.?

(I tried inserting def __length_hint__(self): return 0 , but that doesn't help.)

My real use case: (for reading, if this is useful, feel free to ignore the past)

After applying a specific patch for a tacit search, I found an unpleasant side effect for my patch. Set __getattr__ to my Undefined class, which returns a new Undefined object. Unfortunately, this means list(iniconfig.invalid_section) (where isinstance(iniconfig, iniparse.INIConfig) ) does this (put in print in __getattr__ and __getitem__ ):

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

Et and endlessly.

+6
python
source share
5 answers

If you want to override iteration then just define the __iter__ method in your class

+7
source share

Just raise IndexError instead of KeyError . KeyError is for classes like mappings (like dict ), and IndexError is for sequences.

If you define the __getitem__() method in your class, Python will automatically generate an iterator from it. The iterator ends with an IndexError - see PEP234 .

+3
source share

Like @Sven says the wrong bug to boost. But that’s not the point, the fact is that it is broken, because that’s not what you need to do: preventing __getattr__ from raising an AttributeError means that you redefined the default Python methodology to check whether the object has an attribute and replaced it new ( ini_defined(foo.bar) ).

But Python already has hasattr ! Why not use this?

 >>> class Foo: ... bar = None ... >>> hasattr(Foo, "bar") True >>> hasattr(Foo, "baz") False 
+3
source share

Override how your class repeats using the __iter__() method. Iterator, when they are finished, StopIteration exception, which is part of the normal iterator protocol and does not propagate further. Here is one way to apply this to your example class:

 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 
+1
source share

I think using return iter([]) is the right way, but let's start thinking about how list() works:

Get item from __iter__ ; if you get a StopIrteration error, it will stop ... then get this element.

So you only have yield empty generator in __iter__ , for example (x for x in xrange(0, 0)) , or just iter([]))

0
source share

All Articles