Điểm kế thừa trong Python là gì?


83

Giả sử bạn gặp trường hợp sau

#include <iostream>

class Animal {
public:
    virtual void speak() = 0;
};

class Dog : public Animal {
    void speak() { std::cout << "woff!" <<std::endl; }
};

class Cat : public Animal {
    void speak() { std::cout << "meow!" <<std::endl; }
};

void makeSpeak(Animal &a) {
    a.speak();
}

int main() {
    Dog d;
    Cat c;
    makeSpeak(d);
    makeSpeak(c);
}

Như bạn có thể thấy, makeSpeak là một quy trình chấp nhận một đối tượng Động vật chung. Trong trường hợp này, Animal khá giống với một giao diện Java, vì nó chỉ chứa một phương thức ảo thuần túy. makeSpeak không biết bản chất của Động vật mà nó được truyền qua. Nó chỉ gửi cho nó tín hiệu "speak" và để lại ràng buộc muộn để quan tâm đến phương thức nào sẽ gọi: Cat :: speak () hoặc Dog :: speak (). Điều này có nghĩa là, đối với makeSpeak, kiến ​​thức về lớp con nào thực sự được truyền qua là không liên quan.

Nhưng còn Python thì sao? Hãy xem mã cho trường hợp tương tự trong Python. Xin lưu ý rằng tôi cố gắng giống với trường hợp C ++ nhất có thể trong giây lát:

class Animal(object):
    def speak(self):
        raise NotImplementedError()

class Dog(Animal):
    def speak(self):
        print "woff!"

class Cat(Animal):
    def speak(self):
        print "meow"

def makeSpeak(a):
    a.speak()

d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)

Bây giờ, trong ví dụ này, bạn thấy chiến lược tương tự. Bạn sử dụng tính năng thừa kế để tận dụng khái niệm phân cấp của cả Chó và Mèo là Động vật. Nhưng trong Python, không cần cấu trúc phân cấp này. Điều này hoạt động tốt như nhau

class Dog:
    def speak(self):
        print "woff!"

class Cat:
    def speak(self):
        print "meow"

def makeSpeak(a):
    a.speak()

d=Dog()
c=Cat()
makeSpeak(d)
makeSpeak(c)

Trong Python, bạn có thể gửi tín hiệu “nói” đến bất kỳ đối tượng nào bạn muốn. Nếu đối tượng có thể xử lý nó, nó sẽ được thực thi, nếu không nó sẽ tạo ra một ngoại lệ. Giả sử bạn thêm một lớp Máy bay vào cả hai mã và gửi một đối tượng Máy bay cho makeSpeak. Trong trường hợp C ++, nó sẽ không biên dịch, vì Máy bay không phải là một lớp dẫn xuất của Động vật. Trong trường hợp Python, nó sẽ đưa ra một ngoại lệ trong thời gian chạy, thậm chí có thể là một hành vi được mong đợi.

Mặt khác, giả sử bạn thêm một lớp MouthOfTruth với phương thức speak (). Trong trường hợp C ++, bạn sẽ phải cấu trúc lại hệ thống phân cấp của mình hoặc bạn sẽ phải xác định một phương thức makeSpeak khác để chấp nhận các đối tượng MouthOfTruth hoặc trong java, bạn có thể trích xuất hành vi vào một CanSpeakIface và triển khai giao diện cho từng đối tượng. Có nhiều giải pháp ...

Điều tôi muốn chỉ ra là tôi vẫn chưa tìm thấy một lý do nào để sử dụng kế thừa trong Python (ngoài các khung và cây ngoại lệ, nhưng tôi đoán rằng các chiến lược thay thế tồn tại). bạn không cần phải triển khai cấu trúc phân cấp có nguồn gốc từ cơ sở để thực hiện đa hình. Nếu bạn muốn sử dụng kế thừa để sử dụng lại việc triển khai, bạn có thể thực hiện điều tương tự thông qua ngăn chặn và ủy quyền, với lợi ích bổ sung là bạn có thể thay đổi nó trong thời gian chạy và bạn xác định rõ ràng giao diện của nội dung được chứa mà không gặp phải các tác dụng phụ ngoài ý muốn.

