Python/Sage: 列表能否以索引1开始?

12

我从一个看起来很严肃的来源下载了一个Sage脚本。但它在我的电脑上无法运行,快速调试显示一个问题是因为某些时候,作者在处理n个元素的列表时,将其从1到n编号(而“正常”的Python和(因此)Sage的编号是0..n-1)。

我错过了什么?有没有全局变量隐藏在某处改变了这种约定,就像APL一样?

感谢您的帮助(尽管我的英语和CSish都很薄弱,但我希望我的问题很清楚...)


2
你能否至少发布代码的相关部分? - steabert
3
(1..n)符号似乎是Sage特有的写法(https://dev59.com/Tk_Ta4cB1Zd3GeqPEdy7)。 - Cédric Julien
1
@CédricJulien 在Sage中,列表的索引从0开始,因为它基于Python。您提供的链接是一种制作列表的方法...例如[6..12]是列表[6, 7, 8, 9, 10, 11, 12]。但是,该列表中项目的索引将是0、1、2、3、4、5、6。再次强调,这是一种制作列表的方法,而不是重新索引列表的方法。 - GeoffDS
5个回答

8

Python(包括sage)的列表始终从0开始编号,无法更改。

查看CPython源代码,在http://hg.python.org/cpython/file/70274d53c1dd/Objects/listobject.c的第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];
}

该项查找直接委托给底层的C数组,因为C数组始终是从零开始的。因此,Python列表也始终是从零开始的。

5
一个简单的类可以为您移动索引,提供一个干净的接口来重复使用。
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]

这只是一个非常弱的列表包装器,无法运行任何实际的列表功能。在我看来,子类化collections.abc.MutableSequence会是一种更合适的方法。 - upgrd
1
@upgrd 随意上传更好的实现... - kautenja
这是我的解决方案:https://dev59.com/amw05IYBdhLWcg3wcRYj#64935398;但我并不是很满意.. :( - upgrd

3

我也曾经面临同样的问题,想实现从1开始的索引方法。我想要实现插入排序算法,该算法如下所示:插入排序算法

由于我们已经知道Python列表从0开始,所以我做了以下更改:

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

我只是在索引0中添加了一个“虚拟”的值,按照算法进行了所有操作,然后又将“虚拟”值删除。这只是一种欺骗方法。


0

我建议像这样子类化collections.abc.MutableSequence,因为一旦协议(在本例中为:__getitem____setitem____delitem____len__insert)被实现,所有列表方法都应该适用于自定义序列类型。

我想出的解决方案使用了collections.abc.MutableSequence和一个列表包装器(_lst)以及一个帮助类组件,它对除了它是可下标访问的之外的任何东西都不知道,即它实现了__getitem__来处理索引修改。

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}"

现在举例说明pop函数,尽管它没有明确实现:

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

不过,这种感觉有点混乱,如果有人分享更好的解决方案,我会很感激。

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


0
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']

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接