Lớp “Riêng tư” (triển khai) trong Python


106

Tôi đang viết mã một mô-đun Python nhỏ bao gồm hai phần:

  • một số chức năng xác định giao diện công khai,
  • một lớp thực thi được sử dụng bởi các hàm trên, nhưng không có ý nghĩa bên ngoài mô-đun.

Lúc đầu, tôi quyết định "ẩn" lớp triển khai này bằng cách định nghĩa nó bên trong hàm bằng cách sử dụng nó, nhưng điều này cản trở khả năng đọc và không thể sử dụng nếu nhiều hàm sử dụng lại cùng một lớp.

Vì vậy, ngoài nhận xét và docstrings, có cơ chế nào để đánh dấu một lớp là "riêng tư" hoặc "nội bộ" không? Tôi biết về cơ chế gạch dưới, nhưng theo tôi hiểu, nó chỉ áp dụng cho các biến, hàm và tên phương thức.

Câu trả lời:


174

Sử dụng một tiền tố gạch dưới duy nhất:

class _Internal:
    ...

Đây là quy ước Python chính thức cho các ký hiệu 'nội bộ'; "from module import *" không nhập các đối tượng có tiền tố gạch dưới.

Chỉnh sửa: Tham chiếu đến quy ước gạch dưới đơn


3
Tôi không biết quy tắc gạch dưới mở rộng cho các lớp. Tôi không muốn làm lộn xộn không gian tên của mình khi nhập, vì vậy hành vi này là những gì tôi đang tìm kiếm. Cảm ơn!
oparisy

1
Vì bạn nói rằng đây là quy ước "chính thức" của python, nên sẽ rất tuyệt nếu có một liên kết. Một bài đăng khác ở đây nói rằng tất cả là cách chính thức và có liên kết đến tài liệu.
flodin

11
python.org/dev/peps/pep-0008 - _single_leading_underscore: chỉ báo "sử dụng nội bộ" yếu. Ví dụ: "from M import *" không nhập các đối tượng có tên bắt đầu bằng dấu gạch dưới. - Các lớp sử dụng nội bộ có gạch dưới hàng đầu
Miles

2
Dấu gạch dưới hàng đầu là quy ước để đánh dấu mọi thứ là nội bộ, "đừng gây rối với điều này", trong khi tất cả là hơn cho các mô-đun được thiết kế để sử dụng với "from M import *", mà không nhất thiết ngụ ý rằng người dùng mô-đun không nên chạm vào lớp đó.
Miles

65

Nói ngắn gọn:

  1. Bạn không thể thực thi quyền riêng tư . Không có lớp / phương thức / hàm riêng trong Python. Ít nhất, không phải bảo mật nghiêm ngặt như trong các ngôn ngữ khác, chẳng hạn như Java.

  2. Bạn chỉ có thể chỉ ra / đề xuất quyền riêng tư . Điều này tuân theo một quy ước. Quy ước python để đánh dấu một lớp / hàm / phương thức là private là đặt trước nó bằng dấu _ (dấu gạch dưới). Ví dụ, def _myfunc()hoặc class _MyClass:. Bạn cũng có thể tạo quyền riêng tư giả bằng cách đặt trước phương thức bằng hai dấu gạch dưới (ví dụ __foo:). Bạn không thể truy cập phương thức trực tiếp, nhưng bạn vẫn có thể gọi nó thông qua một tiền tố đặc biệt bằng cách sử dụng tên lớp (ví dụ _classname__foo:). Vì vậy, tốt nhất bạn có thể làm là chỉ ra / đề xuất quyền riêng tư chứ không phải thực thi nó.

Python giống như perl về mặt này. Để diễn giải một dòng nổi tiếng về quyền riêng tư từ cuốn sách Perl, triết lý là bạn nên tránh ra khỏi phòng khách vì bạn không được mời, không phải vì nó được bảo vệ bằng súng ngắn.

Để biết thêm thông tin:


Đáp lại # 1, bạn có thể thực thi quyền riêng tư của các phương pháp. Sử dụng dấu gạch dưới kép như __method (self) sẽ làm cho nó không thể truy cập được bên ngoài lớp. Nhưng có một cách để giải quyết nó bằng cách gọi nó như, Foo () ._ Foo__method (). Tôi đoán nó chỉ đổi tên nó thành một cái gì đó bí truyền hơn.
Evan Fosmark

1
Trích dẫn đó là chính xác.
Paul Draper

37

Xác định __all__, danh sách các tên mà bạn muốn được xuất ( xem tài liệu ).

__all__ = ['public_class'] # don't add here the 'implementation_class'

10

Một mẫu mà tôi đôi khi sử dụng là:

Xác định một lớp:

class x(object):
    def doThis(self):
        ...
    def doThat(self):
        ...

Tạo một phiên bản của lớp, ghi đè lên tên lớp:

x = x()

Xác định các ký hiệu thể hiện chức năng:

doThis = x.doThis
doThat = x.doThat

Xóa chính phiên bản:

del x

Bây giờ bạn có một mô-đun chỉ hiển thị các chức năng công khai của bạn.


2
Mất một phút để hiểu mục đích / chức năng của "ghi đè tên lớp", nhưng tôi đã nở một nụ cười thật tươi khi làm vậy. Không chắc khi nào tôi sẽ sử dụng nó. :)
Zach Young

Có tên cho kỹ thuật này không?
Bishwas Mishra


4

Để giải quyết vấn đề về quy ước thiết kế, và như Christopher đã nói, thực sự không có cái gọi là "riêng tư" trong Python. Điều này nghe có vẻ khó hiểu đối với một người đến từ nền tảng C / C ++ (như tôi một thời gian trước), nhưng cuối cùng, bạn có thể nhận ra rằng làm theo các quy ước là đủ.

Nhìn thấy thứ gì đó có dấu gạch dưới ở phía trước là một gợi ý đủ tốt để không sử dụng nó trực tiếp. Nếu bạn lo lắng với help(MyClass)đầu ra lộn xộn (đó là những gì mọi người nhìn khi tìm kiếm cách sử dụng một lớp), các thuộc tính / lớp gạch dưới không được bao gồm ở đó, vì vậy bạn sẽ chỉ có giao diện "công khai" của mình được mô tả.

Thêm vào đó, mọi thứ công khai đều có những đặc quyền tuyệt vời của riêng nó, chẳng hạn như bạn có thể kiểm tra đơn vị khá nhiều thứ từ bên ngoài (điều mà bạn thực sự không thể làm với các cấu trúc riêng tư C / C ++).


4

Sử dụng hai dấu gạch dưới cho tên tiền tố của số nhận dạng "riêng tư". Đối với các lớp trong một mô-đun, hãy sử dụng một dấu gạch dưới ở đầu và chúng sẽ không được nhập bằng cách sử dụng "từ nhập mô-đun *".

class _MyInternalClass:
    def __my_private_method:
        pass

(Không có cái gọi là "riêng tư" thực sự trong Python. Ví dụ: Python chỉ tự động trộn tên của các thành viên trong lớp với dấu gạch dưới kép __clssname_mymember. Vì vậy, nếu bạn biết tên bị xáo trộn, bạn vẫn có thể sử dụng thực thể "riêng tư" . Xem tại đây. Và tất nhiên bạn có thể chọn nhập các lớp "nội bộ" theo cách thủ công nếu muốn).


Tại sao hai _? Một là đủ.
S.Lott

Một dấu gạch dưới duy nhất là đủ để ngăn "nhập" nhập các lớp và hàm. Bạn cần có hai dấu gạch dưới để tạo ra tính năng mang tên Python. Lẽ ra phải rõ ràng hơn; Tôi đã chỉnh sửa.
chroder

Bây giờ, tiếp theo. Tại sao lại đặt tên mangling? Lợi ích có thể có của điều đó là gì?
S.Lott

5
Lợi ích có thể là gây phiền nhiễu các nhà phát triển khác, những người cần truy cập vào các biến :)
Richard Levasseur
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.