Vì vậy, cuối cùng, câu hỏi đặt ra: điểm kế thừa trong Python là gì?

Chỉnh sửa : cảm ơn vì những câu trả lời rất thú vị. Thật vậy, bạn có thể sử dụng nó để tái sử dụng mã, nhưng tôi luôn cẩn thận khi sử dụng lại việc triển khai. Nói chung, tôi có xu hướng làm các cây kế thừa rất nông hoặc không có cây nào cả, và nếu một chức năng phổ biến, tôi sẽ cấu trúc lại nó như một quy trình mô-đun chung và sau đó gọi nó từ từng đối tượng. Tôi thấy lợi thế của việc có một điểm thay đổi duy nhất (ví dụ: thay vì thêm vào Chó, Mèo, Moose, v.v., tôi chỉ thêm vào Động vật, đây là lợi thế cơ bản của việc kế thừa), nhưng bạn có thể đạt được điều tương tự với một chuỗi ủy quyền (ví dụ: a la JavaScript). Tôi không khẳng định nó tốt hơn, chỉ là một cách khác.

Tôi cũng tìm thấy một bài tương tự về vấn đề này.


18
-1: "bạn có thể đạt được điều tương tự với một chuỗi ủy nhiệm". Đúng, nhưng đau đớn hơn nhiều so với thừa kế. Bạn có thể đạt được điều tương tự mà không cần sử dụng bất kỳ định nghĩa lớp nào, chỉ cần nhiều hàm thuần túy phức tạp. Bạn có thể đạt được điều tương tự với hàng tá cách, tất cả đều ít đơn giản hơn so với kế thừa.
S.Lott

10
thực sự tôi đã nói "Tôi không khẳng định nó tốt hơn;)"
Stefano Borini

4
"Tôi chưa tìm thấy một lý do nào để sử dụng tính năng thừa kế trong python" ... chắc chắn nghe có vẻ như "giải pháp của tôi tốt hơn".
S.Lott

9
Xin lỗi nếu nó cho bạn ấn tượng này. Bài đăng của tôi nhằm mục đích nhận được phản hồi tích cực cho các câu chuyện tình huống thực tế về việc sử dụng kế thừa trong python mà cho đến ngày hôm nay, tôi không thể tìm thấy (chủ yếu là vì trong tất cả các chương trình python của tôi, tôi đã gặp phải những trường hợp không cần thiết và khi nào Tôi đã làm, đó là tình huống tôi đã giải thích ở trên).
Stefano Borini

2
Các phép phân loại trong thế giới thực hiếm khi là cơ sở tốt cho các ví dụ về hướng đối tượng.
Apalala 22/10/12

Câu trả lời:


81

Bạn đang đề cập đến run-time duck-gõ là kế thừa "ghi đè", tuy nhiên tôi tin rằng kế thừa có giá trị riêng như một phương pháp thiết kế và triển khai, là một phần không thể thiếu của thiết kế hướng đối tượng. Theo ý kiến ​​khiêm tốn của tôi, câu hỏi liệu bạn có thể đạt được điều gì đó khác không thì không liên quan lắm, bởi vì thực sự bạn có thể viết mã Python mà không cần các lớp, hàm và hơn thế nữa, nhưng câu hỏi là mã của bạn sẽ được thiết kế tốt, mạnh mẽ và dễ đọc như thế nào.

Tôi có thể đưa ra hai ví dụ về việc thừa kế là cách tiếp cận đúng theo quan điểm của tôi, tôi chắc rằng có nhiều ví dụ khác.

Đầu tiên, nếu bạn viết mã một cách khôn ngoan, hàm makeSpeak của bạn có thể muốn xác thực rằng đầu vào của nó thực sự là Động vật và không chỉ là "nó có thể nói", trong trường hợp đó, phương pháp tốt nhất sẽ là sử dụng kế thừa. Một lần nữa, bạn có thể làm điều đó theo những cách khác, nhưng đó là vẻ đẹp của thiết kế hướng đối tượng với tính kế thừa - mã của bạn sẽ "thực sự" kiểm tra xem đầu vào có phải là "động vật" hay không.

