Trình trang trí cho phương thức lớp python
Tài liệu này nhằm mô tả cú pháp của trình trang trí và quy trình dẫn đến các quyết định được đưa ra. Nó không cố gắng bao gồm số lượng lớn các cú pháp thay thế tiềm năng, cũng không cố gắng liệt kê đầy đủ tất cả các mặt tích cực và tiêu cực của từng dạng Show
trừu tượngPhương thức hiện tại để chuyển đổi các hàm và phương thức (ví dụ: khai báo chúng dưới dạng một lớp hoặc phương thức tĩnh) rất khó xử và có thể dẫn đến mã khó hiểu. Lý tưởng nhất là các phép biến đổi này phải được thực hiện tại cùng một điểm trong mã mà bản thân việc khai báo được thực hiện. PEP này giới thiệu cú pháp mới để chuyển đổi khai báo hàm hoặc phương thức Động lựcPhương pháp hiện tại áp dụng một phép biến đổi cho một hàm hoặc phương thức đặt phép biến đổi thực tế sau thân hàm. Đối với các chức năng lớn, điều này tách một thành phần chính trong hành vi của chức năng khỏi định nghĩa của phần còn lại của giao diện bên ngoài của chức năng. Ví dụ def foo(self): perform method operation foo = classmethod(foo) Điều này trở nên khó đọc hơn với các phương thức dài hơn. Có vẻ như ít hơn Pythonic khi đặt tên hàm ba lần cho một khai báo về mặt khái niệm. Một giải pháp cho vấn đề này là di chuyển sự biến đổi của phương thức gần hơn với khai báo của chính phương thức đó. Mục đích của cú pháp mới là để thay thế def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo) với một giải pháp thay thế đặt trang trí trong khai báo của hàm @classmethod @synchronized(lock) def foo(cls): pass Việc sửa đổi các lớp theo cách này cũng có thể thực hiện được, mặc dù lợi ích không rõ ràng ngay lập tức. Gần như chắc chắn, bất cứ điều gì có thể được thực hiện với các trình trang trí lớp đều có thể được thực hiện bằng cách sử dụng siêu dữ liệu, nhưng việc sử dụng siêu dữ liệu đủ mơ hồ để có một số điểm thu hút để có một cách dễ dàng hơn để thực hiện các sửa đổi đơn giản đối với các lớp. Đối với Trăn 2. 4, chỉ các trình trang trí hàm/phương thức mới được thêm vào PEP 3129 đề xuất thêm các trình trang trí lớp kể từ Python 2. 6 Tại sao nó lại khó thế này?Hai trình trang trí ( @classmethod @synchronized(lock) def foo(cls): pass4 và @classmethod @synchronized(lock) def foo(cls): pass5) đã có sẵn trong Python kể từ phiên bản 2. 2. Người ta cho rằng kể từ khoảng thời gian đó, một số hỗ trợ cú pháp cho chúng cuối cùng sẽ được thêm vào ngôn ngữ. Với giả định này, người ta có thể thắc mắc tại sao lại khó đi đến thống nhất. Các cuộc thảo luận đã diễn ra gay gắt vào những thời điểm trong cả hai comp. lang thang. python và danh sách gửi thư python-dev về cách tốt nhất để triển khai các trình trang trí chức năng. Không có một lý do rõ ràng tại sao điều này nên như vậy, nhưng một số vấn đề dường như gây chia rẽ nhất
Lý lịchCó một thỏa thuận chung rằng hỗ trợ cú pháp là mong muốn đối với tình trạng hiện tại. Guido đã đề cập đến hỗ trợ cú pháp cho các nhà trang trí trong bài thuyết trình quan trọng về DevDay của anh ấy tại Hội nghị Python lần thứ 10, mặc dù sau đó anh ấy nói rằng đó chỉ là một trong số các tiện ích mở rộng mà anh ấy đã đề xuất ở đó “một cách nửa đùa nửa thật”. Michael Hudson đã nêu chủ đề trên @classmethod @synchronized(lock) def foo(cls): pass6 ngay sau hội nghị, quy kết cú pháp ban đầu trong ngoặc đơn cho một đề xuất trước đó trên @classmethod @synchronized(lock) def foo(cls): pass7 của Gareth McCaughan Trang trí lớp có vẻ như là một bước tiếp theo rõ ràng vì định nghĩa lớp và định nghĩa hàm giống nhau về mặt cú pháp, tuy nhiên Guido vẫn chưa thuyết phục và các trình trang trí lớp gần như chắc chắn sẽ không có trong Python 2. 4 Cuộc thảo luận tiếp tục diễn ra trên python-dev từ tháng 2 năm 2002 đến tháng 7 năm 2004. Hàng trăm và hàng trăm bài đăng đã được thực hiện, với những người đề xuất nhiều biến thể cú pháp có thể. Guido đã lấy một danh sách các đề xuất cho EuroPython 2004, nơi diễn ra một cuộc thảo luận. Sau đó, anh ấy quyết định rằng chúng tôi sẽ có cú pháp @decorator kiểu Java và cú pháp này xuất hiện lần đầu tiên trong 2. 4a2. Barry Warsaw đặt tên cho cú pháp này là 'pie-decorator', để tôn vinh vụ đấu súng Pie-thon Parrot xảy ra cùng thời điểm với cú pháp decorator và bởi vì @ trông hơi giống một chiếc bánh. Guido đã phác thảo trường hợp của mình trên Python-dev, bao gồm phần này trên một số (nhiều) biểu mẫu bị từ chối Trên tên 'Decorator'Đã có một số phàn nàn về việc chọn tên 'trang trí' cho tính năng này. Vấn đề chính là cái tên này không phù hợp với việc sử dụng nó trong sách GoF. Cái tên 'decorator' có lẽ được sử dụng nhiều hơn trong khu vực trình biên dịch - một cây cú pháp được duyệt và chú thích. Rất có thể một cái tên hay hơn sẽ xuất hiện Mục tiêu thiết kếCú pháp mới nên
Andrew Kuchling có liên kết đến một loạt các cuộc thảo luận về động cơ và trường hợp sử dụng trong blog của anh ấy. Đặc biệt đáng chú ý là danh sách các trường hợp sử dụng của Jim Huginin Cú pháp hiện tạiCú pháp hiện tại cho các trình trang trí chức năng như được triển khai trong Python 2. 4a2 là @dec2 @dec1 def func(arg1, arg2, ...): pass Điều này tương đương với def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)0 không có phép gán trung gian cho biến @dec2 @dec1 def func(arg1, arg2, ...): pass0. Các bộ trang trí gần khai báo hàm. Dấu hiệu @ cho thấy rõ rằng có điều gì đó mới đang diễn ra ở đây Lý do căn bản cho thứ tự ứng dụng (từ dưới lên trên) là nó phù hợp với thứ tự thông thường cho chức năng-ứng dụng. Trong toán học, thành phần của các hàm (g o f)(x) chuyển thành g(f(x)). Trong Python, @dec2 @dec1 def func(arg1, arg2, ...): pass1 dịch thành @dec2 @dec1 def func(arg1, arg2, ...): pass2 Câu lệnh trang trí bị giới hạn trong những gì nó có thể chấp nhận – các biểu thức tùy ý sẽ không hoạt động. Guido thích điều này vì cảm giác ruột thịt Cú pháp hiện tại cũng cho phép các khai báo của trình trang trí gọi một hàm trả về một trình trang trí def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)4 Điều này tương đương với def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)5 Cơ sở lý luận để có một hàm trả về một trình trang trí là phần sau dấu @ có thể được coi là một biểu thức (mặc dù bị hạn chế về mặt cú pháp chỉ là một hàm) và bất kỳ biểu thức nào mà biểu thức đó trả về đều được gọi là. Xem các đối số khai báo Cú pháp thay thếĐã có một số lượng lớn các cú pháp khác nhau được đề xuất – thay vì cố gắng làm việc thông qua các cú pháp riêng lẻ này, bạn nên chia cuộc thảo luận về cú pháp thành một số lĩnh vực. Cố gắng thảo luận riêng lẻ từng cú pháp có thể sẽ là một hành động điên rồ và tạo ra một PEP hoàn toàn khó sử dụng Vị trí trang tríĐiểm cú pháp đầu tiên là vị trí của các bộ trang trí. Đối với các ví dụ sau, chúng tôi sử dụng @syntax được sử dụng trong 2. 4a2 Các bộ trang trí trước câu lệnh def là lựa chọn thay thế đầu tiên và cú pháp được sử dụng trong 2. 4a2 def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)6 Đã có một số ý kiến phản đối về vị trí này – lý do chính là đây là trường hợp Python thực sự đầu tiên mà một dòng mã có ảnh hưởng đến dòng tiếp theo. Cú pháp có sẵn trong 2. 4a3 yêu cầu một bộ trang trí trên mỗi dòng (trong a2, nhiều bộ trang trí có thể được chỉ định trên cùng một dòng) và quyết định cuối cùng cho 2. 4 cuối cùng ở lại một trang trí trên mỗi dòng Mọi người cũng phàn nàn rằng cú pháp nhanh chóng trở nên khó sử dụng khi sử dụng nhiều bộ trang trí. Tuy nhiên, vấn đề được đưa ra là khả năng một số lượng lớn các bộ trang trí được sử dụng trên một chức năng duy nhất là nhỏ và do đó đây không phải là một vấn đề lớn. Một số ưu điểm của hình thức này là các trình trang trí sống bên ngoài phần thân phương thức – rõ ràng chúng được thực thi tại thời điểm hàm được xác định Một ưu điểm khác là tiền tố cho định nghĩa hàm phù hợp với ý tưởng biết về sự thay đổi đối với ngữ nghĩa của mã trước chính mã đó, do đó bạn biết cách diễn giải đúng ngữ nghĩa của mã mà không cần phải quay lại và thay đổi nhận thức ban đầu của mình nếu Guido quyết định rằng anh ấy muốn có các bộ trang trí trên hàng trước 'def', vì có cảm giác rằng một danh sách đối số dài có nghĩa là các bộ trang trí sẽ bị 'ẩn' Dạng thứ hai là các bộ trang trí giữa def và tên hàm hoặc tên hàm và danh sách đối số def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)7 Có một số phản đối đối với hình thức này. Đầu tiên là nó dễ dàng phá vỡ 'tính khả dụng' của nguồn - bạn không còn có thể tìm kiếm 'def foo(' và tìm định nghĩa của hàm. Phản đối thứ hai, nghiêm trọng hơn, là trong trường hợp có nhiều trình trang trí, cú pháp sẽ cực kỳ khó sử dụng Hình thức tiếp theo, đã có một số người ủng hộ mạnh mẽ, là đặt các bộ trang trí giữa danh sách đối số và dấu @dec2 @dec1 def func(arg1, arg2, ...): pass3 trong dòng 'def' def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)9 Guido đã tóm tắt các lập luận chống lại biểu mẫu này (nhiều trong số đó cũng áp dụng cho biểu mẫu trước đó) như
Hình thức tiếp theo là cú pháp trang trí đi vào bên trong thân phương thức ngay từ đầu, ở cùng một vị trí mà các chuỗi tài liệu hiện đang hoạt động def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)0 Sự phản đối chính đối với biểu mẫu này là nó yêu cầu "nhìn trộm bên trong" thân phương thức để xác định các bộ trang trí. Ngoài ra, mặc dù mã nằm trong thân phương thức nhưng nó không được thực thi khi phương thức được chạy. Guido cảm thấy rằng chuỗi tài liệu không phải là một ví dụ phản biện tốt và rất có thể trình trang trí 'chuỗi tài liệu' có thể giúp di chuyển chuỗi tài liệu ra bên ngoài thân hàm Hình thức cuối cùng là một khối mới bao quanh mã của phương thức. Đối với ví dụ này, chúng tôi sẽ sử dụng từ khóa 'trang trí', vì nó không hợp lý với @syntax def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)1 Hình thức này sẽ dẫn đến thụt đầu dòng không nhất quán cho các phương pháp trang trí và không trang trí. Ngoài ra, phần thân của phương thức được trang trí sẽ bắt đầu ba mức thụt lề trong biểu mẫu cú pháp
Tại sao @?Có một số lịch sử trong Java sử dụng @ ban đầu làm điểm đánh dấu trong các nhận xét Javadoc và sau đó trong Java 1. 5 cho các chú thích, tương tự như các trình trang trí Python. Thực tế là @ trước đây không được sử dụng làm mã thông báo trong Python cũng có nghĩa là rõ ràng là không có khả năng mã như vậy được phân tích cú pháp bởi phiên bản Python cũ hơn, dẫn đến các lỗi ngữ nghĩa có thể tinh vi. Điều đó cũng có nghĩa là sự mơ hồ về những gì là một trang trí và những gì không được loại bỏ. Điều đó nói rằng, @ vẫn là một lựa chọn khá độc đoán. Một số đã đề nghị sử dụng. thay thế Đối với các tùy chọn cú pháp sử dụng cú pháp giống như danh sách (bất kể nó xuất hiện ở đâu) để chỉ định các trình trang trí, một vài lựa chọn thay thế đã được đề xuất. def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)02, def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)03 và @dec2 @dec1 def func(arg1, arg2, ...): pass6 Thực hiện hiện tại, Lịch sửGuido đã yêu cầu một tình nguyện viên thực hiện cú pháp ưa thích của mình và Mark Russell đã bước lên và đăng một bản vá lên SF. Cú pháp mới này đã có sẵn trong 2. 4a2 @dec2 @dec1 def func(arg1, arg2, ...): pass Điều này tương đương với def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)0 mặc dù không có việc tạo biến trung gian có tên là @dec2 @dec1 def func(arg1, arg2, ...): pass0 Phiên bản được triển khai trong 2. 4a2 cho phép nhiều mệnh đề @dec2 @dec1 def func(arg1, arg2, ...): pass4 trên một dòng. Trong 2. 4a3, điều này đã được thắt chặt đến mức chỉ cho phép một người trang trí trên mỗi dòng Một bản vá trước đó của Michael Hudson thực hiện cú pháp list-after-def cũng vẫn đang hoạt động Sau 2. 4a2 đã được phát hành, trước phản ứng của cộng đồng, Guido tuyên bố rằng anh ấy sẽ kiểm tra lại đề xuất của cộng đồng, nếu cộng đồng có thể đưa ra sự đồng thuận của cộng đồng, một đề xuất phù hợp và triển khai. Sau một số lượng đáng kinh ngạc các bài đăng, thu thập một số lượng lớn các lựa chọn thay thế trong wiki Python, một sự đồng thuận của cộng đồng đã xuất hiện (bên dưới). Guido sau đó đã từ chối mẫu thay thế này, nhưng đã thêm
Đồng thuận cộng đồngPhần này ghi lại cú pháp J2 bị từ chối và được đưa vào để hoàn thiện lịch sử Sự đồng thuận nổi lên trên comp. lang thang. python là cú pháp J2 được đề xuất ("J2" là cách nó được tham chiếu trên trang wiki PythonDecorators). từ khóa mới def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)01 đặt trước một khối trang trí trước câu lệnh def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)08. Ví dụ def foo(cls): pass foo = synchronized(lock)(foo) foo = classmethod(foo)8 Các đối số chính cho cú pháp này thuộc học thuyết "tính dễ đọc". Tóm lại, chúng là
Robert Brewer đã viết một đề xuất chi tiết cho biểu mẫu này và Michael Sparks đã tạo ra một bản vá Như đã lưu ý trước đây, Guido đã từ chối biểu mẫu này, nêu rõ các vấn đề của anh ấy với nó trong một thông báo tới python-dev và comp. lang thang. con trăn ví dụPhần lớn cuộc thảo luận về @classmethod @synchronized(lock) def foo(cls): pass7 và danh sách gửi thư của @classmethod @synchronized(lock) def foo(cls): pass6 tập trung vào việc sử dụng các bộ trang trí như một cách rõ ràng hơn để sử dụng các nội trang @classmethod @synchronized(lock) def foo(cls): pass5 và @classmethod @synchronized(lock) def foo(cls): pass4. Khả năng này mạnh hơn nhiều so với khả năng đó. Phần này trình bày một số ví dụ về việc sử dụng Chúng ta có thể sử dụng trình trang trí trên lớp trong Python không?Trong Python, bộ trang trí có thể là hàm hoặc lớp . Trong cả hai trường hợp, trang trí thêm chức năng cho các chức năng hiện có. Khi chúng ta trang trí một hàm với một lớp, thì hàm đó sẽ trở thành một thể hiện của lớp.
Trang trí có thể được sử dụng trong lớp học?Chúng ta có thể dễ dàng tạo các công cụ trang trí bên trong một lớp và nó có thể dễ dàng truy cập đối với các lớp con của nó.
Phương thức @class trong Python là gì?Phương thức của lớp là phương thức được liên kết với lớp chứ không phải đối tượng của lớp . Họ có quyền truy cập vào trạng thái của lớp vì nó nhận tham số lớp trỏ đến lớp chứ không phải thể hiện đối tượng. Nó có thể sửa đổi trạng thái lớp sẽ áp dụng trên tất cả các phiên bản của lớp.
Hàm trang trí nào sau đây được sử dụng để tạo một phương thức lớp trong Python?Các trình trang trí @classmethod và @staticmethod được sử dụng để xác định các phương thức bên trong một không gian tên lớp không được kết nối với một thể hiện cụ thể của lớp đó |