Python:具有唯一ID的相同字符串(或数字)?

时间:2015-05-28 23:38:44

标签: python string

Python经过精彩优化,但我有一个案例,我想解决它。看起来对于小数字和字符串,python会自动将多个对象合并为一个。例如:

>>> a = 1
>>> b = 1
>>> id(a) == id(b)
True
>>> a = str(a)
>>> b = str(b)
>>> id(a) == id(b)
True
>>> a += 'foobar'
>>> b += 'foobar'
>>> id(a) == id(b)
False
>>> a = a[:-6]
>>> b = b[:-6]
>>> id(a) == id(b)
True

我有一个案例,我根据他们的Python ID比较对象。除了我遇到的少数几个案例外,这个工作非常好。有谁知道如何关闭特定字符串和整数的优化?类似于反实习生()的东西?

6 个答案:

答案 0 :(得分:1)

如果不重新编译自己的CPython版本,就无法将其关闭。

但是如果你想拥有相同的小整数的“单独”版本,你可以通过维护自己的id(例如uuid4)与该对象相关联来实现。

由于整数和字符串是不可变的,因此没有明显的理由 - 如果你根本无法修改对象,你不应该关心你是否有“原始”或副本,因为没有使用 - 它可以有任何区别的情况。

相关:How to create the int 1 at two different memory locations?

答案 1 :(得分:1)

当然,可以完成,但它绝不是一个好主意:

# 
Z =1

class MyString(string):
    def __init__(self, *args):
        global Z
        super(MyString, 
                  self).__init__(*args)
        self.i = Z
        Z += 1

>>> a = MyString("1")
>>> b = MyString("1")
>>> a is b
False

顺便说一句,要比较对象是否具有相同的id,只需使用a is b代替id(a)==id(b)

答案 2 :(得分:1)

Python documentation on id()

  

返回对象的“标识”。这是一个整数,在该生命周期内保证该对象是唯一且恒定的。具有非重叠生存期的两个对象可能具有相同的id()值。

     

CPython实现细节:这是内存中对象的地址。

所以它保证是唯一的,它必须用来判断两个变量是否绑定到同一个对象。

a comment on StackOverflow here中,Alex Martelli说CPython实现不是权威的Python,而其他正确的Python实现可以在某些方面表现不同 - 而且Python Language Reference(PLR)是最接近的东西Python有一个明确的规范。

the PLR section on objects中它说的大致相同:

  

每个对象都有一个标识,一个类型和一个值。对象的标识一旦创建就永远不会改变;您可能会将其视为内存中对象的地址。 'is'运算符比较两个对象的身份; id()函数返回一个表示其身份的整数(当前实现为其地址)。

语言参考并未说明它是唯一的。它还说(重新:对象的生命周期):

  

永远不会明确销毁对象;然而,当它们变得无法到达时,它们可能被垃圾收集。允许实现推迟垃圾收集或完全省略它 - 实现垃圾收集的实现质量问题,只要没有收集到仍然可以访问的对象。

  

CPython实现细节:CPython目前使用引用计数方案和(可选)延迟检测循环链接垃圾,一旦它们变得无法访问就收集大多数对象,但不保证收集包含循环引用的垃圾。有关控制循环垃圾收集的信息,请参阅gc模块的文档。其他实现的行为不同,CPython可能会改变。当对象无法访问时,不要依赖于对象的立即终结(例如:总是关闭文件)。

这实际上并不是一个答案,我希望这最终会在某个地方结束。但我现在不想删除它,我引用并引用了它。

我会改变你的前提:python will automatically collapse multiple objects into one. - 不,它不会,它们不是多个对象,它们不可能,因为它们具有相同的id()

如果id()是Python关于两个对象是相同还是不同的明确答案,那么你的前提是不正确的 - 这不是优化,它是Python对世界的看法的基本部分。

答案 3 :(得分:0)

您根本不应该依赖这些对象来成为不同的对象。如果不修改和重新编译Python,就无法关闭此行为,以及它适用于哪些特定对象如有更改,恕不另行通知。

答案 4 :(得分:0)

这个版本说明了wim对未来更积极的拘禁的担忧。它将使用更多的内存,这就是我最初放弃它的原因,但可能是未来的证据。

>>> class Wrapper(object):
...     def __init__(self, obj):
...             self.obj = obj

>>> a = 1
>>> b = 1
>>> aWrapped = Wrapper(a)
>>> bWrapped = Wrapper(b)
>>> aWrapped is bWrapped
False
>>> aUnWrapped = aWrapped.obj
>>> bUnwrapped = bWrapped.obj
>>> aUnWrapped is bUnwrapped
True

或者类似于pickle答案的版本(wrap + pickle = wrapple):

class Wrapple(object):
    def __init__(self, obj):
        self.obj = obj

    @staticmethod
    def dumps(obj):
        return Wrapple(obj)

    def loads(self):
        return self.obj

aWrapped = Wrapple.dumps(a)
aUnWrapped = Wrapple.loads(a)

答案 5 :(得分:-1)

好吧,看到没有人发布有用的回复,我只是让你知道我最终做了什么。

首先,向可能会阅读这一天的人提供一些友好的建议。这不建议正常使用,所以如果您正在考虑它,请问问自己是否有充分的理由。有充分的理由,但它们很少见,如果有人说没有,那么他们就没有足够的思考。

最后,我只是在所有对象上使用pickle.dumps()并传递输出而不是真实对象。另一方面,我检查了id,然后使用pickle.loads()来恢复对象。这个解决方案的好处在于它适用于所有类型,包括None和Booleans。

>>> a = 1
>>> b = 1
>>> a is b
True
>>> aPickled = pickle.dumps(a)
>>> bPickled = pickle.dumps(b)
>>> aPickled is bPickled
False
>>> aUnPickled = pickle.loads(aPickled)
>>> bUnPickled = pickle.loads(bPickled)
>>> aUnPickled is bUnPickled
True
>>> aUnPickled
1