Thứ hai, và rõ ràng đơn giản hơn, là Tính đóng gói - một phần không thể thiếu khác của thiết kế hướng đối tượng. Điều này trở nên phù hợp khi tổ tiên có các thành viên dữ liệu và / hoặc các phương thức không trừu tượng. Lấy ví dụ ngớ ngẩn sau, trong đó tổ tiên có một hàm (speak_twice) gọi một hàm sau đó là trừu tượng:

class Animal(object):
    def speak(self):
        raise NotImplementedError()

    def speak_twice(self):
        self.speak()
        self.speak()

class Dog(Animal):
    def speak(self):
        print "woff!"

class Cat(Animal):
    def speak(self):
        print "meow"

Giả sử "speak_twice"là một tính năng quan trọng, bạn không muốn viết mã cho cả Dog và Cat, và tôi chắc rằng bạn có thể ngoại suy ví dụ này. Chắc chắn, bạn có thể triển khai một hàm độc lập trong Python sẽ chấp nhận một số đối tượng kiểu vịt, kiểm tra xem nó có hàm speak hay không và gọi nó hai lần, nhưng điều đó vừa không trang nhã vừa bỏ sót điểm số 1 (xác nhận nó là một Động vật). Thậm chí tệ hơn, và để tăng cường ví dụ về Encapsulation, điều gì sẽ xảy ra nếu một hàm thành viên trong lớp con cháu muốn sử dụng"speak_twice" ?

Nó thậm chí còn rõ ràng hơn nếu lớp tổ tiên có một thành viên dữ liệu, ví dụ như "number_of_legs"được sử dụng bởi các phương thức không trừu tượng trong tổ tiên "print_number_of_legs", nhưng được khởi tạo trong phương thức khởi tạo của lớp con cháu (ví dụ: Dog sẽ khởi tạo nó bằng 4 trong khi Snake sẽ khởi tạo nó với 0).

Một lần nữa, tôi chắc chắn rằng có vô số ví dụ khác, nhưng về cơ bản mọi phần mềm (đủ lớn) dựa trên thiết kế hướng đối tượng rắn sẽ yêu cầu kế thừa.


3
Đối với trường hợp đầu tiên, điều đó có nghĩa là bạn đang kiểm tra các kiểu thay vì kiểm tra hành vi, điều này khá khó nghe. Đối với trường hợp thứ hai, tôi đồng ý, và về cơ bản bạn đang thực hiện cách tiếp cận "khuôn khổ". Bạn đang tái chế việc triển khai speak_twice, không chỉ giao diện mà còn để ghi đè, bạn có thể sống mà không cần kế thừa khi xem xét python.
Stefano Borini

9
Bạn có thể sống mà không có nhiều thứ, như các lớp và hàm, nhưng câu hỏi là điều gì làm cho mã trở nên tuyệt vời. Tôi nghĩ thừa kế có.
Roee Adler

@Stefano Borini - Có vẻ như bạn đang thực hiện một cách tiếp cận rất "dựa trên quy tắc". Tuy nhiên, khuôn sáo cũ vẫn đúng: chúng được tạo ra để bị phá vỡ. :-)
Jason Baker

@Jason Baker - Tôi có xu hướng thích các quy tắc vì chúng báo cáo sự khôn ngoan thu được từ kinh nghiệm (ví dụ: sai lầm), nhưng tôi không thích sự sáng tạo bị chúng cản trở. Vì vậy, tôi đồng ý với tuyên bố của bạn.
Stefano Borini

1
Tôi không thấy ví dụ này quá rõ ràng - các ví dụ về động vật, ô tô và hình dạng thực sự hấp dẫn đối với những lý do đó :) Điều duy nhất quan trọng với IMHO là bạn có muốn kế thừa triển khai hay không. Nếu vậy, các quy tắc trong python thực sự tương tự như java / C ++; sự khác biệt chủ yếu là để kế thừa cho các giao diện. Trong trường hợp đó, kiểu gõ vịt thường là giải pháp - nhiều hơn là thừa kế.
David Cournapeau

