Truy cập địa chỉ bộ nhớ đối tượng


168

Khi bạn gọi object.__repr__()phương thức trong Python, bạn sẽ nhận được một cái gì đó như thế này:

<__main__.Test object at 0x2aba1c0cf890> 

Có cách nào để có được một địa chỉ bộ nhớ nếu bạn quá tải __repr__(), sau đó gọi super(Class, obj).__repr__()và gọi lại nó không?

Câu trả lời:


208

Các nhãn hiệu Python có này để nói về id():

Trả về "danh tính" của một đối tượng. Đây là một số nguyên (hoặc số nguyên dài) được đảm bảo là duy nhất và không đổi cho đối tượng này trong suốt vòng đời của nó. Hai đối tượng có tuổi thọ không chồng lấp có thể có cùng giá trị id (). (Ghi chú thực hiện: đây là địa chỉ của đối tượng.)

Vì vậy, trong CPython, đây sẽ là địa chỉ của đối tượng. Mặc dù vậy, không có sự đảm bảo nào cho bất kỳ trình thông dịch Python nào khác.

Lưu ý rằng nếu bạn đang viết tiện ích mở rộng C, bạn có toàn quyền truy cập vào phần bên trong của trình thông dịch Python, bao gồm quyền truy cập trực tiếp vào địa chỉ của các đối tượng.


7
Đây không phải là một câu trả lời phổ quát cho câu hỏi; nó chỉ áp dụng cho CPython.
DilithiumMatrix

5
Lưu ý đến bản thân: Bảo đảm không áp dụng cho đa xử lý
Rufus

1
Một số cách để sử dụng nó (để so sánh giá trị mà nó chứa): forum.freecodecamp.com/t/python-id-object/19207
J. Liệu

Ý nghĩa của một đối tượng lifetime(và ý nghĩa của cả đời đối với overlap/not overlap) là gì trong bối cảnh này?
Minh Trần

4
@MinhTran vì id là địa chỉ bộ nhớ của đối tượng, nó được đảm bảo duy nhất trong quy trình và trong khi đối tượng tồn tại. Một thời gian sau khi đối tượng là rác được thu thập, bộ nhớ có thể được sử dụng lại. Vòng đời không chồng lấp có nghĩa là đối tượng ban đầu không còn tồn tại khi đối tượng mới được tạo. Vì vậy, giới hạn này có nghĩa là bạn không thể sử dụng id () một cách an toàn để tạo hàm băm của một đối tượng để lưu trữ, giải phóng nó và sau đó khôi phục nó.
Joshua Clayton

71

Bạn có thể thực hiện lại repr mặc định theo cách này:

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

1
Tôi biết điều này đã cũ, nhưng bạn có thể làm return object.__repr__(self)hoặc thậm chí chỉ cần làm object.__repr__(obj)bất cứ khi nào bạn cần thay vì tạo một lớp mới
Artyer

2
@Artyer: Nhận xét này có liên quan gì đến câu hỏi ban đầu? Câu trả lời được đăng ở đây là tạo lại địa chỉ theo yêu cầu của câu hỏi ban đầu. Bạn sẽ không phải xâu chuỗi nếu bạn làm theo cách bạn đề xuất chứ?
Rafe

1
Đây dường như là câu trả lời tốt nhất cho tôi. Chỉ cần thử tạo một đối tượng (), in nó, sau đó in hex (id (object)) và kết quả khớp
Rafe

@Rafe Câu trả lời của bạn là một cách làm dài dòng __repr__ = object.__repr__và gần như không phải là bằng chứng ngu ngốc, vì có nhiều tình huống mà việc này không hiệu quả, ví dụ như __getattribute__việc thực thi bị ghi đè hoặc không phải CPython trong đó id không vị trí bộ nhớ. Nó cũng không điền vào z, vì vậy bạn sẽ phải xử lý nếu hệ thống là 64 bit và thêm các số 0 nếu cần.
Artyer

@Artyer: Ví dụ của tôi cho thấy cách xây dựng một repr. Chúng tôi thường thêm thông tin tùy chỉnh (và tôi sẽ nói rằng đây là cách thực hành mã hóa tốt vì nó hỗ trợ gỡ lỗi). Chúng tôi sử dụng phong cách này rất nhiều và tôi chưa bao giờ chạy vào các trường hợp cạnh của bạn. Cảm ơn đã chia sẻ chúng!
Rafe

