2012-04-18 26 views
6

điều này là với CPython 2.7.2 và 3.2.2.tại sao một số biểu thức tham chiếu `` x.y`` thay đổi `` id (x.y) ``?

giả sử chúng tôi xác định Classobj như sau.

class Class(object): 

    def m(self): 
     pass 

    @property 
    def p(self): 
     return None 

    @staticmethod 
    def s(): 
     pass 

obj = Class() 

phiên bản ngắn

tại sao sản lượng mã sau False cho mỗi print()?

print(Class.__dict__ is Class.__dict__) 
print(Class.__subclasshook__ is Class.__subclasshook__) 
print(Class.m is Class.m) 

print(obj.__delattr__ is obj.__delattr__) 
print(obj.__format__ is obj.__format__) 
print(obj.__getattribute__ is obj.__getattribute__) 
print(obj.__hash__ is obj.__hash__) 
print(obj.__init__ is obj.__init__) 
print(obj.__reduce__ is obj.__reduce__) 
print(obj.__reduce_ex__ is obj.__reduce_ex__) 
print(obj.__repr__ is obj.__repr__) 
print(obj.__setattr__ is obj.__setattr__) 
print(obj.__sizeof__ is obj.__sizeof__) 
print(obj.__str__ is obj.__str__) 
print(obj.__subclasshook__ is obj.__subclasshook__) 
print(obj.m is obj.m) 

(đó là bằng Python 2; cho Python 3, bỏ qua print() cho Class.m và thêm những cái tương tự cho obj.__eq__, obj.__ge__, obj.__gt__, obj.__le__, obj.__lt__, và obj.__ne__)

và tại sao, ngược lại, đầu ra mã sau đây True cho mỗi print() là gì?

print(Class.__class__ is Class.__class__) 
print(Class.__delattr__ is Class.__delattr__) 
print(Class.__doc__ is Class.__doc__) 
print(Class.__format__ is Class.__format__) 
print(Class.__getattribute__ is Class.__getattribute__) 
print(Class.__hash__ is Class.__hash__) 
print(Class.__init__ is Class.__init__) 
print(Class.__module__ is Class.__module__) 
print(Class.__new__ is Class.__new__) 
print(Class.__reduce__ is Class.__reduce__) 
print(Class.__reduce_ex__ is Class.__reduce_ex__) 
print(Class.__repr__ is Class.__repr__) 
print(Class.__setattr__ is Class.__setattr__) 
print(Class.__sizeof__ is Class.__sizeof__) 
print(Class.__str__ is Class.__str__) 
print(Class.__weakref__ is Class.__weakref__) 
print(Class.p is Class.p) 
print(Class.s is Class.s) 

print(obj.__class__ is obj.__class__) 
print(obj.__dict__ is obj.__dict__) 
print(obj.__doc__ is obj.__doc__) 
print(obj.__module__ is obj.__module__) 
print(obj.__new__ is obj.__new__) 
print(obj.__weakref__ is obj.__weakref__) 
print(obj.p is obj.p) 
print(obj.s is obj.s) 

(đó là bằng Python 2; cho Python 3, thêm tương tự print() s cho Class.__eq__, Class.__ge__, Class.__gt__, Class.__le__, Class.__lt__, và Class.__ne__, và Class.m)

dài phiên bản

nếu chúng ta yêu cầu id(obj.m) hai lần liên tiếp, sau đó chúng tôi nhận được cùng một ID hai lần.

>>> id(obj.m) 
139675714789856 
>>> id(obj.m) 
139675714789856 

nhưng nếu chúng tôi yêu cầu id(obj.m), sau đó một số biểu thức chứa tham chiếu đến obj.m, sau đó id(obj.m) một lần nữa, những thay đổi ID đôi khi (nhưng không phải luôn luôn). nếu nó thay đổi, thì nếu chúng tôi yêu cầu id(obj.m) một lần khác, ID sẽ thay đổi trở lại giá trị ban đầu đôi khi (nhưng không phải lúc nào). nếu nó không thay đổi trở lại, thì có vẻ như lặp lại các biểu thức trung gian khiến ID thay thế giữa hai giá trị.

đây là một số ví dụ trong đó id(obj.m) không thay đổi:

>>> print(obj.m); id(obj.m) 
<bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 
139675714789856 
>>> obj.m is None; id(obj.m) 
False 
139675714789856 
>>> obj.m.__func__.__name__; id(obj.m) 
'm' 
139675714789856 
>>> obj.m(); id(obj.m) 
139675714789856 

đây là một ví dụ trong đó id(obj.m) thay đổi, sau đó thay đổi lại:

>>> obj.m; id(obj.m); id(obj.m) 
<bound method Class.m of <__main__.Class object at 0x7f08c96058d0>> 
139675715407536 
139675714789856 

đây là một ví dụ trong đó id(obj.m) thay đổi, sau đó không thay đổi lại:

>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675715407536 
139675715407536 