12

Kế thừa trong Python là tất cả về việc sử dụng lại mã. Phân tích chức năng chung thành một lớp cơ sở và triển khai các chức năng khác nhau trong các lớp dẫn xuất.


11

Kế thừa trong Python là một tiện ích hơn bất kỳ thứ gì khác. Tôi thấy rằng nó được sử dụng tốt nhất để cung cấp một lớp có "hành vi mặc định".

Thật vậy, có một cộng đồng đáng kể các nhà phát triển Python tranh luận chống lại việc sử dụng kế thừa. Dù bạn làm gì, đừng chỉ đừng lạm dụng nó. Có một hệ thống phân cấp lớp quá phức tạp là một cách chắc chắn để được gắn nhãn "lập trình viên Java", và bạn không thể có điều đó. :-)


8

Tôi nghĩ rằng điểm kế thừa trong Python không phải là làm cho mã biên dịch, nó là vì lý do thực sự của sự kế thừa là mở rộng lớp này thành một lớp con khác và ghi đè logic trong lớp cơ sở. Tuy nhiên, cách gõ vịt trong Python làm cho khái niệm "giao diện" trở nên vô dụng, vì bạn chỉ có thể kiểm tra xem phương thức có tồn tại trước khi thực hiện mà không cần sử dụng giao diện để giới hạn cấu trúc lớp hay không.


3
Ghi đè có chọn lọc là lý do cho sự kế thừa. Nếu bạn định ghi đè mọi thứ, đó là một trường hợp đặc biệt kỳ lạ.
S.Lott

1
Ai sẽ ghi đè lên mọi thứ? bạn có thể nghĩ trăn giống như tất cả các phương pháp là công khai và ảo
bashmohandes

1
@bashmohandes: Tôi sẽ không bao giờ ghi đè mọi thứ. Nhưng câu hỏi cho thấy một trường hợp thoái hóa, nơi mọi thứ bị ghi đè; trường hợp đặc biệt kỳ lạ này là cơ sở cho câu hỏi. Vì nó không bao giờ xảy ra trong thiết kế OO thông thường, câu hỏi này là vô nghĩa.
S.Lott

7

Tôi nghĩ rằng rất khó để đưa ra một câu trả lời cụ thể, có ý nghĩa với những ví dụ trừu tượng như vậy ...

Để đơn giản hóa, có hai loại kế thừa: giao diện và thực thi. Nếu bạn cần kế thừa việc triển khai, thì python không quá khác biệt so với các ngôn ngữ OO được nhập tĩnh như C ++.

Theo kinh nghiệm của tôi, sự kế thừa của giao diện là nơi có sự khác biệt lớn, dẫn đến hậu quả cơ bản đối với việc thiết kế phần mềm của bạn. Các ngôn ngữ như Python không buộc bạn phải sử dụng kế thừa trong trường hợp đó và tránh kế thừa là một điểm tốt trong hầu hết các trường hợp, vì rất khó để sửa chữa một lựa chọn thiết kế sai ở đó sau này. Đó là một điểm nổi tiếng được nêu ra trong bất kỳ cuốn sách OOP hay nào.

Có những trường hợp nên sử dụng tính năng kế thừa cho các giao diện được khuyến khích bằng Python, ví dụ như cho các trình cắm thêm, v.v. (zope, trac, twister). Python 2.6 trở lên có các lớp ABC để giải quyết vấn đề này .


6

Không phải sự kế thừa mà việc gõ vịt trở nên vô nghĩa, đó là các giao diện - giống như giao diện bạn đã chọn khi tạo một lớp động vật trừu tượng.

