访问对象内存地址

Posted

技术标签:

【中文标题】访问对象内存地址【英文标题】:Accessing Object Memory Address 【发布时间】:2010-09-12 09:25:27 【问题描述】:

当你在 Python 中调用 object.__repr__() 方法时,你会得到类似这样的结果:

<__main__.Test object at 0x2aba1c0cf890> 

如果您重载__repr__(),是否有任何方法可以获取内存地址,然后调用super(Class, obj).__repr__() 并对其进行正则表达式?

【问题讨论】:

【参考方案1】:

随便用

id(object)

【讨论】:

给出一个数字。 ... 下一步是什么?我可以用那个号码访问对象吗? 你可以查看这个id()@JLT【参考方案2】:

您可以通过以下方式获得适合该目的的东西:

id(self)

【讨论】:

【参考方案3】:

Python manual 对id() 有这样的看法:

返回对象的“身份”。 这是一个整数(或长整数) 保证是唯一的并且 该对象在其期间的常量 寿命。两个对象 非重叠的生命周期可能有 相同的 id() 值。 (实施说明: 这是对象的地址。)

所以在 CPython 中,这将是对象的地址。但是,任何其他 Python 解释器都没有这样的保证。

请注意,如果您正在编写 C 扩展,您可以完全访问 Python 解释器的内部,包括直接访问对象的地址。

【讨论】:

不是问题的普遍答案;它仅适用于 CPython。 自我注意:保证不适用于多处理 一些使用方法(比较它包含的值):forum.freecodecamp.com/t/python-id-object/19207 在这种情况下,对象的lifetime(以及overlap/not overlap 的生命周期意味着什么)指的是什么? @MinhTran 因为 id 是对象的内存地址,它保证在进程内是唯一的,并且当对象存在时。在对象被垃圾收集后的一段时间内,内存可能会被重用。非重叠生命周期意味着在创建新对象时原始对象不再存在。所以这个限制意味着你不能安全地使用 id() 来创建一个对象的哈希值来存储、释放它,然后再恢复它。【参考方案4】:

您可以通过这种方式重新实现默认 repr:

def __repr__(self):
    return '<%s.%s object at %s>' % (
        self.__class__.__module__,
        self.__class__.__name__,
        hex(id(self))
    )

【讨论】:

我知道这是旧的,但你可以只做return object.__repr__(self),甚至在需要时只做object.__repr__(obj),而不是创建一个新课程 @Artyer:这个评论和原来的问题有什么关系?此处发布的答案是按照原始问题的要求重新创建地址。如果按照您建议的方式进行操作,您就不必串起来了吗? 这对我来说似乎是最好的答案。只需尝试制作一个object(),打印它,然后打印hex(id(object)),结果匹配 @Rafe 您的回答是一种冗长的 __repr__ = object.__repr__ 做法,并且几乎没有那么简单,因为在很多情况下这不起作用,例如被覆盖的__getattribute__ 或 id 不是内存位置的非 CPython 实现。它也没有 z 填充,因此您必须确定系统是否为 64 位并根据需要添加零。 @Artyer:我的例子展示了如何构建一个 repr。我们经常添加自定义信息(我会说这是一种很好的编码习惯,因为它有助于调试)。我们大量使用这种风格,我从来没有遇到过你的边缘情况。感谢分享!【参考方案5】:

使用ctypes,您可以实现同样的目的

>>> import ctypes
>>> a = (1,2,3)
>>> ctypes.addressof(a)
3077760748L

文档:

addressof(C instance) -&gt; integer 返回C实例内部缓冲区的地址

请注意,在 CPython 中,当前为 id(a) == ctypes.addressof(a),但 ctypes.addressof 应该返回每个 Python 实现的真实地址,如果

支持 ctypes 内存指针是一个有效的概念。

编辑:添加了有关 ctypes 的解释器独立性的信息

【讨论】:

>>> import ctypes >>> a = (1,2,3) >>> ctypes.addressof(a) Traceback(最近一次调用最后):文件“”,行1、在 TypeError: invalid type >>> id(a) 4493268872 >>> 我同意 Barry 的观点:当我使用 Python 3.4 进行尝试时,上述代码的结果为 TypeError: invalid type【参考方案6】:

虽然id(object) 在默认的 CPython 实现中确实获得了对象的地址,但这通常是无用的......你不能使用纯 Python 代码中的地址进行任何操作。

您真正能够使用该地址的唯一时间是来自 C 扩展库...在这种情况下,获取对象的地址是微不足道的,因为 Python 对象总是作为 C 指针传递。

【讨论】:

除非您使用标准库中内置的ctypes 工具包。在这种情况下,您可以使用地址做各种事情:)【参考方案7】:

作为对 Torsten 的回应,我无法在常规 python 对象上调用 addressof()。此外,id(a) != addressof(a)。这是在CPython中,不知道其他的。

>>> from ctypes import c_int, addressof
>>> a = 69
>>> addressof(a)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: invalid type
>>> b = c_int(69)
>>> addressof(b)
4300673472
>>> id(b)
4300673392

【讨论】:

【参考方案8】:

这里有一些其他答案未涵盖的问题。

首先,id 只返回:

对象的“身份”。这是一个整数(或长整数),保证该对象在其生命周期内是唯一且恒定的。具有不重叠生命周期的两个对象可能具有相同的 id() 值。