ở đây là cùng một biểu thức, lặp đi lặp lại một vài lần để chứng minh hành vi xen kẽ:

>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675714789856 
139675714789856 
>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675715407536 
139675715407536 
>>> obj.m is obj.m; id(obj.m); id(obj.m) 
False 
139675714789856 
139675714789856 

do đó toàn bộ câu hỏi của tôi là

  • những loại thuộc tính có thể thay đổi danh tính của họ như một tác dụng phụ của biểu thức mà không sửa đổi các thuộc tính đó?
  • loại biểu thức nào kích hoạt những thay đổi đó?
  • cơ chế nào gây ra những thay đổi như vậy?
  • theo các điều kiện nào trong quá khứ được tái tạo?
  • tại sao bản sắc đầu tiên không được tái chế vô thời hạn, điều này sẽ tránh được tất cả các biến chứng này?
  • là bất kỳ tài liệu nào trong số tài liệu này?

Trả lời

5

loại thuộc tính nào có thể thay đổi nhận dạng của họ dưới dạng tác dụng phụ của các biểu thức không sửa đổi các thuộc tính đó?

Thuộc tính hoặc đối tượng chính xác hơn thực hiện descriptor protocol. Ví dụ: Class.__dict__ không phải là dict nhưng là dictproxy. Rõ ràng đối tượng này được tạo ra một lần nữa mỗi lần nó được yêu cầu. Tại sao? Có lẽ để cắt giảm chi phí của việc tạo ra các đối tượng cho đến khi nó là cần thiết để làm như vậy. Tuy nhiên, đây là một chi tiết thực hiện. Điều quan trọng là __dict__ hoạt động như tài liệu.

Ngay cả các phương pháp thể hiện thông thường cũng được xử lý bằng cách sử dụng các trình mô tả, giải thích tại sao obj.m is not obj.m. Thật thú vị, nếu bạn làm obj.m = obj.m bạn vĩnh viễn lưu trữ trình bao bọc phương thức đó vào cá thể và sau đó obj.m is obj.m. :-)

loại biểu thức nào kích hoạt những thay đổi đó?

Bất kỳ quyền truy cập nào vào thuộc tính đều có thể kích hoạt phương thức __get__() của bộ mô tả và phương pháp này luôn có thể trả về cùng một đối tượng hoặc trả về một đối tượng khác nhau.

cơ chế nào gây ra những thay đổi như vậy?

Thuộc tính/mô tả.

trong các điều kiện nào trong quá khứ được tái tạo?

Không chắc chắn ý của bạn là "tái chế". Bạn có nghĩa là "xử lý" hoặc "tái sử dụng"? Trong CPython, id của một đối tượng là vị trí bộ nhớ của nó. Nếu hai đối tượng kết thúc tại cùng một vị trí bộ nhớ tại các thời điểm khác nhau, chúng sẽ có cùng một số id. Do đó, hai tham chiếu có cùng số idtại các thời điểm khác nhau (ngay cả trong một tuyên bố đơn) không nhất thiết phải là cùng một đối tượng. Các triển khai Python khác sử dụng các quy tắc khác nhau để tạo ra id s. Ví dụ, tôi tin rằng Jython sử dụng các số nguyên tăng dần, cung cấp sự rõ ràng hơn về nhận dạng đối tượng.

tại sao bản sắc đầu tiên không được tái chế vô thời hạn, điều này sẽ tránh được tất cả các biến chứng này?

Có lẽ có một số lợi thế khi sử dụng các trình mô tả. Mã nguồn cho trình thông dịch Python có sẵn; nhìn vào đó nếu bạn muốn biết thêm chi tiết.

có bất kỳ tài liệu nào trong số này không?

No. Đây là chi tiết triển khai cụ thể của trình thông dịch CPython và không nên dựa vào. Các triển khai Python khác (bao gồm các phiên bản tương lai của CPython) có thể, và rất có thể, sẽ hoạt động khác nhau. Có sự khác biệt đáng kể giữa 2.x và 3.x CPython, ví dụ.

2

Khi bạn viết x.y phương thức ràng buộc hoặc không gắn kết được tạo. Đây là một đối tượng mới. Nó có thể đi bất cứ nơi nào trong bộ nhớ. Nếu bạn viết x.y và không sử dụng kết quả, refcnt của nó có thể đi đến số không được thu thập. Điều này có nghĩa là bộ nhớ có sẵn và có thể được sử dụng bởi x.y tiếp theo, có thể ở cùng một vị trí, nhưng không nhất thiết.

Lưu ý rằng CPython đảm bảo rất ít về nhận dạng đối tượng (nghĩa là bạn được đảm bảo rằng chỉ có một phiên bản Không có); nếu không, phần lớn những gì bạn thấy là các lựa chọn triển khai tùy ý có thể thay đổi.

+0

@ câu trả lời của kindall giải quyết hầu hết câu hỏi của tôi, nhưng điều này giải quyết câu hỏi phụ về ID tái chế. cảm ơn bạn. – nisavid

Các vấn đề liên quan