Python style: to check or not to check args and data members

Alex Martelli aleax at mac.com
Sun Sep 3 13:52:59 EDT 2006


Jean-Paul Calderone <exarkun at divmod.com> wrote:
   ...
> > >>> class A(object):

note that A is new-style...

> >>> class x:

...while x is old-style.

Here's a small script to explore the problem...:

import sys

class oldstyle:
    def __getitem__(self, index): print index,

class newstyle(object, oldstyle): pass

s = slice(0, 3**33)

print sys.version[:5]
print 'slice:', s
print 'old:',
oldstyle()[s]
oldstyle()[:3**33]
print
print 'new:',
newstyle()[s]
newstyle()[:3**33]
print

Running this on 2.3.5, 2.4.3, 2.5c1, 2.6a0, the results are ALWAYS:

2.5c1
slice: slice(0, 5559060566555523L, None)
old: slice(0, 5559060566555523L, None) slice(0, 2147483647, None)
slice(None, 5559060566555523L, 1)
new: slice(0, 5559060566555523L, None) slice(None, 5559060566555523L,
None) slice(None, 5559060566555523L, 1)

[[except for the version ID, of course, which changes across runs;-)]]

So: no difference across Python releases -- bug systematically there
when slicing oldstyle classes, but only when slicing them with
NON-extended slice syntax (all is fine when slicing with extended syntax
OR when passing a slice object directly; indeed, dis.dis shows that
using extended syntax builds the slice then passes it, while slicing
without a step uses the SLICE+2 opcode instead).

If you add a (deprecated, I believe) __getslice__ method, you'll see the
same bug appear in newstyle classes too (again, for non-extended slicing
syntax only).

A look at ceval.c shows that apply_slice (called by SLICE+2 &c) uses
_PyEval_SliceIndex and PySequence_GetSlice if the LHO has sq_slice in
tp_as_sequence, otherwise PySlice_New and PyObject_GetItem.  And the
relevant signature is...:

_PyEval_SliceIndex(PyObject *v, Py_ssize_t *pi)

(int instead of Py_ssize_t in older versions of Python), so of course
the "detour" through this function MUST truncate the value (to 32 or 64
bits depending on the platform).

The reason the bug shows up in classic classes even without an explicit
__getslice__ is of course that a classic class ``has all the slots''
(from the C-level viewpoint;-) -- only way to allow the per-instance
behavior of classic-instances...


My inclination here would be to let the bug persist, just adding an
explanation of it in the documentation about why one should NOT use
classic classes and should NOT define __getslice__.  Any fix might
perhaps provoke wrong behavior in old programs that define and use
__getslice__ and/or classic classes and "count" on the truncation; the
workaround is easy (only use fully-supported features of the language,
i.e. newstyle classes and __getitem__ for slicing).  But I guess we can
(and probably should) move this debate to python-dev;-).


Alex



More information about the Python-list mailing list