在 CPython 中,这恰好是指向 PyObject 的指针,它代表解释器中的对象,这与 object.__repr__ 显示的内容相同。但这只是 CPython 的一个实现细节,而不是 Python 的一般情况。 Jython 不处理指针,它处理 Java 引用(JVM 当然可能将其表示为指针,但是您看不到这些,也不想看到,因为允许 GC 移动它们)。 PyPy 允许不同类型有不同类型的id,但最通用的只是一个指向您调用id 的对象表的索引,这显然不是一个指针。我不确定 IronPython,但我怀疑在这方面它更像 Jython 而不是 CPython。因此,在大多数 Python 实现中,没有办法得到 repr 中显示的任何内容,如果这样做也没有用。


但是,如果您只关心 CPython 怎么办?毕竟这是很常见的情况。

首先,您可能会注意到id 是一个整数;* 如果您想要0x2aba1c0cf890 字符串而不是数字46978822895760,您将不得不自己格式化它。在幕后,我相信object.__repr__ 最终会使用printf%p 格式,这是Python 所没有的……但你总是可以这样做:

format(id(spam), '#010x' if sys.maxsize.bit_length() <= 32 else '#18x')

* 在 3.x 中,它是 int。在 2.x 中,int 如果足够大以容纳指针(这可能不是因为某些平台上的符号数问题),否则为 long

除了打印出来之外,你还能用这些指针做些什么吗?当然(再次假设您只关心 CPython)。

所有C API 函数都采用指向PyObject 或相关类型的指针。对于那些相关类型,您可以调用PyFoo_Check 以确保它确实是Foo 对象,然后使用(PyFoo *)p 进行转换。因此,如果您正在编写 C 扩展,id 正是您所需要的。

如果您正在编写纯 Python 代码怎么办?您可以从ctypes 调用与pythonapi 完全相同的函数。


最后,其他一些答案提出了ctypes.addressof。这与这里无关。这仅适用于ctypes 对象,如c_int32(可能还有一些类似内存缓冲区的对象,如numpy 提供的对象)。而且,即使在那里,它也不是给你c_int32 值的地址,而是给你c_int32 包裹的C 级int32 的地址。

话虽如此,通常情况下,如果你真的认为你需要某个东西的地址,你一开始就不想要一个原生 Python 对象,你想要一个 ctypes 对象。

【讨论】:

好吧,当身份很重要时,这是在地图/集合中存储可变对象的唯一方法...... @Enerccio id 的其他用途——包括使用它们在seen 集合或cache 字典中保存可变值——不要依赖于id 的任何方式作为指针,或以任何方式与repr 相关。这正是此类代码适用于所有 Python 实现而不是仅适用于 CPython 的原因。 是的,我使用了id,但我的意思是即使在java中你也可以获得对象的地址,看起来很奇怪,在(C)Python中没有办法,因为那个实际上有稳定的gc不会移动对象,因此地址保持不变 @Enerccio 但是您不想将对象的地址用于可缓存值——您想使用对象的id,无论它是否是地址。例如,在 PyPy 中,id 仍然与 CPython 中的键一样有用,尽管它通常只是实现中某个隐藏表的索引,但指针是无用的,因为(如 Java)对象可以在记忆中移动。 @Enerccio 无论如何,一种在 CPython 中获取指针的方法。正如答案中所解释的,作为特定于实现的细节,CPython 明确记录了对象的 id 是指向对象在内存中的位置的指针。因此,如果您在特定于 CPython 的代码中对指针值有任何用途(您几乎从不这样做,正如答案中所解释的那样),有一种方法可以得到它,该方法已记录并保证可以工作。【参考方案9】:

我知道这是一个老问题,但如果你还在编程,这些天在 python 3 中......我实际上发现如果它是一个字符串,那么有一个非常简单的方法可以做到这一点:

>>> spam.upper
<built-in method upper of str object at 0x1042e4830>
>>> spam.upper()
'YO I NEED HELP!'
>>> id(spam)
4365109296

字符串转换也不影响内存中的位置:

>>> spam = 437 : 'passphrase'
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'
>>> str(spam)
"437: 'passphrase'"
>>> object.__repr__(spam)
'<dict object at 0x1043313f0>'

【讨论】:

【参考方案10】:

您可以使用内置 'str的'partition'方法获取任意对象的内存地址/位置>' 类型。

这是一个使用它来获取对象的内存地址的示例:

Python 3.8.3 (default, May 27 2020, 02:08:17)
[GCC 9.3.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> object.__repr__(1)
'<int object at 0x7ca70923f0>'
>>> hex(int(object.__repr__(1).partition('object at ')[2].strip('>'), 16))
0x7ca70923f0
>>>

这里,我使用内置的 'object' 类' '__repr__' 方法和一个对象/项目,例如1 作为参数来返回字符串和然后我对该字符串进行分区,它将返回我提供的字符串之前的字符串元组,我提供的字符串,然后是我提供的字符串之后的字符串,并且内存位置位于'object at'之后,我可以得到内存地址,因为它已经从那部分进行了分区。

然后,由于内存地址作为返回元组中的第三项返回,我可以使用元组中的索引2 访问它。但是,它在我获得的字符串中有一个直角括号作为后缀,所以我使用 'strip' 函数将其删除,这将在没有尖括号的情况下返回它。然后我将结果字符串转换为以 16 为底的整数,然后将其转换为十六进制数。

【讨论】:

【参考方案11】:

如果__repr__重载,可以考虑__str__查看变量的内存地址。

这是 *** 中 Moshe Zadka 的 __repr__ versus __str__ 的详细信息。

【讨论】:

以上是关于访问对象内存地址的主要内容,如果未能解决你的问题,请参考以下文章

编程中访问对象

运行时Java对象在内存中的存储

在c/c++中获取java对象的内存地址

java内存

Java再谈_变量

Java再谈_变量