Nếu bạn đã sử dụng một lớp động vật giới thiệu một số hành vi thực sự cho con cháu của nó sử dụng, thì các lớp chó và mèo giới thiệu một số hành vi bổ sung sẽ có lý do cho cả hai lớp. Chỉ trong trường hợp lớp tổ tiên không đóng góp mã thực sự nào cho các lớp con thì đối số của bạn là đúng.

Bởi vì Python có thể biết trực tiếp các khả năng của bất kỳ đối tượng nào và bởi vì những khả năng đó có thể thay đổi được ngoài định nghĩa lớp, ý tưởng sử dụng một giao diện trừu tượng thuần túy để "nói" cho chương trình biết phương thức nào có thể được gọi là hơi vô nghĩa. Nhưng đó không phải là điểm duy nhất, hay thậm chí là điểm kế thừa chính.


5

Trong C ++ / Java / etc, tính đa hình là do kế thừa. Hãy từ bỏ niềm tin không đúng đắn đó và các ngôn ngữ động sẽ mở ra cho bạn.

Về cơ bản, trong Python không có giao diện nào giống như "sự hiểu biết rằng một số phương thức nhất định có thể gọi được". Khá đẹp mắt và nghe có vẻ hàn lâm, phải không? Nó có nghĩa là bởi vì bạn gọi là "speak", bạn rõ ràng mong đợi rằng đối tượng nên có một phương thức "speak". Đơn giản phải không? Điều này rất Liskov-ian ở chỗ người dùng của một lớp xác định giao diện của nó, một khái niệm thiết kế tốt dẫn bạn đến TDD lành mạnh hơn.

Vì vậy, những gì còn lại là, như một người đăng khác đã quản lý một cách lịch sự để tránh nói, một thủ thuật chia sẻ mã. Bạn có thể viết cùng một hành vi vào mỗi lớp "con", nhưng điều đó sẽ là thừa. Dễ dàng kế thừa hoặc kết hợp chức năng bất biến trên hệ thống phân cấp kế thừa. Nói chung, mã DRY-er nhỏ hơn, tốt hơn.


2

Tôi không thấy có nhiều điểm trong việc thừa kế.

Mỗi khi tôi sử dụng tính năng thừa kế trong các hệ thống thực, tôi đều bị bỏng vì nó dẫn đến một mạng lưới phụ thuộc rối rắm, hoặc tôi chỉ đơn giản nhận ra rằng tôi sẽ tốt hơn rất nhiều nếu không có nó. Bây giờ, tôi tránh nó nhiều nhất có thể. Tôi chỉ đơn giản là không bao giờ sử dụng nó.

class Repeat:
    "Send a message more than once"
    def __init__(repeat, times, do):
        repeat.times = times
        repeat.do = do

    def __call__(repeat):
        for i in xrange(repeat.times):
             repeat.do()

class Speak:
    def __init__(speak, animal):
        """
        Check that the animal can speak.

        If not we can do something about it (e.g. ignore it).
        """
        speak.__call__ = animal.speak

    def twice(speak):
        Repeat(2, speak)()

class Dog:
     def speak(dog):
         print "Woof"

class Cat:
     def speak(cat):
         print "Meow"

>>> felix = Cat()
>>> Speak(felix)()
Meow

>>> fido = Dog()
>>> speak = Speak(fido)
>>> speak()
Woof

>>> speak.twice()
Woof

>>> speak_twice = Repeat(2, Speak(felix))
>>> speak_twice()
Meow
Meow

James Gosling đã từng được hỏi trong một cuộc họp báo một câu hỏi dọc theo dòng: "Nếu bạn có thể quay lại và làm Java theo cách khác, bạn sẽ bỏ qua điều gì?". Câu trả lời của anh ấy là "Lớp học", trong đó có tiếng cười. Tuy nhiên, anh ấy rất nghiêm túc và giải thích rằng thực sự thì vấn đề không phải là các lớp học mà là sự kế thừa.

