[Python-Dev] PySequence_Fast_GET_ITEM in string join

Andrew Dalke dalke at dalkescientific.com
Tue May 23 16:41:18 CEST 2006


The Sourceforge tracker is kaputt so I'm sending it here, in part
because the effbot says it's interesting.

I can derive from list and override __getitem__

 >>> class Spam(list):
...   def __getitem__(self, i):
...     print i
...     return 2
...
 >>> Spam()[1]
1
2
 >>> Spam()[9]
9
2
 >>>

Now consider the following

 >>> class Spam(list):
...   def __getitem__(self, i):
...     print "Asking for", i
...     if i == 0: return "zero"
...     if i == 1: return "one"
...     raise IndexError, i
...
 >>> Spam()[1]
Asking for 1
'one'
 >>>

Spiffy!  For my next trick....

 >>> "".join(Spam())
''
 >>>

The relevant code in stringobject uses PySequence_Fast_GET_ITEM(seq, i),
which likely doesn't know about my derived __getitem__.

         p = PyString_AS_STRING(res);
         for (i = 0; i < seqlen; ++i) {
                 size_t n;
                 item = PySequence_Fast_GET_ITEM(seq, i);
                 n = PyString_GET_SIZE(item);
                 memcpy(p, PyString_AS_STRING(item), n);
                 p += n;
                 if (i < seqlen - 1) {
                         memcpy(p, sep, seplen);
                         p += seplen;
                 }
         }


The Unicode join has the same problem

 >>> class Spam(list):
...   def __getitem__(self, i):
...     print "Asking for", i
...     if i == 0: return "zero"
...     if i == 1: return u"one"
...     raise IndexError, i
...
 >>> "".join(Spam())
''

While if my class is not derived from list, everything is copacetic.

 >>> class Spam:
...   def __getitem__(self, i):
...     print "Asking for", i
...     if i == 0: return "zero"
...     if i == 1: return u"one"
...     raise IndexError, i
...
 >>> "".join(Spam())
Asking for 0
Asking for 1
Asking for 2
u'zeroone'
 >>>

Ditto for deriving from object.

 >>> class Spam(object):
...   def __getitem__(self, i):
...     print "Asking for", i
...     if i == 0: return "zero"
...     if i == 1: return "one"
...     raise IndexError, i
...
 >>> "".join(Spam())
Asking for 0
Asking for 1
Asking for 2
'zeroone'
 >>>

					Andrew
					dalke at dalkescientific.com



More information about the Python-Dev mailing list