มันดีไหมที่จะมีหลายคลาสในไฟล์เดียวกันใน Python?


18

ฉันเพิ่งเข้ามาสู่โลก Python หลังจากหลายปีของ Java และ PHP ในขณะที่ภาษานั้นค่อนข้างตรงไปตรงมาฉันกำลังดิ้นรนกับปัญหา 'เล็กน้อย' บางอย่างที่ฉันไม่สามารถคาดหัวได้ - และฉันไม่สามารถหาคำตอบในเอกสารและแบบฝึกหัดมากมายที่ฉันอ่านมาได้ .

สำหรับผู้ปฏิบัติงาน Python ที่มีประสบการณ์คำถามนี้อาจดูไร้สาระ แต่ฉันต้องการคำตอบจริงๆเพื่อที่ฉันจะได้ไปต่อด้วยภาษา:

ใน Java และ PHP ( แม้ว่าจะไม่จำเป็นอย่างเคร่งครัด ) คุณคาดว่าจะเขียนแต่ละclassไฟล์ของตัวเองโดยมีชื่อไฟล์ว่าclassเป็นแนวทางปฏิบัติที่ดีที่สุด

แต่ใน Python หรืออย่างน้อยในแบบฝึกหัดที่ฉันตรวจสอบมันก็โอเคที่จะมีหลายคลาสในไฟล์เดียวกัน

กฎนี้มีไว้ในการผลิตรหัสพร้อมใช้หรือไม่หรือทำเพื่อความกะทัดรัดในรหัสที่ให้ความรู้เท่านั้น

คำตอบ:


13

มันดีไหมที่จะมีหลายคลาสในไฟล์เดียวกันใน Python?

ใช่. ทั้งจากมุมมองเชิงปรัชญาเช่นเดียวกับในทางปฏิบัติ

ใน Python โมดูลคือเนมสเปซที่มีอยู่ครั้งเดียวในหน่วยความจำ

สมมติว่าเรามีโครงสร้างไดเรกทอรีสมมุติต่อไปนี้โดยกำหนดหนึ่งคลาสต่อไฟล์:

                    Defines
 abc/
 |-- callable.py    Callable
 |-- container.py   Container
 |-- hashable.py    Hashable
 |-- iterable.py    Iterable
 |-- iterator.py    Iterator
 |-- sized.py       Sized
 ... 19 more

คลาสเหล่านี้ทั้งหมดมีอยู่ในcollectionsโมดูลและ (ซึ่งในความเป็นจริงทั้งหมด 25 รวม) ถูกกำหนดในโมดูลไลบรารีมาตรฐานใน_collections_abc.py

มีสองประเด็นที่ฉันเชื่อว่าทำให้_collections_abc.pyดีกว่าโครงสร้างไดเรกทอรีทางเลือกสมมุติ

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

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

เลขที่

จากZen of Pythonซึ่งสะท้อนถึงปรัชญาและหลักการที่เติบโตและมีวิวัฒนาการ:

Namespaces เป็นหนึ่งในแนวคิดที่ยอดเยี่ยม - ลองทำสิ่งเหล่านี้ให้มากขึ้น!

แต่ให้เราจำไว้ว่ามันยังบอกว่า:

แบนดีกว่าซ้อนกัน

Python สะอาดและอ่านง่าย มันสนับสนุนให้คุณอ่าน การวางทุกคลาสแยกจากกันในไฟล์แยกกันจะทำให้หมดกำลังใจในการอ่าน สิ่งนี้ขัดกับปรัชญาหลักของ Python ดูโครงสร้างของStandard Libraryโมดูลส่วนใหญ่เป็นโมดูลไฟล์เดียวไม่ใช่แพ็คเกจ ฉันจะส่งให้คุณว่ารหัสงูสำนวนที่เขียนในลักษณะเดียวกับ lib มาตรฐาน CPython

นี่คือรหัสที่เกิดขึ้นจริงจากโมดูลชั้นฐานนามธรรม ฉันชอบที่จะใช้มันเป็นข้อมูลอ้างอิงสำหรับ denotation ของประเภทนามธรรมต่างๆในภาษา

คุณจะบอกว่าแต่ละชั้นเรียนเหล่านี้ควรจะต้องมีไฟล์แยกต่างหาก

class Hashable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __hash__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Hashable:
            try:
                for B in C.__mro__:
                    if "__hash__" in B.__dict__:
                        if B.__dict__["__hash__"]:
                            return True
                        break
            except AttributeError:
                # Old-style class
                if getattr(C, "__hash__", None):
                    return True
        return NotImplemented