Tôi xem nó giống như một sự phụ thuộc vào ma túy - nó giúp bạn nhanh chóng sửa chữa và cảm thấy dễ chịu, nhưng cuối cùng, nó khiến bạn rối tung lên. Ý tôi là đó là một cách thuận tiện để sử dụng lại mã, nhưng nó buộc phải ghép nối không lành mạnh giữa lớp con và lớp cha. Những thay đổi đối với cha mẹ có thể phá vỡ đứa trẻ. Đứa trẻ phụ thuộc vào cha mẹ cho một số chức năng nhất định và không thể thay đổi chức năng đó. Do đó, chức năng được cung cấp bởi con cũng được gắn với cha mẹ - bạn chỉ có thể có cả hai.

Tốt hơn là cung cấp một lớp đối mặt với khách hàng duy nhất cho một giao diện triển khai giao diện, sử dụng chức năng của các đối tượng khác được cấu tạo tại thời điểm xây dựng. Thực hiện điều này thông qua các giao diện được thiết kế phù hợp, tất cả các khớp nối có thể được loại bỏ và chúng tôi cung cấp một API có khả năng tổng hợp cao (Điều này không có gì mới - hầu hết các lập trình viên đã làm điều này, chỉ là chưa đủ). Lưu ý rằng lớp triển khai không được chỉ đơn giản là hiển thị chức năng, nếu không, máy khách chỉ nên sử dụng trực tiếp các lớp đã soạn - nó phải làm điều gì đó mới bằng cách kết hợp chức năng đó.

Có lập luận từ phe kế thừa rằng việc triển khai ủy quyền thuần túy bị ảnh hưởng bởi vì chúng yêu cầu nhiều phương pháp 'keo' chỉ đơn giản là truyền các giá trị thông qua một 'chuỗi' ủy quyền. Tuy nhiên, đây chỉ đơn giản là phát minh lại một thiết kế giống như kế thừa bằng cách sử dụng ủy quyền. Các lập trình viên có quá nhiều năm tiếp xúc với các thiết kế dựa trên kế thừa đặc biệt dễ bị rơi vào cái bẫy này, vì nếu không nhận ra nó, họ sẽ nghĩ về cách họ sẽ triển khai thứ gì đó bằng cách sử dụng kế thừa và sau đó chuyển đổi nó thành ủy quyền.

Việc tách các mối quan tâm một cách thích hợp như đoạn mã trên không yêu cầu phương pháp keo, vì mỗi bước thực sự làm tăng thêm giá trị , vì vậy chúng không thực sự là phương pháp 'keo' (nếu chúng không tăng giá trị, thiết kế bị sai sót).

Nó tóm gọn lại điều này:

  • Đối với mã có thể tái sử dụng, mỗi lớp chỉ nên làm một việc (và làm tốt điều đó).

  • Kế thừa tạo ra các lớp thực hiện nhiều hơn một việc, bởi vì chúng được trộn lẫn với các lớp cha.

  • Do đó, việc sử dụng kế thừa làm cho các lớp khó sử dụng lại.


1

Bạn có thể tìm hiểu về kế thừa bằng Python và khá nhiều ngôn ngữ khác. Tuy nhiên, đó là tất cả về tái sử dụng mã và đơn giản hóa mã.

Chỉ là một thủ thuật ngữ nghĩa, nhưng sau khi xây dựng các lớp và các lớp cơ sở của mình, bạn thậm chí không cần biết những gì có thể xảy ra với đối tượng của mình để xem liệu bạn có thể làm được hay không.

Giả sử bạn có d là Chó phân loại Động vật.

command = raw_input("What do you want the dog to do?")
if command in dir(d): getattr(d,command)()

Nếu bất kỳ thứ gì người dùng nhập vào có sẵn, mã sẽ chạy theo phương thức thích hợp.

Sử dụng điều này, bạn có thể tạo ra bất kỳ sự kết hợp quái dị lai giữa Động vật có vú / Bò sát / Chim mà bạn muốn và bây giờ bạn có thể khiến nó nói 'Vỏ cây!' trong khi bay và thè cái lưỡi chẻ của nó ra và nó sẽ xử lý nó đúng cách! Hãy vui vẻ với nó!


1