52

Chỉ dùng

id(object)

6
trong đó đưa ra một con số. ... Cái gì tiếp theo? Tôi có thể truy cập đối tượng với số đó không?
JLT

Bạn có thể kiểm tra id()@JLT
Billal Begueradj

24

Có một vài vấn đề ở đây không được đề cập trong bất kỳ câu trả lời nào khác.

Đầu tiên, idchỉ trả về:

bản sắc của người Viking về một đối tượng. Đây là một số nguyên (hoặc số nguyên dài) được đảm bảo là duy nhất và không đổi cho đối tượng này trong suốt vòng đời của nó. Hai đối tượng có tuổi thọ không chồng chéo có thể có cùng id()giá trị.


Trong CPython, điều này xảy ra là con trỏ tới PyObjectđại diện cho đối tượng trong trình thông dịch, đó là điều tương tự nhưobject.__repr__ hiển thị. Nhưng đây chỉ là một chi tiết triển khai của CPython, không phải là điều gì đó đúng với Python nói chung. Jython không xử lý các con trỏ, nó xử lý các tham chiếu Java (dĩ nhiên JVM có thể đại diện cho các con trỏ, nhưng bạn không thể nhìn thấy các con trỏ đó và sẽ không muốn, bởi vì GC được phép di chuyển chúng xung quanh). PyPy cho phép các loại khác nhau có các loại khác nhau id, nhưng chung nhất chỉ là một chỉ mục vào một bảng các đối tượng bạn đã gọiidtrên, rõ ràng sẽ không phải là một con trỏ. Tôi không chắc chắn về IronPython, nhưng tôi nghi ngờ nó giống Jython hơn là CPython về vấn đề này. Vì vậy, trong hầu hết các triển khai Python, không có cách nào để hiển thị bất cứ thứ gì xuất hiện trong đó reprvà không sử dụng nếu bạn đã làm.


Nhưng nếu bạn chỉ quan tâm đến CPython thì sao? Rốt cuộc, đó là một trường hợp khá phổ biến.

Chà, trước tiên, bạn có thể nhận thấy đó idlà một số nguyên; * nếu bạn muốn 0x2aba1c0cf890chuỗi đó thay vì số 46978822895760, bạn sẽ phải tự định dạng nó. Dưới sự bao trùm, tôi tin object.__repr__là cuối cùng sử dụng printf's %pđịnh dạng mà bạn không cần phải từ Python ... nhưng bạn luôn có thể làm điều này:

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

* Trong 3.x, đó là một int. Trong 2.x, đó là intnếu nó đủ lớn để chứa một con trỏ, điều này có thể không phải do vấn đề về số lượng đã ký trên một số nền tảng long.

Có điều gì bạn có thể làm với những con trỏ này ngoài việc in chúng ra không? Chắc chắn (một lần nữa, giả sử bạn chỉ quan tâm đến CPython).

Tất cả các hàm API C đều đưa một con trỏ đến một PyObjecthoặc một loại liên quan. Đối với những loại liên quan, bạn chỉ có thể gọi PyFoo_Checkđể đảm bảo rằng nó thực sự là một Foođối tượng, sau đó sử dụng (PyFoo *)p. Vì vậy, nếu bạn đang viết một phần mở rộng C, idthì đó chính xác là những gì bạn cần.

Điều gì xảy ra nếu bạn đang viết mã Python thuần túy? Bạn có thể gọi các chức năng chính xác tương tự với pythonapitừ ctypes.


Cuối cùng, một vài câu trả lời khác đã đưa ra ctypes.addressof. Điều đó không liên quan ở đây. Điều này chỉ hoạt động cho ctypescác đối tượng như c_int32(và có thể một vài đối tượng giống như bộ nhớ đệm, như các đối tượng được cung cấp bởi numpy). Và, ngay cả ở đó, nó không cung cấp cho bạn địa chỉ của c_int32giá trị, nó sẽ cung cấp cho bạn địa chỉ của cấp độ C int32c_int32kết thúc.

Điều đó đang được nói, thường xuyên hơn không, nếu bạn thực sự nghĩ rằng bạn cần địa chỉ của một cái gì đó, bạn đã không muốn một đối tượng Python nguyên gốc ở nơi đầu tiên, bạn muốn có một ctypesđối tượng.



