객체 메모리 주소 접근


168

object.__repr__()파이썬 에서 메소드 를 호출하면 다음과 같은 것을 얻습니다.

<__main__.Test object at 0x2aba1c0cf890> 

과부하 __repr__(), 메모리 호출 super(Class, obj).__repr__()및 정규화 이외 의 메모리 주소를 보유 할 수있는 방법이 있습니까?

답변:


208

파이썬 매뉴얼 에 대해 할 말이이있다 id():

객체의 "정체성"을 반환합니다. 이는 수명 동안이 객체에 대해 고유하고 일정하게 보장되는 정수 (또는 긴 정수)입니다. (구현 정보 : 이것은 객체의 주소입니다.)

CPython에서는 이것이 객체의 주소가 될 것입니다. 그러나 다른 파이썬 인터프리터에 대해서는 그러한 보장이 없습니다.

C 확장을 작성하는 경우 객체 주소에 직접 액세스하는 것을 포함하여 Python 인터프리터의 내부에 대한 모든 액세스 권한이 있습니다.


7
이것은 질문에 대한 보편적 인 답변 이 아닙니다 . CPython에만 적용됩니다.
DilithiumMatrix

5
자체 참고 사항 : 다중 처리에는 보증이 적용되지 않습니다
Rufus

1
: 어떤 방법 (포함 된 값 비교)을 사용하는 forum.freecodecamp.com/t/python-id-object/19207
J.합니까

이 맥락에서 객체의 lifetime의미는 무엇입니까 (그리고 일생 동안 의미하는 것은 무엇 overlap/not overlap입니까)?
민 Tran

4
ID는 객체의 메모리 주소이므로 @MinhTran은 프로세스 내에서 고유하며 객체가 존재하는 동안 보장됩니다. 객체가 가비지 수집 된 후 얼마 후에 메모리가 재사용 될 수 있습니다. 수명이 겹치지 않으면 새 객체를 만들 때 원래 객체가 더 이상 존재하지 않습니다. 따라서이 제한은 id ()를 안전하게 사용하여 객체의 해시를 만들어 저장하고 해제 한 다음 나중에 복원 할 수 없음을 의미합니다.
Joshua Clayton

71

이 방법으로 기본 repr을 다시 구현할 수 있습니다.

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

1
나는 이것이 오래 return object.__repr__(self)object.__repr__(obj)
되었다는

2
@Artyer :이 의견은 원래 질문과 어떤 관련이 있습니까? 여기에 게시 된 답변은 원래 질문에서 요청한대로 주소를 다시 작성하는 것입니다. 당신이 제안한 방식으로 맹 글링을 할 필요가 없습니까?
Rafe

1
이것은 나에게 가장 좋은 답변 인 것 같습니다. 그냥 object ()를 만들어 인쇄 한 다음 hex (id (object))를 인쇄하면 결과가 일치합니다
Rafe

@Rafe 당신의 대답은 오래 걸리는 방법이며 __repr__ = object.__repr__, 이것이 작동하지 않는 다양한 상황이 있기 때문에 거의 바보 같은 증거 __getattribute__는 아닙니다. 메모리 위치 또한 z 채우기가 없으므로 시스템이 64 비트인지 확인하고 필요에 따라 0을 추가해야합니다.
Artyer

@Artyer : 내 예는 repr을 구성하는 방법을 보여줍니다. 우리는 종종 사용자 정의 정보를 추가합니다 (그리고 이것이 디버깅을 돕는 좋은 코딩 방법이라고 말합니다). 우리는이 스타일을 무겁게 사용하며 나는 당신의 엣지 케이스에 결코 뛰어 들지 않았습니다. 공유해 주셔서 감사합니다!
Rafe


24

여기에 다른 답변으로는 다루지 않는 몇 가지 문제가 있습니다.

먼저 다음 id만 반환합니다.

객체의 "정체성". 이것은 수명 동안이 개체에 대해 고유하고 일정하게 보장되는 정수 (또는 긴 정수)입니다. 겹치지 않는 수명을 가진 두 개체의 id()값 이 동일 할 수 있습니다 .


CPython에서 이것은 PyObject인터프리터의 객체를 나타내는 포인터에 대한 포인터 이며 이는 object.__repr__표시 되는 것과 동일 합니다. 그러나 이것은 CPython의 구현 세부 사항 일뿐, 일반적으로 Python에서는 사실이 아닙니다. 자이 썬은 포인터를 다루지 않고 자바 참조를 처리한다 (물론 JVM은 포인터로 표현할 수 있지만 GC는 포인터를 움직일 수 있기 때문에 원하지 않는다). PyPy는 서로 다른 유형이 서로 다른 종류를 가질 수 id있지만 가장 일반적인 것은 호출 한 객체 테이블에 대한 색인 일뿐입니다idon, 이것은 분명히 포인터가 될 수 없습니다. IronPython에 대해서는 잘 모르겠지만 CPython과 비교하여 Jython과 더 비슷하다고 생각합니다. 따라서 대부분의 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꼭 필요한 것입니다.