class Iterable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __iter__(self):
        while False:
            yield None

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterable:
            if _hasattr(C, "__iter__"):
                return True
        return NotImplemented

Iterable.register(str)


class Iterator(Iterable):

    @abstractmethod
    def next(self):
        'Return the next item from the iterator. When exhausted, raise StopIteration'
        raise StopIteration

    def __iter__(self):
        return self

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Iterator:
            if _hasattr(C, "next") and _hasattr(C, "__iter__"):
                return True
        return NotImplemented


class Sized:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __len__(self):
        return 0

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Sized:
            if _hasattr(C, "__len__"):
                return True
        return NotImplemented


class Container:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __contains__(self, x):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Container:
            if _hasattr(C, "__contains__"):
                return True
        return NotImplemented


class Callable:
    __metaclass__ = ABCMeta

    @abstractmethod
    def __call__(self, *args, **kwds):
        return False

    @classmethod
    def __subclasshook__(cls, C):
        if cls is Callable:
            if _hasattr(C, "__call__"):
                return True
        return NotImplemented

ดังนั้นพวกเขาแต่ละคนควรมีไฟล์ของตัวเอง?

ฉันหวังว่าไม่

ไฟล์เหล่านี้ไม่ได้เป็นเพียงรหัส แต่เป็นเอกสารเกี่ยวกับความหมายของ Python

พวกเขาอาจเฉลี่ย 10 ถึง 20 บรรทัด ทำไมฉันต้องไปที่ไฟล์แยกกันโดยสมบูรณ์เพื่อดูโค้ดอีก 10 บรรทัด? ที่จะทำไม่ได้อย่างมาก นอกจากนี้จะมีการนำเข้าแผ่นสำเร็จรูปเกือบเหมือนกันในแต่ละไฟล์เพิ่มรหัสซ้ำซ้อนมากขึ้น

ฉันพบว่ามันค่อนข้างมีประโยชน์ที่จะรู้ว่ามีโมดูลเดียวที่ฉันสามารถค้นหา Abstract Base Classes ทั้งหมดแทนที่จะต้องดูรายการของโมดูล การดูพวกเขาในบริบทซึ่งกันและกันทำให้ฉันเข้าใจพวกเขาดีขึ้น เมื่อฉันเห็นว่า Iterator เป็น Iterable ฉันสามารถตรวจสอบสิ่งที่ Iterable ประกอบด้วยได้อย่างรวดเร็ว

บางครั้งฉันก็จบด้วยการเรียนสั้น ๆ สองสามครั้ง พวกเขาอยู่ในไฟล์แม้ว่าพวกเขาต้องการที่จะเติบโตขนาดใหญ่ในช่วงเวลา บางครั้งโมดูลที่โตแล้วมีโค้ดมากกว่า 1,000 บรรทัด แต่ ctrl-f นั้นง่ายและ IDE บางตัวทำให้ง่ายต่อการดูโครงร่างของไฟล์ - ดังนั้นไม่ว่าไฟล์จะมีขนาดใหญ่แค่ไหนคุณก็สามารถไปที่วัตถุหรือวิธีการใดก็ได้ที่คุณต้องการ

ข้อสรุป

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


1
ในขณะที่ฉันเข้าใจขอบคุณรหัสที่คุณส่งมาว่ามันใช้ได้หลายชั้นในไฟล์เดียวกันฉันไม่สามารถหาข้อโต้แย้งได้อย่างน่าเชื่อถือ ตัวอย่างเช่นมันเป็นเรื่องธรรมดามากใน PHP ที่จะมีไฟล์ทั้งหมดที่มีโค้ดคล้ายกับนี้:class SomeException extends \Exception {}
Olivier Malki

3
ชุมชนต่าง ๆ มีมาตรฐานการเข้ารหัสที่แตกต่างกัน คน Java มองงูหลามแล้วพูดว่า "ทำไมจึงอนุญาตให้มีหลายคลาสต่อไฟล์!?" ผู้คนใน Python มองไปที่ Java และพูดว่า "ทำไมจึงต้องใช้แต่ละชั้นมีไฟล์ของตัวเอง!?" ที่ดีที่สุดคือทำตามสไตล์ของชุมชนที่คุณกำลังทำงาน
Gort the Robot

