Có lợi ích khi xác định một lớp bên trong một lớp khác trong Python không?


106

Điều tôi đang nói ở đây là các lớp lồng nhau. Về cơ bản, tôi có hai lớp học mà tôi đang làm mẫu. Một lớp DownloadManager và một lớp DownloadThread. Khái niệm OOP rõ ràng ở đây là thành phần. Tuy nhiên, bố cục không nhất thiết có nghĩa là lồng vào nhau, phải không?

Tôi có mã trông giống như sau:

class DownloadThread:
    def foo(self):
        pass

class DownloadManager():
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadThread())

Nhưng bây giờ tôi đang tự hỏi nếu có một tình huống mà tổ sẽ tốt hơn. Cái gì đó như:

class DownloadManager():
    class DownloadThread:
        def foo(self):
            pass
    def __init__(self):
        dwld_threads = []
    def create_new_thread():
        dwld_threads.append(DownloadManager.DownloadThread())

Câu trả lời:


121

Bạn có thể muốn làm điều này khi lớp "bên trong" là một lớp duy nhất, lớp này sẽ không bao giờ được sử dụng bên ngoài định nghĩa của lớp bên ngoài. Ví dụ: sử dụng một chiếc đồng hồ đeo tay, đôi khi rất tiện dụng để làm

class Foo(object):
    class __metaclass__(type):
        .... 

thay vì xác định một siêu kính riêng biệt, nếu bạn chỉ sử dụng nó một lần.

Lần duy nhất tôi sử dụng các lớp lồng nhau như vậy, tôi chỉ sử dụng lớp ngoài làm không gian tên để nhóm một loạt các lớp có liên quan chặt chẽ với nhau:

class Group(object):
    class cls1(object):
       ...

    class cls2(object):
       ...

Sau đó, từ một mô-đun khác, bạn có thể nhập Nhóm và gọi chúng là Group.cls1, Group.cls2, v.v. Tuy nhiên, người ta có thể tranh luận rằng bạn có thể hoàn thành giống hệt nhau (có lẽ theo cách ít khó hiểu hơn) bằng cách sử dụng một mô-đun.


13
Tôi -có thể lập luận rằng trong trường hợp thứ hai, bạn-chắc chắn- sẽ được phục vụ tốt hơn khi sử dụng một mô-đun hoặc gói, được thiết kế để trở thành không gian tên.
Matthew Trevor

5
Đây là một câu trả lời tuyệt vời. Đơn giản, chính xác, và đề cập đến phương pháp thay thế sử dụng mô-đun. +1
CornSmith

5
Đối với bản ghi, đối với những người cố chấp từ chối hoặc không thể tổ chức mã của họ thành một gói phân cấp và các mô-đun thích hợp, sử dụng các lớp làm không gian tên đôi khi có thể tốt hơn là không sử dụng bất cứ thứ gì.
Acumenus

19

Tôi không biết Python, nhưng câu hỏi của bạn có vẻ rất chung chung. Bỏ qua tôi nếu nó dành riêng cho Python.

Việc lồng lớp là tất cả về phạm vi. Nếu bạn nghĩ rằng một lớp sẽ chỉ có ý nghĩa trong bối cảnh của lớp khác, thì lớp trước có lẽ là một ứng cử viên tốt để trở thành một lớp lồng nhau.

Đó là một mẫu phổ biến làm cho các lớp trợ giúp như các lớp riêng, lồng nhau.


9
Chỉ cần lưu ý đối với bản ghi, khái niệm privatelớp không áp dụng nhiều trong Python, nhưng phạm vi sử dụng ngụ ý vẫn chắc chắn được áp dụng.
Acumenus

7

Có một cách sử dụng khác cho lớp lồng nhau, khi một người muốn xây dựng các lớp kế thừa có các chức năng nâng cao được đóng gói trong một lớp lồng nhau cụ thể.

Xem ví dụ này:

class foo:

  class bar:
    ...  # functionalities of a specific sub-feature of foo

  def __init__(self):
    self.a = self.bar()
    ...

  ...  # other features of foo


class foo2(foo):

  class bar(foo.bar):
    ... # enhanced functionalities for this specific feature

  def __init__(self):
    foo.__init__(self)

Lưu ý rằng trong hàm tạo của foo, dòng self.a = self.bar()sẽ tạo a foo.barkhi đối tượng đang được xây dựng thực sự là một foođối tượng và một foo2.barđối tượng khi đối tượng được xây dựng thực sự là một foo2đối tượng.

Nếu thay vào đó, nếu lớp barđược định nghĩa bên ngoài lớp foo, cũng như phiên bản kế thừa của nó ( bar2ví dụ sẽ được gọi ), thì việc xác định lớp mới foo2sẽ khó khăn hơn nhiều, bởi vì hằng số của foo2sẽ cần phải có dòng đầu tiên của nó được thay thế bằng self.a = bar2(), ngụ ý viết lại toàn bộ hàm tạo.


6

Thực sự không có lợi khi làm điều này, ngoại trừ trường hợp bạn đang làm việc với kính đeo.

the class: suite thực sự không như bạn nghĩ. Đó là một phạm vi kỳ lạ, và nó làm những điều kỳ lạ. Nó thực sự thậm chí không tạo nên một lớp học! Nó chỉ là một cách thu thập một số biến - tên của lớp, các cơ sở, một từ điển nhỏ về các thuộc tính và một siêu kim loại.

Tên, từ điển và cơ sở đều được chuyển cho hàm là siêu kính, và sau đó nó được gán cho biến 'name' trong phạm vi mà class: suite ở đó.

Những gì bạn có thể đạt được bằng cách làm rối với metaclasses và thực sự bằng cách lồng các lớp vào trong các lớp chuẩn cổ phiếu của bạn, mã khó đọc hơn, mã khó hiểu hơn và các lỗi kỳ quặc cực kỳ khó hiểu nếu không quen thuộc với lý do tại sao 'lớp' phạm vi hoàn toàn khác với bất kỳ phạm vi python nào khác.


3
Không đồng ý: Việc lồng các lớp ngoại lệ rất hữu ích, vì người dùng lớp của bạn có thể kiểm tra các ngoại lệ tùy chỉnh của bạn mà không cần phải thực hiện bất kỳ thao tác nhập lộn xộn nào. Ví dụexcept myInstanceObj.CustomError, e:
RobM

2
@Jerub, sao tệ vậy?
Robert Siemer

5

Bạn có thể đang sử dụng một lớp làm trình tạo lớp. Thích (trong một số mã ngoài vòng bít :)

class gen(object):
    class base_1(object): pass
    ...
    class base_n(object): pass

    def __init__(self, ...):
        ...
    def mk_cls(self, ..., type):
        '''makes a class based on the type passed in, the current state of
           the class, and the other inputs to the method'''

Tôi cảm thấy như khi bạn cần chức năng này, nó sẽ rất rõ ràng cho bạn. Nếu bạn không cần phải làm điều gì đó tương tự hơn nó có lẽ không phải là một trường hợp sử dụng tốt.


1

Không, bố cục không có nghĩa là lồng vào nhau. Sẽ rất hợp lý khi có một lớp lồng nhau nếu bạn muốn ẩn nó nhiều hơn trong không gian tên của lớp bên ngoài.

Dù sao, tôi không thấy bất kỳ công dụng thực tế nào cho việc lồng vào trường hợp của bạn. Nó sẽ làm cho mã khó đọc (hiểu) hơn và nó cũng sẽ làm tăng độ thụt đầu dòng khiến các dòng ngắn hơn và dễ bị tách.

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.