การสืบทอดของ Python เป็นรูปแบบของ "is-a" ของการสืบทอดหรือสไตล์การแต่งหรือไม่?


10

เนื่องจาก Python อนุญาตให้ใช้การสืบทอดหลายแบบการถ่ายทอดทางพันธุกรรมใน Python มีลักษณะอย่างไร

ในภาษาที่มีการสืบทอดเดียวเช่น Java การสืบทอดจะถูกใช้เมื่อคุณสามารถพูดได้ว่าวัตถุหนึ่ง "is-a" ของวัตถุอื่นและคุณต้องการแบ่งปันรหัสระหว่างวัตถุ (จากวัตถุหลักไปยังวัตถุลูก) ตัวอย่างเช่นคุณสามารถพูดได้ว่าDogเป็นAnimal:

public class Animal {...}
public class Dog extends Animal {...}

แต่เนื่องจาก Python รองรับการสืบทอดหลายแบบเราจึงสามารถสร้างวัตถุโดยการรวมวัตถุอื่นเข้าด้วยกัน ลองพิจารณาตัวอย่างด้านล่าง:

class UserService(object):
    def validate_credentials(self, username, password):
        # validate the user credentials are correct
        pass


class LoggingService(object):
    def log_error(self, error):
        # log an error
        pass


class User(UserService, LoggingService):
    def __init__(self, username, password):
        self.username = username
        self.password = password

    def authenticate(self):
        if not super().validate_credentials(self.username, self.password):
            super().log_error('Invalid credentials supplied')
            return False
         return True

นี่เป็นการใช้งานที่ยอมรับได้หรือดีสำหรับการสืบทอดหลาย ๆ อย่างใน Python หรือไม่? แทนที่จะพูดว่ามรดกคือเมื่อวัตถุหนึ่ง "เป็น-เป็น" ของวัตถุอื่นเราจะสร้างUserรูปแบบประกอบด้วยและUserServiceLoggingService

ทั้งหมดตรรกะสำหรับฐานข้อมูลหรือเครือข่ายการดำเนินงานจะถูกเก็บไว้แยกจากUserรูปแบบโดยการวางไว้ในวัตถุและเก็บทุกตรรกะสำหรับการเข้าสู่ระบบในUserServiceLoggingService

