2009-07-22 32 views
98

Trong Java, ví dụ, chú thích @Override không chỉ cung cấp việc kiểm tra thời gian biên dịch ghi đè mà còn tạo ra mã tự tạo tài liệu tuyệt vời. Tôi chỉ đang tìm tài liệu (mặc dù nếu đó là chỉ báo cho một số người kiểm tra như pylint, đó là phần thưởng). Tôi có thể thêm một bình luận hoặc docstring một nơi nào đó, nhưng cách thành ngữ để chỉ ra một ghi đè trong Python là gì?Trong Python, làm cách nào để cho biết tôi đang ghi đè phương thức?

+10

Nói cách khác, bạn đừng bao giờ chỉ ra rằng bạn đang ghi đè một phương pháp? Hãy để nó cho người đọc để tìm ra rằng mình? – Bluu

+2

Vâng, tôi biết nó có vẻ như một tình huống dễ bị lỗi đến từ một ngôn ngữ biên dịch, nhưng bạn chỉ phải chấp nhận nó. Trong thực tế tôi đã không tìm thấy nó là một vấn đề lớn (Ruby trong trường hợp của tôi, không phải Python, nhưng cùng một ý tưởng) –

+0

Chắc chắn rồi, xong. Cả hai câu trả lời của Triptych và câu trả lời của mkorpela đều đơn giản, tôi thích điều đó, nhưng tinh thần rõ ràng hơn, và ngăn chặn những sai lầm một cách rõ ràng. – Bluu

Trả lời

140

UPDATE (2015/05/23): Dựa vào điều này và fwc: s câu trả lời tôi đã tạo ra một pip có thể cài đặt gói https://github.com/mkorpela/overrides

Từ bất cứ lúc nào tôi kết thúc ở đây nhìn vào câu hỏi này. Chủ yếu điều này xảy ra sau (một lần nữa) thấy cùng một lỗi trong cơ sở mã của chúng tôi: Ai đó đã quên một số lớp "giao diện" triển khai thực hiện trong khi đổi tên một phương thức trong "giao diện" ..

Vâng Python không phải là Java nhưng Python có quyền lực - và rõ ràng là tốt hơn là tiềm ẩn - và có những trường hợp cụ thể thực sự trong thế giới thực, nơi điều này sẽ giúp tôi.

Vì vậy, đây là bản phác thảo ghi đè trang trí. Điều này sẽ kiểm tra xem lớp được đưa ra như một tham số có cùng một phương thức (hay cái gì đó) tên như là phương thức được trang trí.

Nếu bạn có thể nghĩ ra giải pháp tốt hơn, hãy đăng nó ở đây!

def overrides(interface_class): 
    def overrider(method): 
     assert(method.__name__ in dir(interface_class)) 
     return method 
    return overrider 

Nó hoạt động như sau:

class MySuperInterface(object): 
    def my_method(self): 
     print 'hello world!' 


class ConcreteImplementer(MySuperInterface): 
    @overrides(MySuperInterface) 
    def my_method(self): 
     print 'hello kitty!' 

và nếu bạn làm một phiên bản bị lỗi nó sẽ nâng cao một lỗi khẳng định trong lớp tải:

class ConcreteFaultyImplementer(MySuperInterface): 
    @overrides(MySuperInterface) 
    def your_method(self): 
     print 'bye bye!' 

>> AssertionError!!!!!!! 
+11

Tuyệt vời. Điều này bắt gặp một lỗi chính tả trong lần đầu tiên tôi thử nó. Thanh danh. –

+1

Vì vậy, tôi đang sử dụng python 2.7 và nếu lớp của tôi mở rộng một loạt các lớp khác và, không giống như giao diện, tôi không muốn mã hóa cứng tên lớp chính xác có chứa chức năng giao diện, sau đó có thể làm việc này nói chung Tôi kế thừa từ nhiều hơn một lớp hoặc thứ tự độ phân giải phương pháp sẽ phá vỡ điều này? –

+0

Làm thế nào bạn viết một xác nhận để kiểm tra xem số đối số có giống nhau trong lớp cơ sở/giao diện và lớp con không? – Skarab

4

Theo như tôi biết, không có cách nào đặc biệt để chỉ ra ghi đè bằng Python. Bạn chỉ cần xác định phương thức và bao gồm một docstring, như mọi khi.

8

Python không phải là Java. Tất nhiên là không có thứ gì như kiểm tra thời gian biên dịch.

Tôi nghĩ rằng nhận xét trong chuỗi tài liệu rất nhiều. Điều này cho phép bất kỳ người dùng nào của phương thức của bạn nhập help(obj.method) và thấy rằng phương thức này là ghi đè.

Bạn cũng có thể mở rộng giao diện một cách rõ ràng với class Foo(Interface), cho phép người dùng nhập help(Interface.method) để có ý tưởng về chức năng mà phương pháp của bạn nhằm cung cấp.

+40

