开发者

Python/sage: can lists start at index 1?

I've downloaded from a supposedly serious source a sage script. It doesn't work on my computer, and a quick debugging showed that a problem came from the fact that at some point, the authors were doing as if a n-element list was numbered from 1 to n (whereas the “normal” numbering in Python and (开发者_如何学Pythonthus) sage is 0..n-1).

What am I missing? Is there a global variable hidden somewhere that changes this convention, like in APL?

Thanks for your help (I hope my question is clear despite my feeble grasp of both English and CSish...)


Python (and therefore sage) lists are always numbered from 0, and there isn't a way to change that.

Looking at CPython's source, in http://hg.python.org/cpython/file/70274d53c1dd/Objects/listobject.c on line 449:

static PyObject *
list_item(PyListObject *a, Py_ssize_t i)
{
    if (i < 0 || i >= Py_SIZE(a)) {
        if (indexerr == NULL) {
            indexerr = PyString_FromString(
                "list index out of range");
            if (indexerr == NULL)
                return NULL;
        }
        PyErr_SetObject(PyExc_IndexError, indexerr);
        return NULL;
    }
    Py_INCREF(a->ob_item[i]);
    return a->ob_item[i];
}

The item lookup delegates straight to the underlying C array, and C arrays are always zero-based. So Python lists are always zero-based as well.


A simple class that shifts the index for you provides a clean interface to something reusable.

class Array(object):

    def __init__(self, items: list) -> None:
        self.items = items

    def __repr__(self) -> str:
        return '{}({})'.format(self.__class__.__name__, self.items)

    def __len__(self) -> int:
        return len(self.items)

    def __contains__(self, item: any) -> bool:
        return item in self.items

    def __getitem__(self, key: int) -> any:
        return self.items[key - 1]

    def __setitem__(self, key: int, value: any) -> None:
        self.items[key - 1] = value

    def __delitem__(self, key: int) -> None:
        del self.items[key - 1]


Well I too was facing the same idea on how to implement the method of indexing to be start from 1. I wanted to implement the Insertion Sort Algorithm which is as follows:

Python/sage: can lists start at index 1?

As we already know python list start from 0, what I did was following:

A = ['dummy',5,2,6,4,1,3]
for j in range(2,len(A)):
    key = A[j]
    i=j-1
    while i>0 and A[i]>key:
        A[i+1] = A[i]
        i = i-1
    A[i+1] = key
A.pop(0)
print A

I Just added a 'Dummy' in index 0, did all the work like in Algorithm and removed the 'dummy' again. This was just a cheating method.


I would suggest subclassing e.g. collections.abc.MutableSequence for something like this, because once the protocol (in this case: __getitem__, __setitem__, __delitem__, __len__, insert) is implemented all list methods should work on the custom sequence type.

The solution I came up with uses collections.abc.MutableSequence with a list wrapper (_lst) and a helper class component that doesn't know much about anything except that it is subscriptable, i.e. it implements __getitem__ which handles the index modification.

import collections.abc

class _IndexComponent:
    def __getitem__(self, index):
        if index == 0: raise IndexError("Index 0 is a lie.")
        if index > 0: return index -1
        else: return index
        
class OneList(collections.abc.MutableSequence):

    def __init__(self, init: list = None) -> None:
        self._index_comp = _IndexComponent()
        self._lst = []
        if not init is None: # and isinstance(init, list)?
            self._lst.extend(init)
    
    def __getitem__(self, index: int) -> any:
        return self._lst[self._index_comp[index]]

    def __setitem__(self, index: int, val: any) -> None:
        self._lst[self._index_comp] = val

    def __delitem__(self, index: int) -> None:
        del self._lst[self._index_comp[index]]

    def __len__(self) -> int:
        return len(self._lst)

    def insert(self, index: int, val: any) -> None:
        self._lst.insert(self._index_comp[index], val)

    def __repr__(self) -> str:
        return f"{self._lst}"

Now for example pop works although it isn't explicitly implemented:

ol = OneList([1,2,3,4])
print(ol.pop(1))
ol.pop(0) # IndexError

Somehow this feels kind of messy though, I would appriciate if someone shared a better solution.

l = [] l.extend([]) print(l)


In [1]: index_0 = ['foo', 'bar', 'quux']

In [2]: index_1 = [None] + index_0

In [3]: index_1[1]
Out[3]: 'foo'

In [4]: index_1[1:]
Out[4]: ['foo', 'bar', 'quux']
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