Một điểm nhỏ nữa là ví dụ thứ 3 của op, bạn không thể gọi isinstance (). Ví dụ: chuyển ví dụ thứ 3 của bạn sang một đối tượng khác và nhập "Động vật" một cuộc gọi nói trên nó. Nếu không làm vậy, bạn sẽ phải kiểm tra loại chó, loại mèo, v.v. Không chắc liệu kiểm tra phiên bản có thực sự là "Pythonic" hay không, vì ràng buộc muộn. Nhưng sau đó bạn sẽ phải thực hiện một số cách để AnimalControl không cố ném các loại Cheeseburger vào xe tải, vì Cheeseburgers không nói được.

class AnimalControl(object):
    def __init__(self):
        self._animalsInTruck=[]

    def catachAnimal(self,animal):
        if isinstance(animal,Animal):
            animal.speak()  #It's upset so it speak's/maybe it should be makesNoise
            if not self._animalsInTruck.count <=10:
                self._animalsInTruck.append(animal) #It's then put in the truck.
            else:
                #make note of location, catch you later...
        else:
            return animal #It's not an Animal() type / maybe return False/0/"message"

0

Các lớp trong Python về cơ bản chỉ là cách nhóm một loạt các hàm và dữ liệu. Chúng khác với các lớp trong C ++ và tương tự ..

Tôi hầu như đã thấy kế thừa được sử dụng để ghi đè các phương thức của siêu lớp. Ví dụ, có lẽ cách sử dụng kế thừa của Python nhiều hơn sẽ là ..

from world.animals import Dog

class Cat(Dog):
    def speak(self):
        print "meow"

Tất nhiên mèo không phải là một loại chó, nhưng tôi có (bên thứ ba) này Doglớp mà hoạt động hoàn hảo, ngoại trừ những speakphương pháp mà tôi muốn ghi đè lên - điều này giúp tiết kiệm được triển khai lại toàn bộ lớp, chỉ cần như vậy nó Meows. Một lần nữa, mặc dù Catkhông phải là một loạiDog , nhưng một con mèo được thừa hưởng rất nhiều thuộc tính ..

Một ví dụ tốt hơn (thực tế) về việc ghi đè một phương thức hoặc thuộc tính là cách bạn thay đổi tác nhân người dùng cho urllib. Về cơ bản, bạn phân lớp urllib.FancyURLopenervà thay đổi thuộc tính phiên bản ( từ tài liệu ):

import urllib

class AppURLopener(urllib.FancyURLopener):
    version = "App/1.7"

urllib._urlopener = AppURLopener()

Một cách khác các ngoại lệ được sử dụng là Exceptions, khi việc kế thừa được sử dụng theo cách "thích hợp" hơn:

class AnimalError(Exception):
    pass

class AnimalBrokenLegError(AnimalError):
    pass

class AnimalSickError(AnimalError):
    pass

.. sau đó bạn có thể AnimalErrorbắt tất cả các ngoại lệ kế thừa từ nó hoặc một ngoại lệ cụ thể như AnimalBrokenLegError


6
Tôi… hơi bối rối trước ví dụ đầu tiên của bạn. Lần cuối tôi kiểm tra, mèo không phải là một loại chó, vì vậy tôi không chắc bạn đang cố gắng thể hiện mối quan hệ nào. :-)
Ben Blank

1
Bạn đang bối rối với nguyên tắc Liskov: Mèo KHÔNG PHẢI là Chó. Có thể được sử dụng trong trường hợp này, nhưng điều gì sẽ xảy ra nếu lớp Chó thay đổi và nhận được, ví dụ, trường "Chì", là trường vô nghĩa đối với mèo?
Dmitry Risenberg

1
Chà nếu không có lớp cơ sở Động vật, thì cách thay thế của bạn là tái hiện lại toàn bộ .. Tôi không nói đó là phương pháp hay nhất (nếu có lớp cơ sở Động vật, hãy sử dụng nó), nhưng nó hoạt động và được sử dụng phổ biến ( đó là cách đề nghị của việc thay đổi user-agent của urllib, theo ví dụ tôi thêm)
dBR
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.