ฉันก็สับสนเหมือนกัน เห็นได้ชัดว่าฉันเข้าใจบางสิ่งเกี่ยวกับ Python กับคำตอบของฉัน แต่จะใช้ "แบนดีกว่าซ้อนกัน" เพื่อเพิ่มวิธีการมากที่สุดเท่าที่เป็นไปได้ในชั้นเรียนหรือไม่ โดยทั่วไปแล้วฉันคิดว่าหลักการของการทำงานร่วมกันและ SRP ยังคงใช้กับโมดูลที่เป็นที่นิยมซึ่งให้คลาสที่เกี่ยวข้องอย่างใกล้ชิดกับฟังก์ชันการทำงาน (แม้ว่าอาจจะดีกว่าคลาสมากกว่าหนึ่งเนื่องจากโมดูลโมเดลแนวคิดแพคเกจ coarser กว่าคลาสเดียว ) โดยเฉพาะอย่างยิ่งเนื่องจากตัวแปรขอบเขตโมดูลใด ๆ (แม้ว่าจะหลีกเลี่ยงโดยหวังว่าโดยทั่วไป) จะเพิ่มขึ้นในขอบเขต

1
Zen of Pythonเป็นรายการของหลักการที่อยู่ในความตึงเครียดซึ่งกันและกัน หนึ่งอาจตอบสนองในข้อตกลงกับจุดของคุณด้วย "Sparse ดีกว่าหนาแน่น" - ซึ่งตามมาทันที "แฟลตดีกว่าซ้อนกัน" แต่ละบรรทัดจาก The Zen of Python สามารถนำไปใช้ในทางที่ผิดได้อย่างง่ายดายและถูกนำไปสู่สุดขั้ว แต่โดยรวมแล้วสามารถช่วยในการเขียนโค้ดและค้นหาพื้นดินทั่วไปซึ่งผู้คนที่มีเหตุผลอาจไม่เห็นด้วย ฉันไม่คิดว่าผู้คนจะพิจารณาตัวอย่างโค้ดของฉันหนาแน่น แต่สิ่งที่คุณอธิบายฟังดูมีความหนาแน่นมากสำหรับฉัน
Aaron Hall

ขอขอบคุณคุณ Jeffrey Albertson / Guy หนังสือการ์ตูน :) ผู้ใช้ Python ส่วนใหญ่ไม่ควรใช้วิธีพิเศษ (double-underscore) แต่พวกเขาอนุญาตให้ผู้ออกแบบหลัก / สถาปนิกมีส่วนร่วมในการเขียนโปรแกรมเมตาเพื่อใช้งานตัวดำเนินการแบบกำหนดเองตัวเปรียบเทียบตัวย่อบริบทและอื่น ๆ คุณสมบัติภาษา ตราบใดที่พวกเขาไม่ละเมิดหลักการประหลาดใจอย่างน้อยที่สุดฉันคิดว่าความเสียหายต่ออัตราส่วนมูลค่านั้นน้อยมาก
Aaron Hall

4

เมื่อจัดโครงสร้างแอปพลิเคชันของคุณใน Python คุณต้องคิดในแง่ของแพ็คเกจและโมดูล

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

อย่าลืมที่จะอ่านเป็นครั้งคราวเกี่ยวกับดัชนีของข้อเสนอการเพิ่มประสิทธิภาพของงูหลาม


2

คำตอบที่แท้จริงสำหรับเรื่องนี้เป็นเรื่องทั่วไปและไม่ได้ขึ้นอยู่กับภาษาที่ใช้: สิ่งที่ควรมีในไฟล์ไม่ได้ขึ้นอยู่กับจำนวนคลาสที่กำหนดเป็นหลัก มันขึ้นอยู่กับความเชื่อมโยงทางตรรกะและความซับซ้อน ระยะเวลา

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

ที่กล่าวว่ากฎหนึ่งระดับต่อไฟล์มักจะเป็นฮิวริสติกที่ดี อย่างไรก็ตามมีข้อยกเว้นที่สำคัญคือคลาสตัวช่วยขนาดเล็กที่เป็นเพียงรายละเอียดการใช้งานของคลาสผู้ใช้ แต่เพียงผู้เดียวโดยทั่วไปควรรวมเข้ากับไฟล์ของคลาสผู้ใช้นั้น ในทำนองเดียวกันถ้าคุณมีสามชั้นvector2, vector3และvector4มีเหตุผลเล็ก ๆ น้อย ๆ มีแนวโน้มที่จะใช้พวกเขาในแฟ้มที่แยกต่างหาก


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