Khi nghi ngờ, hãy để nó ở chế độ "công khai" - ý tôi là đừng thêm bất cứ thứ gì để che khuất tên thuộc tính của bạn. Nếu bạn có một lớp với một số giá trị nội bộ, đừng bận tâm về nó. Thay vì viết:
class Stack(object):
def __init__(self):
self.__storage = [] # Too uptight
def push(self, value):
self.__storage.append(value)
viết cái này theo mặc định:
class Stack(object):
def __init__(self):
self.storage = [] # No mangling
def push(self, value):
self.storage.append(value)
Đây chắc chắn là một cách làm gây tranh cãi. Những người mới dùng Python chỉ ghét nó và thậm chí một số anh chàng Python cũ cũng coi thường mặc định này - nhưng dù sao thì nó cũng là mặc định, vì vậy tôi thực sự khuyên bạn nên làm theo nó, ngay cả khi bạn cảm thấy không thoải mái.
Nếu bạn thực sự muốn gửi thông báo "Không thể chạm vào cái này!" đối với người dùng của bạn, cách thông thường là đặt trước biến bằng một dấu gạch dưới. Đây chỉ là một quy ước, nhưng mọi người hiểu nó và cẩn thận khi xử lý những thứ như vậy:
class Stack(object):
def __init__(self):
self._storage = [] # This is ok but pythonistas use it to be relaxed about it
def push(self, value):
self._storage.append(value)
Điều này cũng có thể hữu ích để tránh xung đột giữa tên thuộc tính và tên thuộc tính:
class Person(object):
def __init__(self, name, age):
self.name = name
self._age = age if age >= 0 else 0
@property
def age(self):
return self._age
@age.setter
def age(self, age):
if age >= 0:
self._age = age
else:
self._age = 0
Còn dấu gạch dưới kép thì sao? Chà, phép thuật gạch dưới kép được sử dụng chủ yếu để tránh tình trạng quá tải các phương thức và xung đột tên với các thuộc tính của lớp cha . Nó có thể khá hữu ích nếu bạn viết một lớp dự kiến sẽ được kéo dài nhiều lần.
Nếu bạn muốn sử dụng nó cho các mục đích khác, bạn có thể, nhưng nó không bình thường và cũng không được khuyến khích.
EDIT : Tại sao điều này là như vậy? Chà, kiểu Python thông thường không nhấn mạnh đến việc đặt mọi thứ ở chế độ riêng tư - ngược lại! Có rất nhiều lý do cho điều đó - hầu hết đều gây tranh cãi ... Chúng ta hãy xem một số trong số đó.
Python có các thuộc tính
Hầu hết các ngôn ngữ OO ngày nay sử dụng cách tiếp cận ngược lại: những gì không nên sử dụng sẽ không được hiển thị, do đó các thuộc tính phải là riêng tư. Về mặt lý thuyết, điều này sẽ mang lại nhiều lớp dễ quản lý hơn, ít ghép nối hơn, bởi vì không ai có thể thay đổi các giá trị bên trong các đối tượng một cách liều lĩnh.
Tuy nhiên, nó không đơn giản như vậy. Ví dụ, các lớp Java có rất nhiều thuộc tính và bộ nhận chỉ lấy giá trị và bộ định tuyến chỉ đặt giá trị. Giả sử bạn cần bảy dòng mã để khai báo một thuộc tính duy nhất - điều mà một lập trình viên Python sẽ nói là phức tạp không cần thiết. Ngoài ra, trên thực tế, bạn chỉ cần viết toàn bộ đoạn mã này để lấy một trường công khai, vì bạn có thể thay đổi giá trị của nó bằng cách sử dụng getters và setters.
Vậy tại sao phải tuân theo chính sách private-by-default này? Chỉ cần đặt thuộc tính của bạn ở chế độ công khai theo mặc định. Tất nhiên, đây là vấn đề trong Java, vì nếu bạn quyết định thêm một số xác thực vào thuộc tính của mình, nó sẽ yêu cầu bạn thay đổi tất cả
person.age = age;
trong mã của bạn, hãy để chúng tôi nói,
person.setAge(age);
setAge()
là:
public void setAge(int age) {
if (age >= 0) {
this.age = age;
} else {
this.age = 0;
}
}
Vì vậy, trong Java (và các ngôn ngữ khác), mặc định là sử dụng getters và setters, vì chúng có thể gây khó chịu khi viết nhưng có thể giúp bạn tiết kiệm rất nhiều thời gian nếu bạn rơi vào tình huống mà tôi đã mô tả.
Tuy nhiên, bạn không cần phải làm điều đó trong Python, vì Python có các thuộc tính. Nếu bạn có lớp học này:
class Person(object):
def __init__(self, name, age):
self.name = name
self.age = age
và sau đó bạn quyết định xác thực độ tuổi, bạn không cần phải thay đổi các person.age = age
phần mã của mình. Chỉ cần thêm một thuộc tính (như hình bên dưới)
class Person(object):
def __init__(self, name, age):
self.name = name
self._age = age if age >= 0 else 0
@property
def age(self):
return self._age
@age.setter
def age(self, age):
if age >= 0:
self._age = age
else:
self._age = 0
Nếu bạn có thể làm điều đó và vẫn sử dụng person.age = age
, tại sao bạn lại thêm các trường riêng tư và getters và setters?
(Ngoài ra, hãy xem Python không phải là Java và bài viết này về tác hại của việc sử dụng getters và setters .).
Mọi thứ đều hiển thị - và cố gắng che giấu chỉ làm phức tạp công việc của bạn
Ngay cả trong những ngôn ngữ có các thuộc tính riêng tư, bạn có thể truy cập chúng thông qua một số loại thư viện phản ánh / nội quan. Và mọi người làm điều đó rất nhiều, trong khuôn khổ và để giải quyết các nhu cầu cấp thiết. Vấn đề là các thư viện nội quan chỉ là một cách khó để thực hiện những gì bạn có thể làm với các thuộc tính chung.
Vì Python là một ngôn ngữ rất năng động, nên việc thêm gánh nặng này vào các lớp của bạn sẽ phản tác dụng.
Vấn đề là không thể nhìn thấy - nó được yêu cầu để xem
Đối với Pythonista, việc bao bọc không phải là không có khả năng nhìn thấy bên trong của các lớp, mà là khả năng tránh nhìn vào nó. Ý tôi là, tính đóng gói là thuộc tính của một thành phần cho phép nó được sử dụng mà người dùng không quan tâm đến các chi tiết bên trong. Nếu bạn có thể sử dụng một thành phần mà không cần bận tâm về việc triển khai nó, thì nó được đóng gói (theo ý kiến của một lập trình viên Python).
Bây giờ, nếu bạn đã viết lớp của mình theo cách như vậy, bạn có thể sử dụng nó mà không cần phải suy nghĩ về các chi tiết triển khai, không có vấn đề gì nếu bạn muốn xem bên trong lớp vì một lý do nào đó. Vấn đề là: API của bạn phải tốt và phần còn lại là chi tiết.
Guido đã nói như vậy
Chà, điều này không có gì phải bàn cãi: thực ra anh ấy đã nói như vậy . (Tìm "kimono hở.")
Đây là văn hóa
Có, có một số lý do, nhưng không có lý do quan trọng nào. Đây chủ yếu là một khía cạnh văn hóa của lập trình bằng Python. Thành thật mà nói, nó cũng có thể là theo cách khác - nhưng không phải vậy. Ngoài ra, bạn có thể dễ dàng hỏi ngược lại: tại sao một số ngôn ngữ sử dụng thuộc tính riêng theo mặc định? Vì lý do chính tương tự như đối với thực hành Python: bởi vì nó là văn hóa của các ngôn ngữ này, và mỗi lựa chọn đều có ưu điểm và nhược điểm.
Vì đã có văn hóa này, bạn nên tuân theo nó. Nếu không, bạn sẽ thấy khó chịu khi các lập trình viên Python yêu cầu bạn xóa __
mã khỏi mã của bạn khi bạn đặt câu hỏi trong Stack Overflow :)