Giải thích chính xác từ Armin Ronacher ở trên, mở rộng câu trả lời của anh ấy để những người mới bắt đầu như tôi hiểu rõ về nó:
Sự khác biệt trong các phương thức được định nghĩa trong một lớp, cho dù là phương thức tĩnh hay phương thức (có một loại phương thức lớp khác - không được thảo luận ở đây nên bỏ qua nó), trong thực tế liệu chúng có bị ràng buộc với thể hiện của lớp hay không. Ví dụ, giả sử phương thức có nhận được tham chiếu đến thể hiện của lớp trong thời gian chạy không
class C:
a = []
def foo(self):
pass
C # this is the class object
C.a # is a list object (class property object)
C.foo # is a function object (class property object)
c = C()
c # this is the class instance
Các __dict__
bất động sản từ điển của đối tượng lớp giữ tham chiếu đến tất cả các thuộc tính và phương thức của một đối tượng lớp và do đó
>>> C.__dict__['foo']
<function foo at 0x17d05b0>
phương pháp foo có thể truy cập như trên. Một điểm quan trọng cần lưu ý ở đây là mọi thứ trong python đều là một đối tượng và vì vậy các tài liệu tham khảo trong từ điển ở trên đều tự trỏ đến các đối tượng khác. Hãy để tôi gọi chúng là Đối tượng thuộc tính lớp - hoặc là CPO trong phạm vi câu trả lời của tôi cho ngắn gọn.
Nếu CPO là một mô tả, thì người phiên dịch python gọi __get__()
phương thức của CPO để truy cập giá trị mà nó chứa.
Để xác định xem CPO có phải là mô tả hay không, trình thông dịch python kiểm tra xem nó có thực hiện giao thức mô tả hay không. Để thực hiện giao thức mô tả là thực hiện 3 phương thức
def __get__(self, instance, owner)
def __set__(self, instance, value)
def __delete__(self, instance)
ví dụ
>>> C.__dict__['foo'].__get__(c, C)
Ở đâu
self
là CPO (nó có thể là một thể hiện của danh sách, str, hàm, v.v.) và được cung cấp bởi bộ thực thi
instance
là thể hiện của lớp nơi CPO này được xác định (đối tượng 'c' ở trên) và cần phải được cung cấp bởi chúng tôi
owner
là lớp nơi CPO này được xác định (đối tượng lớp 'C' ở trên) và cần được cung cấp bởi chúng tôi. Tuy nhiên điều này là do chúng tôi đang gọi nó trên CPO. khi chúng ta gọi nó trên cá thể, chúng ta không cần cung cấp cái này vì bộ thực thi có thể cung cấp thể hiện hoặc lớp của nó (đa hình)
value
là giá trị dự định cho CPO và cần được cung cấp bởi chúng tôi
Không phải tất cả CPO là mô tả. Ví dụ
>>> C.__dict__['foo'].__get__(None, C)
<function C.foo at 0x10a72f510>
>>> C.__dict__['a'].__get__(None, C)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__get__'
Điều này là do lớp danh sách không thực hiện giao thức mô tả.
Do đó, đối số tự nhập c.foo(self)
là bắt buộc vì chữ ký phương thức của nó thực sự C.__dict__['foo'].__get__(c, C)
là như vậy (như đã giải thích ở trên, C là không cần thiết vì nó có thể được tìm ra hoặc đa hình) Và đây cũng là lý do tại sao bạn nhận được TypeError nếu bạn không vượt qua đối số yêu cầu đó.
Nếu bạn nhận thấy phương thức vẫn được tham chiếu qua lớp Object C và liên kết với thể hiện của lớp được thực hiện thông qua việc chuyển một bối cảnh dưới dạng đối tượng thể hiện vào hàm này.
Điều này khá tuyệt vời vì nếu bạn chọn không giữ ngữ cảnh hoặc không ràng buộc với thể hiện, tất cả những gì cần thiết là viết một lớp để bọc CPO mô tả và ghi đè __get__()
phương thức của nó để không yêu cầu ngữ cảnh. Lớp mới này là cái mà chúng ta gọi là trang trí và được áp dụng thông qua từ khóa@staticmethod
class C(object):
@staticmethod
def foo():
pass
Sự vắng mặt của bối cảnh trong CPO được bao bọc mới foo
không gây ra lỗi và có thể được xác minh như sau:
>>> C.__dict__['foo'].__get__(None, C)
<function foo at 0x17d0c30>
Ca sử dụng của một phương thức tĩnh là nhiều hơn một không gian tên và khả năng duy trì mã (lấy nó ra khỏi một lớp và làm cho nó có sẵn trong toàn bộ mô-đun, v.v.).
Có thể tốt hơn để viết các phương thức tĩnh thay vì các phương thức cá thể bất cứ khi nào có thể, trừ khi bạn cần phải tiếp cận các phương thức (như các biến đối tượng truy cập, biến lớp, v.v.). Một lý do là để giảm bớt việc thu gom rác bằng cách không giữ các tham chiếu không mong muốn đến các đối tượng.