153 lines
5.8 KiB
Plaintext
153 lines
5.8 KiB
Plaintext
PEP: 357
|
|
Title: Allowing Any Object to be Used for Slicing
|
|
Version: $Revision$
|
|
Last Modified: $Date$
|
|
Author: Travis Oliphant <oliphant@ee.byu.edu>
|
|
Status: Draft
|
|
Type: Standards Track
|
|
Created: 09-Feb-2006
|
|
Python-Version: 2.5
|
|
|
|
Abstract
|
|
|
|
This PEP proposes adding an nb_index slot in PyNumberMethods and an
|
|
__index__ special method so that arbitrary objects can be used
|
|
whenever integers are explicitly needed in Python, such as in slice
|
|
syntax (from which the slot gets its name).
|
|
|
|
Rationale
|
|
|
|
Currently integers and long integers play a special role in
|
|
slicing in that they are the only objects allowed in slice
|
|
syntax. In other words, if X is an object implementing the
|
|
sequence protocol, then X[obj1:obj2] is only valid if obj1 and
|
|
obj2 are both integers or long integers. There is no way for obj1
|
|
and obj2 to tell Python that they could be reasonably used as
|
|
indexes into a sequence. This is an unnecessary limitation.
|
|
|
|
In NumPy, for example, there are 8 different integer scalars
|
|
corresponding to unsigned and signed integers of 8, 16, 32, and 64
|
|
bits. These type-objects could reasonably be used as integers in
|
|
many places where Python expects true integers but cannot inherit from
|
|
the Python integer type because of incompatible memory layouts.
|
|
There should be some way to be able to tell Python that an object can
|
|
behave like an integer.
|
|
|
|
It is not possible to use the nb_int (and __int__ special method)
|
|
for this purpose because that method is used to *coerce* objects
|
|
to integers. It would be inappropriate to allow every object that
|
|
can be coerced to an integer to be used as an integer everywhere
|
|
Python expects a true integer. For example, if __int__ were used
|
|
to convert an object to an integer in slicing, then float objects
|
|
would be allowed in slicing and x[3.2:5.8] would not raise an error
|
|
as it should.
|
|
|
|
Proposal
|
|
|
|
Add an nb_index slot to PyNumberMethods, and a corresponding
|
|
__index__ special method. Objects could define a function to place
|
|
in the nb_index slot that returns an appropriate C-integer (Py_ssize_t
|
|
after PEP 353). This C-integer will be used whenever Python needs
|
|
one such as in PySequence_GetSlice, PySequence_SetSlice, and
|
|
PySequence_DelSlice.
|
|
|
|
Specification:
|
|
|
|
1) The nb_index slot will have the signature
|
|
|
|
Py_ssize_t index_func (PyObject *self)
|
|
|
|
2) The __index__ special method will have the signature
|
|
|
|
def __index__(self):
|
|
return obj
|
|
|
|
where obj must be either an int or a long.
|
|
|
|
3) A new C-API function PyNumber_Index will be added with signature
|
|
|
|
Py_ssize_t PyNumber_Index (PyObject *obj)
|
|
|
|
which will return obj->ob_type->tp_as_number->nb_index(obj) if it is available.
|
|
A -1 will be returned and an exception set on an error.
|
|
|
|
4) A new operator.index(obj) function will be added that calls
|
|
equivalent of obj.__index__() and raises an error if obj does not implement
|
|
the special method.
|
|
|
|
Implementation Plan
|
|
|
|
1) Add the nb_index slot in object.h and modify typeobject.c to
|
|
create the __index__ method
|
|
|
|
2) Change the ISINT macro in ceval.c to ISINDEX and alter it to
|
|
accomodate objects with the index slot defined.
|
|
|
|
3) Change the _PyEval_SliceIndex function to accomodate objects
|
|
with the index slot defined.
|
|
|
|
4) Change all builtin objects (e.g. lists) that use the as_mapping
|
|
slots for subscript access and use a special-check for integers to
|
|
check for the slot as well.
|
|
|
|
5) Add the nb_index slot to integers and long_integers.
|
|
|
|
6) Add PyNumber_Index C-API to return an integer from any
|
|
Python Object that has the nb_index slot.
|
|
|
|
7) Add the operator.index(x) function.
|
|
|
|
|
|
Discussion Questions
|
|
|
|
Speed:
|
|
|
|
Implementation should not slow down Python because integers and long
|
|
integers used as indexes will complete in the same number of
|
|
instructions. The only change will be that what used to generate
|
|
an error will now be acceptable.
|
|
|
|
Why not use nb_int which is already there?
|
|
|
|
The nb_int method is used for coercion and so means something
|
|
fundamentally different than what is requested here. This PEP
|
|
proposes a method for something that *can* already be thought of as
|
|
an integer communicate that information to Python when it needs an
|
|
integer. The biggest example of why using nb_int would be a bad
|
|
thing is that float objects already define the nb_int method, but
|
|
float objects *should not* be used as indexes in a sequence.
|
|
|
|
Why the name __index__?
|
|
|
|
Some questions were raised regarding the name __index__ when other
|
|
interpretations of the slot are possible. For example, the slot
|
|
can be used any time Python requires an integer internally (such
|
|
as in "mystring" * 3). The name was suggested by Guido because
|
|
slicing syntax is the biggest reason for having such a slot and
|
|
in the end no better name emerged. See the discussion thread:
|
|
http://mail.python.org/pipermail/python-dev/2006-February/thread.html#60594
|
|
for examples of names that were suggested such as "__discrete__" and
|
|
"__ordinal__".
|
|
|
|
Why return Py_ssize_t from nb_index?
|
|
|
|
The nb_index slot is primarily intended to return an integer
|
|
needed by the sequence interface. In Python 2.5 this is
|
|
Py_ssize_t. As this is the primary purpose of the slot, it
|
|
makes sense to return the C-integer directly and not wrapped
|
|
in a Python int object.
|
|
|
|
Why can't __index__ return any object with the nb_index method?
|
|
|
|
This would allow infinite recursion in many different ways that are not
|
|
easy to check for. This restriction is similar to the requirement that
|
|
__nonzero__ return an int or a bool.
|
|
|
|
Reference Implementation
|
|
|
|
Submitted as patch 1436368 to SourceForge.
|
|
|
|
Copyright
|
|
|
|
This document is placed in the public domain
|