순수한 파이썬 코드를 작성한다면 어떨까요? pythonapifrom 와 동일한 함수를 호출 할 수 있습니다 ctypes.


마지막으로, 다른 답변들 중 몇 가지가 나타났습니다 ctypes.addressof. 여기에는 관련이 없습니다. 이것은 ctypes같은 객체 c_int32(및 아마도 제공하는 것과 같은 메모리 버퍼와 같은 객체) 에서만 작동합니다 numpy. 그리고, 심지어 거기, 당신에게의 주소를 제공하지 않는 c_int32당신에게 C 레벨의 주소를주고, 값이 int32(가) 것을 c_int32래핑합니다.

사실, 무언가의 주소가 필요하다고 생각되면 처음에는 네이티브 Python 객체를 원하지 않았고 객체를 원했습니다 ctypes.


음, 이것이 정체성이 중요 할 때 변경 가능한 객체를 맵 / 세트에 저장하는 유일한 방법입니다.
Enerccio

@Enerccio 세트 나 딕트 id에서 변경 가능한 값을 유지하기 위해 그것들을 사용하는 것을 포함하여 , 다른 용도 는 포인터가되는 것에 의존 하거나 어떤 식 으로든 관련되지 않습니다 . 이것이 바로 그러한 코드가 CPython에서만 작동하는 대신 모든 Python 구현에서 작동하는 이유입니다. seencacheidrepr
abarnert

그래, 나는 그것을 사용 id했지만, 여전히 자바에서도 객체의 주소를 얻을 수 있다는 것을 의미한다. (C) Python에는 이상한 방법으로 보이지 않는다. 왜냐하면 실제로 객체를 움직이지 않는 안정적인 gc를 가지고 있기 때문에 주소는 동일하게 유지되기 때문이다.
Enerccio

@Enerccio 그러나 객체의 주소를 캐시 가능한 값으로 사용하고 싶지는 id않습니다. 주소에 관계없이 객체 를 사용하려고합니다 . 예를 들어 PyPy id에서 CPython의 키만큼 유용하지만 일반적으로 구현에서 숨겨진 테이블에 대한 인덱스 일 뿐이지 만 (Java와 같이) 객체를 이동할 수 있기 때문에 포인터가 쓸모가 없습니다. 기억.
abarnert

어쨌든 @Enerccio, 거기 이다 CPython에의 포인터를 얻을 수있는 방법. 답변에서 설명한 것처럼 CPython은 구현 별 세부 사항 id으로 객체의 객체가 메모리에서 객체의 위치에 대한 포인터 임을 명시 적으로 문서화 합니다. 따라서 CPython 특정 코드에서 포인터 값 (응답에 설명 된 것처럼 거의 수행하지 않는)을 사용하는 경우 문서화되고 작동하도록 보장하는 방법이 있습니다.
abarnert

13

Torsten에 대한 응답으로 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

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의 인터프리터 독립성에 대한 정보 추가


13
>>> import ctypes >>> a = (1,2,3) >>> ctypes.addressof (a) 역 추적 (가장 최근 호출) : <module> TypeError의 파일 "<input>", 라인 1, 잘못된 유형 >>> id (a) 4493268872 >>>

5
Barry와 동의합니다. 위의 코드 TypeError: invalid type는 Python 3.4로 시도 할 때 발생합니다.
Brandon Rhodes


1

나는 이것이 오래된 질문이라는 것을 알고 있지만 요즘 파이썬 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>'

0

id(object)기본 CPython 구현에서 객체의 주소 를 얻는 것이 사실이지만 , 이것은 일반적으로 쓸모가 없습니다 . 순수한 파이썬 코드의 주소로 아무것도 할 수 없습니다 .

실제로 주소를 사용할 수있는 유일한 시간은 C 확장 라이브러리에서 온 것입니다.이 경우 Python 객체는 항상 C 포인터로 전달되므로 객체의 주소를 얻는 것이 쉽지 않습니다.


1
ctypes표준 라이브러리에서 내장 툴킷 을 사용하지 않는 한 . 어떤 경우에 당신은 주소로 모든 종류의 일을 할 수 있습니다 :)
Brandon Rhodes
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.