@Enerccio Các cách sử dụng khác của idcách sử dụng chúng để giữ các giá trị có thể thay đổi trong một seentập hợp hoặc một cachedict không phụ thuộc vào bất kỳ cách nào để idtrở thành một con trỏ hoặc liên quan đến bất kỳ cách nào repr. Đó chính xác là lý do tại sao mã như vậy hoạt động trong tất cả các triển khai Python, thay vì chỉ hoạt động trong CPython.
abarnert

vâng, tôi đã sử dụng idnó, nhưng ý tôi là ngay cả trong java bạn cũng có thể lấy địa chỉ của đối tượng, có vẻ lạ là không có cách nào trong (C) Python vì người ta có gc thực sự ổn định mà không di chuyển các đối tượng do đó địa chỉ vẫn giữ nguyên
Enerccio

@Enerccio Nhưng bạn không muốn sử dụng địa chỉ của một đối tượng cho một giá trị có thể lưu trong bộ nhớ mà bạn muốn sử dụng cho idmột đối tượng, cho dù đó có phải là địa chỉ hay không. Ví dụ, trong PyPy, idvẫn hữu dụng như một khóa trong CPython, mặc dù nó thường chỉ là một chỉ mục vào một bảng ẩn trong triển khai, nhưng một con trỏ sẽ vô dụng, vì (như Java) đối tượng có thể được di chuyển vào ký ức.
abarnert

@Enerccio Dù sao, có một cách để có được một con trỏ trong CPython. Như đã giải thích trong câu trả lời, CPython tài liệu rõ ràng, như một chi tiết cụ thể về triển khai, rằng idđối tượng là con trỏ đến vị trí của đối tượng trong bộ nhớ. Vì vậy, nếu bạn có bất kỳ việc sử dụng nào cho giá trị con trỏ (điều mà bạn gần như không bao giờ làm, như được giải thích trong câu trả lời) trong mã dành riêng cho CPython, có một cách để làm cho nó được ghi lại và đảm bảo hoạt động.
abarnert

13

Chỉ để đáp lại Torsten, tôi đã không thể gọi addressof()một đối tượng trăn thông thường. Hơn nữa , id(a) != addressof(a). Đây là trong CPython, không biết về bất cứ điều gì khác.

>>> 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

Với ctypes , bạn có thể đạt được điều tương tự với

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

Tài liệu:

addressof(C instance) -> integer
Trả về địa chỉ của bộ đệm nội bộ C

Lưu ý rằng trong CPython, hiện tại id(a) == ctypes.addressof(a), nhưng ctypes.addressofnên trả về địa chỉ thực cho mỗi lần triển khai Python, nếu

  • ctypes được hỗ trợ
  • con trỏ bộ nhớ là một khái niệm hợp lệ.

Chỉnh sửa : thêm thông tin về tính độc lập của trình thông dịch của ctypes


13
>>> nhập ctypes >>> a = (1,2,3) >>> >>> loại không hợp lệ >>> id (a) 4493268872 >>>

5
Tôi đồng tình với Barry: đoạn mã trên cho kết quả TypeError: invalid typekhi tôi thử nó với Python 3.4.
Brandon Rhodes

2

Bạn có thể nhận được một cái gì đó phù hợp cho mục đích đó với:

id(self)

1

Tôi biết đây là một câu hỏi cũ nhưng nếu bạn vẫn đang lập trình, trong python 3 ngày nay ... tôi thực sự thấy rằng nếu đó là một chuỗi, thì có một cách thực sự dễ dàng để làm điều này:

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

chuyển đổi chuỗi cũng không ảnh hưởng đến vị trí trong bộ nhớ:

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

0

Mặc dù đúng là id(object)có địa chỉ của đối tượng trong triển khai CPython mặc định, nhưng điều này thường vô dụng ... bạn không thể làm gì với địa chỉ từ mã Python thuần túy.

Lần duy nhất bạn thực sự có thể sử dụng địa chỉ là từ thư viện C mở rộng ... trong trường hợp đó là việc lấy địa chỉ của đối tượng là không quan trọng vì các đối tượng Python luôn được truyền xung quanh dưới dạng con trỏ C.


1
Trừ khi bạn sử dụng ctypesbộ công cụ tích hợp trong Thư viện chuẩn. Trong trường hợp nào bạn có thể làm tất cả mọi thứ với địa chỉ :)
Brandon Rhodes
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.