Python - 如何区分指向同一对象的两个列表元素?

4 投票
7 回答
502 浏览
提问于 2025-04-16 04:42

我有一个环形结构,下面是我实现的方式(参考了一个食谱):

class Ring(list):

    def turn(self):
        last = self.pop(0)
        self.append(last)

    def setTop(self, objectReference):
        if objectReference not in self:
            raise ValueError, "object is not in ring"

        while self[0] is not objectReference:
            self.turn()

假设我这样做:

x = Ring([1,2,3,4,4])
x.setTop(4)

我的代码总是把前面4个(现在是x[3])设置为x[0]。通过对比x[3]和x[4]的对象身份和哈希值,似乎Python在重复使用这4个对象。

我该怎么告诉Python,我真的想把第二个4(现在是x[4])放在最上面呢?

抱歉问这么基础的问题……这就是自学的初学者的一些困扰。

谢谢,

迈克

===编辑===

顺便说一下,我把类中的setTop方法去掉了。我本来是觉得“嘿,这样做很酷,可能会有用”而加上的。根据回答(特别是“有什么区别”,说得很对)和我自己使用这个结构的经验来看,这个方法其实很糟糕,根本不支持我的任何用例。

换句话说,添加一些东西只是因为我能做到,而不是为了满足需求 = 失败。

7 个回答

1

在Python的Cpython版本中,对于一些小整数(大约是从-5到255之间的数字),它有一个“整数缓存”。这意味着这些数字会重用同一个对象,也就是说,所有的4都是同一个值为4的整数对象。这样做是为了减少创建新对象的需要。

如果你想绕过这个限制,有几种方法:

  • 你可以使用长整型(比如写4L而不是4)。Python对长整型不使用缓存。(你也可以使用浮点数,因为它们同样不被缓存。)不过,如果你对这些数字进行大量数学运算,可能会影响性能。
  • 你可以把每个数字放在一个列表或元组里(这样做比较方便,因为语法简单,虽然比使用长整型或浮点数要多一些语法)。
  • 你可以创建一个自己的对象来包装这个整数。这个对象会有和整数一样的方法(所以在数学运算、比较、打印等方面都能像整数一样工作),但每个实例都是独一无二的。

我个人比较喜欢在这种情况下使用长整型。你可以在构造函数中轻松地把整数转换为长整型,并在任何添加项目的方法中进行转换。

2

如果你已经确定想要第二个选项,那就这样做:

x = Ring([1,2,3,4,4])
x.setTop(4)
x.turn()
x.setTop(4)

你可以改进setTop()这个函数,让它接受一个额外的参数,然后在里面处理这个参数。

4

来自《学习Python,第4版》第6章:

从概念上讲,每当你在脚本中通过运行一个表达式生成一个新值时,Python都会创建一个新的对象(也就是一块内存)来表示这个值。为了提高效率,Python内部会缓存并重用某些不可改变的对象,比如小整数和字符串(比如每个0其实并不是一块新的内存——关于这种缓存行为我们稍后会详细讲)。不过,从逻辑上看,每个表达式的结果值就像是一个独立的对象,而每个对象又是一个独立的内存块。

问题是……

if x[3] is x[4]:
    print "What's the difference?"

撰写回答