ฉันเห็นปัญหาบางอย่างกับวิธีนี้คือ:

  • สิ่งนี้สร้างวัตถุของพระเจ้าหรือไม่? ตั้งแต่Userสืบทอดจากหรือประกอบด้วยUserServiceและLoggingServiceเป็นจริงตามหลักการของความรับผิดชอบเดียวหรือไม่
  • ในการเข้าถึงวิธีการบนวัตถุพาเรนต์ / บรรทัดถัดไป (เช่นUserService.validate_credentialsเราต้องใช้superสิ่งนี้ทำให้ยากขึ้นนิดหน่อยที่จะเห็นว่าวัตถุใดที่จะจัดการกับวิธีนี้และไม่ชัดเจนเหมือนพูด สร้างอินสแตนซ์UserServiceและทำสิ่งที่ต้องการself.user_service.validate_credentials

สิ่งที่จะเป็นวิธี Pythonic ในการใช้รหัสข้างต้น?

คำตอบ:


9

การสืบทอดของ Python เป็นรูปแบบของ "is-a" ของการสืบทอดหรือสไตล์การแต่งหรือไม่?

Python รองรับทั้งสองสไตล์ คุณกำลังแสดงให้เห็นถึงความสัมพันธ์ของการแต่งเพลงที่มีซึ่งผู้ใช้มีฟังก์ชั่นการบันทึกจากแหล่งข้อมูลหนึ่งและการตรวจสอบข้อมูลรับรองจากแหล่งอื่น The LoggingServiceและUserServiceฐานเป็น mixins: พวกเขามีฟังก์ชั่นและไม่ได้ตั้งใจที่จะยกตัวอย่างด้วยตัวเอง

โดยการเขียนมิกซ์อินในประเภทคุณมีผู้ใช้ที่สามารถเข้าสู่ระบบได้ แต่ใครจะต้องเพิ่มฟังก์ชันการสร้างอินสแตนซ์ของตัวเอง

นี่ไม่ได้หมายความว่าเราไม่สามารถยึดติดกับมรดกเดียว Python ก็รองรับเช่นกัน หากความสามารถในการพัฒนาของคุณถูกขัดขวางโดยความซับซ้อนของการสืบทอดหลายแบบคุณสามารถหลีกเลี่ยงได้จนกว่าคุณจะรู้สึกสะดวกสบายมากขึ้นหรือมาถึงจุดออกแบบที่คุณเชื่อว่ามันจะคุ้มค่ากับการแลกเปลี่ยน

สิ่งนี้สร้างวัตถุของพระเจ้าหรือไม่?

การบันทึกดูเหมือนจะค่อนข้างเป็นวง - หลามมีโมดูลการบันทึกของตัวเองพร้อมกับวัตถุคนตัดไม้และการประชุมคือมีคนตัดไม้หนึ่งคนต่อโมดูล

แต่ตั้งค่าโมดูลการบันทึกไว้ บางทีนี่อาจเป็นการละเมิดความรับผิดชอบเพียงอย่างเดียว แต่บางทีในบริบทเฉพาะของคุณมันเป็นสิ่งสำคัญในการกำหนดผู้ใช้ ความรับผิดชอบที่ลดลงอาจขัดแย้งกัน แต่หลักการที่กว้างกว่าก็คือ Python อนุญาตให้ผู้ใช้ทำการตัดสินใจ

ชัดเจนน้อยลงหรือไม่

superจำเป็นเฉพาะเมื่อคุณต้องการมอบอำนาจให้ผู้ปกครองในการแก้ไขวิธีการสั่งซื้อ (MRO) จากภายในของฟังก์ชั่นที่มีชื่อเดียวกัน การใช้มันแทนการเข้ารหัสแบบยาก ๆ การเรียกใช้เมธอดของผู้ปกครองเป็นวิธีปฏิบัติที่ดีที่สุด superแต่ถ้าคุณไม่ได้ไปยากรหัสแม่คุณไม่จำเป็นต้อง

self.validate_credentialsในตัวอย่างของคุณที่นี่คุณจะต้องทำ selfไม่ชัดเจนมากขึ้นจากมุมมองของคุณ พวกเขาทั้งสองติดตาม MRO ฉันจะใช้แต่ละอย่างตามความเหมาะสม

หากคุณโทรauthenticateมาvalidate_credentialsแทนคุณจะต้องใช้super(หรือรหัสฮาร์ดแม่) เพื่อหลีกเลี่ยงข้อผิดพลาดในการเรียกซ้ำ

ข้อเสนอแนะรหัสทางเลือก

ดังนั้นสมมติว่าซีแมนทิกส์นั้นใช้ได้ (เช่นการบันทึก) สิ่งที่ฉันจะทำคือในชั้นเรียนUser:

    def validate_credentials(self): # changed from "authenticate" to 
                                    # demonstrate need for super
        if not super().validate_credentials(self.username, self.password):
            # just use self on next call, no need for super:
            self.log_error('Invalid credentials supplied') 
            return False
        return True

1
ฉันไม่เห็นด้วย. การสืบทอดสร้างสำเนาของส่วนต่อประสานสาธารณะของคลาสในคลาสย่อยเสมอ นี่ไม่ใช่ความสัมพันธ์ "มี - a" นี่คือการพิมพ์ย่อยเรียบง่ายและไม่เหมาะสมกับแอปพลิเคชันที่อธิบายไว้
จูลส์

@Jules คุณไม่เห็นด้วยกับอะไร ฉันพูดหลายสิ่งหลายอย่างที่สามารถพิสูจน์ได้และทำข้อสรุปที่เป็นไปตามหลักเหตุผล คุณมีความถูกต้องเมื่อคุณพูดว่า "มรดกจะสร้างสำเนาของอินเตอร์เฟซของประชาชนในระดับย่อยของมันเป็น." ใน Python ไม่มีการคัดลอก - วิธีการค้นหาแบบไดนามิกตามลำดับความละเอียดวิธีการแก้ปัญหาวิธีการ C3 (MRO)
Aaron Hall

1
ประเด็นไม่ได้เกี่ยวกับรายละเอียดที่เฉพาะเจาะจงเกี่ยวกับวิธีการใช้งาน แต่เป็นเรื่องเกี่ยวกับลักษณะส่วนต่อประสานสาธารณะของชั้นเรียน ในกรณีของตัวอย่างUserวัตถุมีอยู่ในอินเตอร์เฟสไม่เพียง แต่สมาชิกที่กำหนดไว้ในUserคลาส แต่ยังรวมถึงวัตถุที่กำหนดไว้ในUserServiceและLoggingServiceด้วย นี่ไม่ใช่ความสัมพันธ์ "มี - a" เนื่องจากส่วนต่อประสานสาธารณะถูกคัดลอก (แม้ว่าจะไม่ใช่ผ่านการคัดลอกโดยตรง แต่เป็นการค้นหาทางอ้อมไปยังส่วนต่อประสานของซูเปอร์คลาส)
จูลส์

Has-a Composition มิกซ์อินเป็นรูปแบบขององค์ประกอบ คลาส User นั้นไม่ใช่ UserService หรือ LoggingService แต่มีฟังก์ชันการทำงานนั้น ฉันคิดว่ามรดกของ Python นั้นแตกต่างจากของ Java มากกว่าที่คุณรู้
Aaron Hall

@ AaronHall คุณเข้าใจง่ายเกินไป (นี่มีแนวโน้มที่จะขัดแย้งกับคำตอบอื่น ๆที่ฉันพบโดยบังเอิญ) จากมุมมองของความสัมพันธ์ย่อยผู้ใช้เป็นทั้ง UserService และ LoggingService ตอนนี้วิญญาณที่นี่คือการแต่งฟังก์ชั่นเพื่อให้ผู้ใช้มีฟังก์ชั่นดังกล่าวและดังกล่าว มิกซ์อินโดยทั่วไปไม่จำเป็นต้องนำไปใช้กับการสืบทอดหลายแบบ อย่างไรก็ตามนี่เป็นวิธีการปกติใน Python
coredump

-1

นอกเหนือจากความจริงที่ว่ามันอนุญาตให้หลาย ๆ superclasses มรดกของไพ ธ อนก็ไม่ต่างกับของชวากล่าวคือสมาชิกของคลาสย่อยก็เป็นสมาชิกของแต่ละ supertypes [1] ความจริงที่ว่า Python ใช้การพิมพ์แบบเป็ดนั้นไม่มีความแตกต่างเช่นกัน: คลาสย่อยของคุณมีสมาชิกซูเปอร์คลาสทั้งหมดจึงสามารถใช้งานได้กับรหัสใด ๆ ที่สามารถใช้คลาสย่อยเหล่านั้นได้ ความจริงที่ว่าการสืบทอดหลาย ๆ อย่างถูกนำไปใช้อย่างมีประสิทธิภาพโดยการใช้การจัดองค์ประกอบเป็นปลาเฮอริ่งแดง: การคัดลอกคุณสมบัติของคลาสหนึ่งไปยังอีกคลาสอัตโนมัตินั้นเป็นปัญหาและมันไม่สำคัญว่ามันจะใช้องค์ประกอบ ในการทำงาน: มีพวกเขาผิด

ใช่นี่เป็นการละเมิดความรับผิดชอบเพียงอย่างเดียวเพราะคุณให้ความสามารถในการดำเนินการกับวัตถุที่ไม่ได้เป็นส่วนหนึ่งของสิ่งที่พวกเขาถูกออกแบบมาให้ทำตามหลักเหตุผล ใช่มันสร้างวัตถุ "พระเจ้า" ซึ่งเป็นอีกวิธีหนึ่งในการพูดสิ่งเดียวกัน

เมื่อออกแบบระบบเชิงวัตถุใน Python maxim ที่เหมือนกันที่สั่งสอนโดยหนังสือการออกแบบ Java ก็ใช้เช่นกัน: ชอบแต่งองค์ประกอบของการสืบทอด เช่นเดียวกับระบบอื่น ๆ (ส่วนใหญ่ [2]) ที่มีหลายมรดก

[1]: คุณอาจเรียกว่าความสัมพันธ์ "is-a" แม้ว่าฉันจะไม่ชอบคำนี้เป็นการส่วนตัวเพราะมันแสดงให้เห็นแนวคิดของการสร้างแบบจำลองโลกแห่งความจริงและการสร้างแบบจำลองเชิงวัตถุนั้นไม่เหมือนกับโลกแห่งความเป็นจริง

[2]: ฉันไม่แน่ใจเกี่ยวกับ C ++ C ++ รองรับ "private inheritance" ซึ่งเป็นส่วนประกอบหลักโดยไม่จำเป็นต้องระบุชื่อฟิลด์เมื่อคุณต้องการใช้สมาชิกสาธารณะของคลาสที่สืบทอดมา ไม่ส่งผลกระทบต่อส่วนต่อประสานสาธารณะของคลาสเลย ฉันไม่ชอบใช้มัน แต่ฉันไม่เห็นเหตุผลที่ดีที่จะไม่ทำ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.