访问对象内存地址

时间:2008-09-23 14:35:00

标签: python object memory-address repr

当您在Python中调用object.__repr__()方法时,您会得到类似的内容:

<__main__.Test object at 0x2aba1c0cf890> 

如果你重载__repr__(),那么有没有办法获取内存地址,除了调用super(Class, obj).__repr__()并重新出现之外?

10 个答案:

答案 0 :(得分:176)

Python manual可以说id()

  

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

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

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

答案 1 :(得分:63)

你可以这样重新实现默认的repr:

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

答案 2 :(得分:46)

只需使用

id(object)

答案 3 :(得分:21)

这里有一些问题没有被其他任何答案所涵盖。

首先,id仅返回:

  

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


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


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

嗯,首先,您可能会注意到id是一个整数; *如果您想要0x2aba1c0cf890字符串而不是数字46978822895760,那么您将需要自己格式化。在幕后,我相信object.__repr__最终会使用printf的{​​{1}}格式,而这些格式是您从Python中获得的......但您可以随时执行此操作:< / p>

%p

*在3.x中,它是format(id(spam), '#010x' if sys.maxsize.bit_length() <= 32 else '#18x') 。在2.x中,它是int,如果它足以容纳指针 - 这可能不是因为在某些平台上签名的数字问题 - 而int否则

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

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

如果您正在编写纯Python代码,该怎么办?您可以使用id中的pythonapi调用完全相同的函数。


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

话虽如此,通常情况下,如果你真的认为你需要某些东西的地址,你首先想要一个原生的Python对象,你想要一个c_int32对象。 / p>

答案 4 :(得分:13)

为了回应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

答案 5 :(得分:4)

使用ctypes,您可以使用

实现相同的目标
>>> import ctypes
>>> a = (1,2,3)
>>> ctypes.addressof(a)
3077760748L

文档:

  

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

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

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

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

答案 6 :(得分:2)

您可以通过以下方式获得适合此目的的内容:

id(self)

答案 7 :(得分:0)

虽然id(object)在默认的CPython实现中获取对象的地址是正确的,但这通常是无用的......你不能用纯Python代码的地址任何事情。

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

答案 8 :(得分:0)

我知道这是一个老问题,但是,如果您现在仍在使用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>'

答案 9 :(得分:0)

我的方法

(我推荐的)

您可以使用内置 'str<的'分区'方法获取任何对象的内存地址/位置/strong>' 类型。

以下是使用它获取对象内存地址的示例:

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 为底的整数,然后将其转换为十六进制数。