Có thể có nhiều lớp trong cùng một tệp trong Python không?
Đúng. Cả hai từ góc độ triết học cũng như thực tiễn.
Trong Python, các mô-đun là một không gian tên tồn tại một lần trong bộ nhớ.
Giả sử chúng tôi có cấu trúc thư mục giả định sau, với một lớp được xác định cho mỗi tệp:
Defines
abc/
|-- callable.py Callable
|-- container.py Container
|-- hashable.py Hashable
|-- iterable.py Iterable
|-- iterator.py Iterator
|-- sized.py Sized
... 19 more
Tất cả các lớp này đều có sẵn trong collections
mô-đun và (trên thực tế, có tổng cộng 25) được định nghĩa trong mô-đun thư viện tiêu chuẩn trong_collections_abc.py
Có một vài vấn đề ở đây mà tôi tin rằng làm cho sự _collections_abc.py
vượt trội so với cấu trúc thư mục giả thuyết thay thế.
- Các tập tin này được sắp xếp theo thứ tự abc. Bạn có thể sắp xếp chúng theo những cách khác, nhưng tôi không biết về một tính năng sắp xếp các tệp theo các phụ thuộc ngữ nghĩa. Nguồn mô-đun _collections_abc được tổ chức theo sự phụ thuộc.
- Trong các trường hợp không bệnh lý, cả hai mô-đun và định nghĩa lớp là singletons, xảy ra một lần trong bộ nhớ. Sẽ có một ánh xạ mô phỏng của các mô-đun vào các lớp - làm cho các mô-đun trở nên dư thừa.
- Số lượng tệp ngày càng tăng làm cho việc đọc tình cờ qua các lớp trở nên ít thuận tiện hơn (trừ khi bạn có một IDE làm cho nó đơn giản) - làm cho mọi người không thể truy cập mà không có công cụ.
Bạn có bị ngăn không cho chia các nhóm lớp thành các mô-đun khác nhau khi bạn thấy nó mong muốn từ góc độ tổ chức và không gian tên?
Không.
Từ Zen of Python , phản ánh triết lý và nguyên tắc mà theo đó nó phát triển và phát triển:
Không gian tên là một ý tưởng tuyệt vời - hãy làm nhiều hơn nữa!
Nhưng chúng ta hãy nhớ rằng nó cũng nói:
Bằng phẳng là tốt hơn so với lồng nhau.
Python cực kỳ sạch sẽ và dễ đọc. Nó khuyến khích bạn đọc nó. Đặt mỗi lớp riêng biệt trong một tệp riêng biệt không khuyến khích đọc. Điều này đi ngược lại triết lý cốt lõi của Python. Nhìn vào cấu trúc của Thư viện chuẩn , phần lớn các mô-đun là các mô-đun một tệp, không phải các gói. Tôi sẽ gửi cho bạn rằng mã Python thành ngữ được viết theo cùng kiểu với lib chuẩn CPython.
Đây là mã thực tế từ mô-đun lớp cơ sở trừu tượng . Tôi thích sử dụng nó như một tài liệu tham khảo cho việc biểu thị các loại trừu tượng khác nhau trong ngôn ngữ.
Bạn có nói rằng mỗi lớp này cần một tệp riêng không?
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
Vì vậy, mỗi người nên có tập tin riêng của họ?
Tôi hy vọng là không.
Các tệp này không chỉ là mã - chúng là tài liệu về ngữ nghĩa của Python.
Họ có thể trung bình 10 đến 20 dòng. Tại sao tôi phải vào một tệp hoàn toàn riêng biệt để xem 10 dòng mã khác? Điều đó sẽ rất không thực tế. Hơn nữa, sẽ có các bản nhập nồi hơi gần như giống hệt nhau trên mỗi tệp, thêm nhiều dòng mã dự phòng.
Tôi thấy khá hữu ích khi biết rằng có một mô-đun duy nhất nơi tôi có thể tìm thấy tất cả các Lớp cơ sở trừu tượng này, thay vì phải xem qua danh sách các mô-đun. Xem chúng trong bối cảnh với nhau cho phép tôi hiểu rõ hơn về chúng. Khi tôi thấy rằng một Iterator là một Iterable, tôi có thể nhanh chóng xem lại những gì một Iterable bao gồm bằng cách liếc lên.
Thỉnh thoảng tôi có một vài lớp học rất ngắn. Họ ở lại trong tập tin, ngay cả khi họ cần phát triển lớn hơn theo thời gian. Đôi khi các mô-đun trưởng thành có hơn 1000 dòng mã. Nhưng ctrl-f rất dễ và một số IDE giúp dễ dàng xem các phác thảo của tệp - vì vậy cho dù tệp lớn đến đâu, bạn có thể nhanh chóng đi đến bất kỳ đối tượng hoặc phương thức nào bạn đang tìm kiếm.
Phần kết luận
Hướng của tôi, trong ngữ cảnh của Python, là thích giữ các định nghĩa lớp tương tự và có liên quan về mặt ngữ nghĩa trong cùng một tệp. Nếu tập tin phát triển quá lớn đến mức trở nên khó sử dụng, thì hãy xem xét tổ chức lại.
class SomeException extends \Exception {}