Khi viết các lớp, người ta thường cố gắng gói gọn các chi tiết bên trong. Phần này giới thiệu một vài thành ngữ lập trình Python cho điều này bao gồm các biến và thuộc tính riêng tư
Công cộng vs Riêng tư
Một trong những vai trò chính của một lớp là đóng gói dữ liệu và chi tiết triển khai bên trong của một đối tượng. Tuy nhiên, một lớp cũng định nghĩa một giao diện chung mà thế giới bên ngoài phải sử dụng để thao tác với đối tượng. Sự khác biệt giữa các chi tiết triển khai và giao diện công khai là rất quan trọng
Vấn đề
Trong Python, hầu hết mọi thứ về lớp và đối tượng đều mở
- Bạn có thể dễ dàng kiểm tra bên trong đối tượng
- Bạn có thể thay đổi mọi thứ theo ý muốn
- Không có khái niệm mạnh mẽ về kiểm soát truy cập [tôi. e. , thành viên lớp riêng]
Đó là một vấn đề khi bạn đang cố gắng tách biệt các chi tiết của việc triển khai nội bộ
Đóng gói Python
Python dựa vào các quy ước lập trình để chỉ ra mục đích sử dụng của một thứ gì đó. Các quy ước này dựa trên việc đặt tên. Có một thái độ chung rằng lập trình viên phải tuân thủ các quy tắc chứ không phải để ngôn ngữ thực thi chúng.
Thuộc tính riêng tư
Bất kỳ tên thuộc tính nào có đầu
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
1 đều được coi là riêng tưclass Person[object]:
def __init__[self, name]:
self._name = 0
Như đã đề cập trước đó, đây chỉ là một phong cách lập trình. Bạn vẫn có thể truy cập và thay đổi nó
>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
Theo nguyên tắc chung, bất kỳ tên nào có đầu ____________1 đều được coi là triển khai nội bộ cho dù đó là tên biến, hàm hay tên mô-đun. Nếu bạn thấy mình trực tiếp sử dụng những tên như vậy, có lẽ bạn đã làm sai điều gì đó. Tìm kiếm chức năng cấp cao hơn
Thuộc tính đơn giản
Hãy xem xét lớp sau
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
Một tính năng đáng ngạc nhiên là bạn có thể đặt các thuộc tính thành bất kỳ giá trị nào
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
Bạn có thể nhìn vào đó và nghĩ rằng bạn muốn kiểm tra thêm
s.shares = '50' # Raise a TypeError, this is a string
Bạn sẽ làm điều này như thế nào?
Thuộc tính được quản lý
Một cách tiếp cận. giới thiệu các phương thức truy cập
class Stock:
def __init__[self, name, shares, price]:
self.name = name self.set_shares[shares] self.price = price
# Function that layers the "get" operation
def get_shares[self]:
return self._shares
# Function that layers the "set" operation
def set_shares[self, value]:
if not isinstance[value, int]:
raise TypeError['Expected an int']
self._shares = value
Thật tệ là điều này phá vỡ tất cả các mã hiện có của chúng tôi.
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
3 trở thành class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
4Của cải
Có một cách tiếp cận thay thế cho mô hình trước đó
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
@property
def shares[self]:
return self._shares
@shares.setter
def shares[self, value]:
if not isinstance[value, int]:
raise TypeError['Expected int']
self._shares = value
Truy cập thuộc tính thông thường hiện kích hoạt các phương thức getter và setter trong
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
5 và class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
6>>> s = Stock['IBM', 50, 91.1]
>>> s.shares # Triggers @property
50
>>> s.shares = 75 # Triggers @shares.setter
>>>
Với mẫu này, không cần thay đổi mã nguồn. Trình thiết lập mới cũng được gọi khi có một phép gán trong lớp, bao gồm cả bên trong phương thức
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
7class Stock:
def __init__[self, name, shares, price]:
...
# This assignment calls the setter below
self.shares = shares
...
...
@shares.setter
def shares[self, value]:
if not isinstance[value, int]:
raise TypeError['Expected int']
self._shares = value
Thường có sự nhầm lẫn giữa tài sản và việc sử dụng tên riêng. Mặc dù thuộc tính bên trong sử dụng tên riêng như
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
8, nhưng phần còn lại của lớp [không phải thuộc tính] có thể tiếp tục sử dụng tên như class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
9Các thuộc tính cũng hữu ích cho các thuộc tính dữ liệu được tính toán
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
@property
def cost[self]:
return self.shares * self.price
...
Điều này cho phép bạn loại bỏ các dấu ngoặc thừa, che giấu sự thật rằng đó thực sự là một phương thức
>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
0truy cập thống nhất
Ví dụ cuối cùng cho thấy cách đặt một giao diện thống nhất hơn trên một đối tượng. Nếu bạn không làm điều này, một đối tượng có thể gây nhầm lẫn khi sử dụng
>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
1Tại sao
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
0 được yêu cầu cho chi phí, nhưng không phải cho cổ phiếu? Cú pháp trang trí
Cú pháp
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
1 được gọi là *decoration". Nó chỉ định một công cụ sửa đổi được áp dụng cho định nghĩa hàm ngay sau đó>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
2Thông tin chi tiết được đưa ra trong Phần 7
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
2 Thuộc tính
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
Bạn có thể hạn chế tập hợp các tên thuộc tính
>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
3Nó sẽ gây ra lỗi cho các thuộc tính khác
>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
4Mặc dù điều này ngăn ngừa lỗi và hạn chế sử dụng các đối tượng, nhưng nó thực sự được sử dụng cho hiệu suất và giúp Python sử dụng bộ nhớ hiệu quả hơn
Nhận xét cuối cùng về đóng gói
Đừng quá nhiệt tình với các thuộc tính, thuộc tính, vị trí riêng tư, v.v. Chúng phục vụ một mục đích cụ thể và bạn có thể thấy chúng khi đọc mã Python khác. Tuy nhiên, chúng không cần thiết cho hầu hết các mã hàng ngày
bài tập
bài tập 5. 6. Thuộc tính đơn giản
Thuộc tính là một cách hữu ích để thêm "thuộc tính được tính" vào một đối tượng. Trong
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
3, bạn đã tạo một đối tượng >>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
4. Lưu ý rằng trên đối tượng của bạn có một chút mâu thuẫn về cách các loại dữ liệu khác nhau được trích xuất>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
5Cụ thể, hãy lưu ý cách bạn phải thêm dấu [] vào
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
5 vì đó là một phương thứcBạn có thể loại bỏ phần thừa [] trên
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
6 nếu bạn biến nó thành tài sản. Lấy lớp >>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
4 của bạn và sửa đổi nó để tính toán chi phí hoạt động như thế này>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
6Hãy thử gọi
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
8 như một hàm và nhận thấy rằng nó không hoạt động khi mà >>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
5 đã được định nghĩa là một thuộc tính>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
7Thực hiện thay đổi này có thể sẽ phá vỡ chương trình
s.shares = '50' # Raise a TypeError, this is a string
0 trước đó của bạn. Bạn có thể cần quay lại và loại bỏ >>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
0 trên phương pháp >>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
6bài tập 5. 7. Thuộc tính và Setters
Sửa đổi thuộc tính
class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
9 để giá trị được lưu trữ trong một thuộc tính riêng tư và một cặp hàm thuộc tính được sử dụng để đảm bảo rằng nó luôn được đặt thành một giá trị nguyên. Đây là một ví dụ về hành vi dự kiến>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
8bài tập 5. 8. Thêm vị trí
Sửa đổi lớp
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
4 để nó có thuộc tính >>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
2. Sau đó, xác minh rằng không thể thêm các thuộc tính mới>>> p = Person['Guido']
>>> p._name
'Guido'
>>> p._name = 'Dave'
>>>
9Khi bạn sử dụng
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
2, Python sẽ sử dụng cách biểu diễn đối tượng bên trong hiệu quả hơn. Điều gì xảy ra nếu bạn cố gắng kiểm tra từ điển cơ bản của s.shares = '50' # Raise a TypeError, this is a string
7 ở trên?class Stock:
def __init__[self, name, shares, price]:
self.name = name
self.shares = shares
self.price = price
0Cần lưu ý rằng
>>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
2 được sử dụng phổ biến nhất để tối ưu hóa trên các lớp đóng vai trò là cấu trúc dữ liệu. Sử dụng khe cắm sẽ làm cho các chương trình như vậy sử dụng ít bộ nhớ hơn và chạy nhanh hơn một chút. Tuy nhiên, có lẽ bạn nên tránh >>> s = Stock['IBM', 50, 91.1]
>>> s.shares = 100
>>> s.shares = "hundred"
>>> s.shares = [1, 0, 0]
>>>
2 trên hầu hết các lớp học khác