Điểm thực của '@ Override' trong Java không phải là tài liệu - nó bắt gặp lỗi khi bạn định ghi đè phương thức, nhưng cuối cùng xác định một phương thức mới (ví dụ: vì bạn viết sai chính tả tên, trong Java, nó có thể cũng xảy ra vì bạn đã sử dụng chữ ký sai, nhưng đây không phải là một vấn đề trong Python - nhưng lỗi chính tả vẫn là). –

+2

@ Pavel Minaev: Đúng, nhưng vẫn thuận tiện để có tài liệu, đặc biệt nếu bạn đang sử dụng trình soạn thảo IDE/văn bản không có chỉ báo tự động cho ghi đè (JDT của Eclipse cho chúng gọn gàng cùng với số dòng) . –

+2

@PavelMinaev Sai. Một trong những điểm chính của '@ Override' là tài liệu ngoài việc kiểm tra thời gian biên dịch. – siamii

8

Nếu bạn muốn điều này cho các mục đích tài liệu duy nhất, bạn có thể xác định override trang trí của riêng bạn:

def override(f): 
    return f 


class MyClass (BaseClass): 

    @override 
    def method(self): 
     pass 

Đây thực sự là không có gì nhưng eye-candy, trừ khi bạn tạo ghi đè (f) theo cách như vậy đó là thực sự kiểm tra ghi đè.

Nhưng sau đó, đây là Python, tại sao viết nó giống như Java?

+2

Người ta có thể thêm xác nhận thực tế thông qua kiểm tra để trang trí 'override'. –

+25

* Nhưng sau đó, đây là Python, tại sao viết nó như nó là Java? * Bởi vì một số ý tưởng trong Java là tốt và có giá trị mở rộng sang các ngôn ngữ khác? –

+4

Bởi vì khi bạn đổi tên một phương thức trong một siêu lớp, sẽ thật tuyệt khi biết rằng một số lớp con cấp 2 đã bị ghi đè. Chắc chắn, thật dễ dàng để kiểm tra, nhưng một chút trợ giúp từ trình phân tích cú pháp ngôn ngữ sẽ không bị tổn thương. – Abgan

21

Dưới đây là một thực hiện điều đó không có yêu cầu đặc tả của tên interface_class.

import inspect 
import re 

def overrides(method): 
    # actually can't do this because a method is really just a function while inside a class def'n 
    #assert(inspect.ismethod(method)) 

    stack = inspect.stack() 
    base_classes = re.search(r'class.+\((.+)\)\s*\:', stack[2][4][0]).group(1) 

    # handle multiple inheritance 
    base_classes = [s.strip() for s in base_classes.split(',')] 
    if not base_classes: 
     raise ValueError('overrides decorator: unable to determine base class') 

    # stack[0]=overrides, stack[1]=inside class def'n, stack[2]=outside class def'n 
    derived_class_locals = stack[2][0].f_locals 

    # replace each class name in base_classes with the actual class type 
    for i, base_class in enumerate(base_classes): 

     if '.' not in base_class: 
      base_classes[i] = derived_class_locals[base_class] 

     else: 
      components = base_class.split('.') 

      # obj is either a module or a class 
      obj = derived_class_locals[components[0]] 

      for c in components[1:]: 
       assert(inspect.ismodule(obj) or inspect.isclass(obj)) 
       obj = getattr(obj, c) 

      base_classes[i] = obj 


    assert(any(hasattr(cls, method.__name__) for cls in base_classes)) 
    return method 
+1

Một chút huyền diệu nhưng làm cho việc sử dụng thông thường dễ dàng hơn nhiều. Bạn có thể bao gồm các ví dụ sử dụng không? – Bluu

+0

Chi phí trung bình và tồi tệ nhất của việc sử dụng trang trí này, có thể được biểu thị như một so sánh với trình trang trí dựng sẵn như @classmethod hoặc @property? – larham1

+2

@ larham1 Trình trang trí này được thực hiện một lần, khi định nghĩa lớp được phân tích, không phải trên mỗi cuộc gọi. Do đó, chi phí thực hiện không liên quan, khi so sánh với thời gian chạy chương trình. – Abgan

2

Giống như những người khác đã nói không giống như Java không có thẻ @Overide tuy nhiên ở trên, bạn có thể tạo ra bằng cách sử dụng trang trí riêng tuy nhiên tôi sẽ đề nghị sử dụng getattrib() phương pháp toàn cầu thay vì sử dụng dict nội bộ để bạn có được một cái gì đó như như sau:

def Override(superClass): 
    def method(func) 
     getattr(superClass,method.__name__) 
    return method 

Nếu bạn muốn bạn có thể bắt getattr(), hãy thử bắt lỗi của riêng bạn nhưng tôi nghĩ phương pháp getattr tốt hơn trong trường hợp này.

Ngoài này bắt tất cả các mục bị ràng buộc vào một lớp học bao gồm các phương pháp lớp và vairables

0

Nghe là đơn giản nhất và làm việc theo Jython với các lớp Java:

class MyClass(SomeJavaClass): 
    def __init__(self): 
     setattr(self, "name_of_method_to_override", __method_override__) 

    def __method_override__(self, some_args): 
     some_thing_to_do() 
Các vấn đề liên quan