Thuộc tính set được sử dụng để làm gì trong python?
Hỏi lúc:1 tháng trước
Trả lời:0
Lượt xem:40
Với Python, bạn có thể tạo các thuộc tính được quản lý trong các lớp của mình. Bạn có thể sử dụng các thuộc tính được quản lý, còn được gọi là thuộc tính, khi bạn cần sửa đổi triển khai nội bộ của chúng mà không thay đổi API công khai của lớp. Việc cung cấp các API ổn định có thể giúp bạn tránh vi phạm mã của người dùng khi họ dựa vào các lớp và đối tượng của bạn
4 để xác thực dữ liệu đầu vào, tính toán các giá trị thuộc tính một cách linh hoạt, ghi nhật ký mã của bạn, v.v. Để tận dụng tối đa hướng dẫn này, bạn nên biết kiến thức cơ bản về lập trình hướng đối tượng và trình trang trí trong Python
Tiền thưởng miễn phí. 5 Suy nghĩ về Làm chủ Python, một khóa học miễn phí dành cho các nhà phát triển Python cho bạn thấy lộ trình và tư duy mà bạn sẽ cần để đưa các kỹ năng Python của mình lên một tầm cao mới
Quản lý thuộc tính trong lớp học của bạn
Khi bạn định nghĩa một lớp trong ngôn ngữ lập trình hướng đối tượng, có thể bạn sẽ kết thúc với một số thể hiện và lớp. Nói cách khác, bạn sẽ nhận được các biến có thể truy cập thông qua thể hiện, lớp hoặc thậm chí cả hai, tùy thuộc vào ngôn ngữ. Các thuộc tính đại diện hoặc giữ trạng thái bên trong của một đối tượng nhất định mà bạn sẽ thường xuyên cần truy cập và thay đổi
Thông thường, bạn có ít nhất hai cách để quản lý một thuộc tính. Bạn có thể truy cập và thay đổi thuộc tính trực tiếp hoặc bạn có thể sử dụng các phương thức. Phương thức là các chức năng gắn liền với một lớp nhất định. Chúng cung cấp các hành vi và hành động mà một đối tượng có thể thực hiện với dữ liệu và thuộc tính bên trong của nó
Nếu bạn hiển thị các thuộc tính của mình cho người dùng, thì chúng sẽ trở thành một phần của API công khai của các lớp của bạn. Người dùng của bạn sẽ truy cập và thay đổi chúng trực tiếp trong mã của họ. Sự cố xảy ra khi bạn cần thay đổi cách triển khai nội bộ của một thuộc tính nhất định
Các ngôn ngữ lập trình như Java và C++ khuyến khích bạn không bao giờ để lộ các thuộc tính của mình để tránh loại vấn đề này. Thay vào đó, bạn nên cung cấp các phương thức getter và setter, còn được gọi là bộ truy cập và bộ biến đổi, tương ứng. Các phương pháp này cung cấp một cách để thay đổi cách triển khai nội bộ các thuộc tính của bạn mà không cần thay đổi API công khai của bạn
Ghi chú. Các phương thức getter và setter thường được coi là phản mẫu và là tín hiệu của thiết kế hướng đối tượng kém. Đối số chính đằng sau đề xuất này là các phương thức này phá vỡ sự đóng gói. Chúng cho phép bạn truy cập và thay đổi các thành phần của đối tượng của bạn
Cuối cùng, những ngôn ngữ này cần các phương thức getter và setter vì chúng không cung cấp cách phù hợp để thay đổi cách triển khai bên trong của một thuộc tính nếu một yêu cầu nhất định thay đổi. Việc thay đổi triển khai nội bộ sẽ yêu cầu sửa đổi API, điều này có thể phá vỡ mã của người dùng cuối của bạn
Loại bỏ các quảng cáo
Phương pháp Getter và Setter trong Python
Về mặt kỹ thuật, không có gì ngăn cản bạn sử dụng getter và setter trong Python. Đây là cách tiếp cận này sẽ trông như thế nào
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
4 với hai thuộc tính không công khai là
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
5 và
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
6 để giữ tọa độ Descartes của điểm trong tầm tay
Ghi chú. Python không có khái niệm về công cụ sửa đổi truy cập, chẳng hạn như
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
7,
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
8 và
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
9, để hạn chế quyền truy cập vào các thuộc tính và phương thức. Trong Python, sự khác biệt là giữa các thành viên lớp công khai và không công khai
Nếu bạn muốn báo hiệu rằng một thuộc tính hoặc phương thức đã cho là không công khai, thì bạn phải sử dụng Python nổi tiếng để thêm tiền tố vào tên bằng dấu gạch dưới (
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
5 và
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
6. Bạn có thể sử dụng phương thức setter để lưu trữ một giá trị mới trong thuộc tính được quản lý tương ứng. Từ mã này, bạn có thể xác nhận rằng Python không hạn chế quyền truy cập vào các thuộc tính không công khai. Bạn có làm như vậy hay không là tùy thuộc vào bạn
Phương pháp tiếp cận Pythonic
Mặc dù ví dụ bạn vừa thấy sử dụng phong cách mã hóa Python, nhưng nó không giống Pythonic. Trong ví dụ này, các phương thức getter và setter không thực hiện thêm bất kỳ xử lý nào với
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
5 và
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
6. Bạn có thể viết lại
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
Mã này phát hiện ra một nguyên tắc cơ bản. Việc hiển thị các thuộc tính cho người dùng cuối là bình thường và phổ biến trong Python. Bạn không cần phải làm lộn xộn các lớp của mình với các phương thức getter và setter mọi lúc, điều này nghe có vẻ khá thú vị. Tuy nhiên, làm thế nào bạn có thể xử lý các thay đổi yêu cầu dường như liên quan đến thay đổi API?
Không giống như Java và C++, Python cung cấp các công cụ tiện dụng cho phép bạn thay đổi cách triển khai cơ bản của các thuộc tính mà không cần thay đổi API công khai của bạn. Cách tiếp cận phổ biến nhất là biến thuộc tính của bạn thành thuộc tính
Ghi chú. Một cách tiếp cận phổ biến khác để cung cấp các thuộc tính được quản lý là sử dụng các bộ mô tả. Tuy nhiên, trong hướng dẫn này, bạn sẽ tìm hiểu về các thuộc tính
Các thuộc tính đại diện cho một chức năng trung gian giữa một thuộc tính đơn giản (hoặc trường) và một phương thức. Nói cách khác, chúng cho phép bạn tạo các phương thức hoạt động giống như các thuộc tính. Với các thuộc tính, bạn có thể thay đổi cách tính toán thuộc tính đích bất cứ khi nào bạn cần
6 sẽ cho phép bạn sửa đổi triển khai nội bộ của chúng và thực hiện hành động đối với chúng ngay trước khi người dùng của bạn truy cập và thay đổi chúng
Ghi chú. Các thuộc tính không dành riêng cho Python. Các ngôn ngữ như JavaScript, C#, Kotlin và các ngôn ngữ khác cũng cung cấp các công cụ và kỹ thuật để tạo các thuộc tính với tư cách là thành viên của lớp
Ưu điểm chính của các thuộc tính Python là chúng cho phép bạn hiển thị các thuộc tính của mình như một phần của API công khai. Nếu bạn cần thay đổi triển khai cơ bản, thì bạn có thể biến thuộc tính thành thuộc tính bất kỳ lúc nào mà không gặp nhiều khó khăn
Trong các phần sau, bạn sẽ tìm hiểu cách tạo thuộc tính trong Python
Loại bỏ các quảng cáo
Bắt đầu với # circle.py class Circle: def __init__(self, radius): self._radius = radius def _get_radius(self): print("Get radius") return self._radius def _set_radius(self, value): print("Set radius") self._radius = value def _del_radius(self): print("Delete radius") del self._radius radius = property( fget=_get_radius, fset=_set_radius, fdel=_del_radius, doc="The radius property." ) 4 của Python
Python là cách Pythonic để tránh các phương thức getter và setter chính thức trong mã của bạn. Chức năng này cho phép bạn chuyển thành thuộc tính hoặc thuộc tính được quản lý. Vì
4 là một lớp được thiết kế để hoạt động như một chức năng hơn là một lớp thông thường. Đó là lý do tại sao hầu hết các nhà phát triển Python gọi nó là một chức năng. Đó cũng là lý do tại sao
4, bạn có thể đính kèm các phương thức getter và setter cho các thuộc tính lớp đã cho. Bằng cách này, bạn có thể xử lý việc triển khai nội bộ cho thuộc tính đó mà không để lộ các phương thức getter và setter trong API của mình. Bạn cũng có thể chỉ định cách xử lý việc xóa thuộc tính và cung cấp chuỗi tài liệu phù hợp cho thuộc tính của mình
4 nhận các đối tượng hàm. Bạn có thể coi một đối tượng hàm là tên hàm mà không cần gọi cặp dấu ngoặc đơn
Bạn có thể sử dụng
deffunc(a):returnafunc=decorator(func)
4 để cung cấp chuỗi tài liệu thích hợp cho tài sản của mình. Bạn và các lập trình viên đồng nghiệp của bạn sẽ có thể đọc chuỗi tài liệu đó bằng Python. Đối số
deffunc(a):returnafunc=decorator(func)
4 cũng hữu ích khi bạn đang làm việc với trình soạn thảo mã và IDE hỗ trợ truy cập chuỗi tài liệu
4 làm chức năng hoặc công cụ trang trí để xây dựng thuộc tính của mình. Trong hai phần sau, bạn sẽ học cách sử dụng cả hai phương pháp. Tuy nhiên, bạn nên biết trước rằng phương pháp trang trí phổ biến hơn trong cộng đồng Python
Tạo thuộc tính với # circle.py class Circle: def __init__(self, radius): self._radius = radius def _get_radius(self): print("Get radius") return self._radius def _set_radius(self, value): print("Set radius") self._radius = value def _del_radius(self): print("Delete radius") del self._radius radius = property( fget=_get_radius, fset=_set_radius, fdel=_del_radius, doc="The radius property." ) 4
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
16 và
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
18 khi cần. Khi bạn thực thi
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
31, Python gọi
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
21, thao tác này sẽ xóa
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
15 bên dưới
Sử dụng Hàm
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
34 làm Phương thức GetterHiển thị/Ẩn
Bên cạnh việc sử dụng các hàm được đặt tên thông thường để cung cấp các phương thức getter trong các thuộc tính của bạn, bạn cũng có thể sử dụng các hàm
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
Nếu chức năng của phương thức getter của bạn bị giới hạn chỉ trả về giá trị hiện tại của thuộc tính được quản lý, thì sử dụng hàm
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
34 có thể là một cách tiếp cận hữu ích
Thuộc tính là thuộc tính lớp quản lý thuộc tính thể hiện. Bạn có thể coi một thuộc tính là một tập hợp các phương thức được kết hợp với nhau. Nếu bạn kiểm tra cẩn thận
Bạn có thể truy cập các phương thức getter, setter và deleter trong một thuộc tính nhất định thông qua các phương thức tương ứng
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
44,
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
45 và
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
46
Các thuộc tính cũng ghi đè các mô tả. Nếu bạn sử dụng để kiểm tra các thành viên nội bộ của một tài sản nhất định, thì bạn sẽ tìm thấy
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
48 và
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
49 trong danh sách. Các phương pháp này cung cấp một cài đặt mặc định của
Ghi chú. Nếu bạn muốn hiểu rõ hơn về việc triển khai nội bộ của
@decoratordeffunc(a):returna
4 với tư cách là một lớp, hãy xem phần mô tả trong tài liệu
Ví dụ: triển khai mặc định của
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
48 sẽ chạy khi bạn không cung cấp phương thức thiết lập tùy chỉnh. Trong trường hợp này, bạn nhận được một
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
53 vì không có cách nào để đặt thuộc tính cơ bản
Loại bỏ các quảng cáo
Sử dụng # circle.py class Circle: def __init__(self, radius): self._radius = radius def _get_radius(self): print("Get radius") return self._radius def _set_radius(self, value): print("Set radius") self._radius = value def _del_radius(self): print("Delete radius") del self._radius radius = property( fget=_get_radius, fset=_set_radius, fdel=_del_radius, doc="The radius property." ) 4 làm Trang trí
Trang trí ở khắp mọi nơi trong Python. Chúng là các hàm lấy một hàm khác làm đối số và trả về một hàm mới với chức năng được thêm vào. Với một trình trang trí, bạn có thể đính kèm các thao tác trước và sau xử lý vào một chức năng hiện có
4, cú pháp trang trí không khả dụng. Cách duy nhất để xác định các thuộc tính là chuyển các phương thức getter, setter và deleter, như bạn đã học trước đây. Cú pháp trang trí đã được thêm vào và ngày nay, sử dụng
6 bây giờ trông Pythonic và sạch sẽ hơn. Bạn không cần sử dụng các tên phương thức như
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
16,
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
18 và
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
21 nữa. Bây giờ bạn có ba phương thức có cùng tên giống như thuộc tính rõ ràng và mô tả. Làm thế nào là có thể?
Phương pháp trang trí để tạo thuộc tính yêu cầu xác định phương thức đầu tiên sử dụng tên công khai cho thuộc tính được quản lý cơ bản, trong trường hợp này là
7 (dòng 8) để giữ nó. Thuộc tính mới này chứa cùng một tập hợp các phương thức của thuộc tính ban đầu ở dòng 8 với việc bổ sung phương thức setter mới được cung cấp ở dòng 14. Cuối cùng, cú pháp trang trí gán lại thuộc tính mới cho tên cấp lớp
7 như cách bạn truy cập vào một thuộc tính thông thường, đây là mục đích sử dụng chính của các thuộc tính. Chúng cho phép bạn coi các phương thức là thuộc tính và chúng đảm nhiệm việc tự động gọi tập phương thức cơ bản
Dưới đây là bản tóm tắt một số điểm quan trọng cần nhớ khi bạn tạo các thuộc tính bằng phương pháp trang trí
Trình trang trí
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
63 phải trang trí phương thức getter
Chuỗi tài liệu phải đi theo phương thức getter
Các phương thức setter và deleter phải được trang trí bằng tên của phương thức getter cộng với
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
90 và
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
91, tương ứng
Cho đến thời điểm này, bạn đã tạo các thuộc tính được quản lý bằng cách sử dụng
6 của mình cho đến nay, thì bạn sẽ lưu ý rằng các phương thức getter và setter của chúng không thêm bất kỳ xử lý bổ sung thực sự nào vào đầu các thuộc tính của bạn
Nói chung, bạn nên tránh biến các thuộc tính không cần xử lý thêm thành các thuộc tính. Sử dụng các thuộc tính trong những tình huống đó có thể làm cho mã của bạn
Dài dòng không cần thiết
Gây nhầm lẫn cho các nhà phát triển khác
Chậm hơn mã dựa trên các thuộc tính thông thường
Trừ khi bạn cần thứ gì đó hơn là quyền truy cập thuộc tính trần, đừng viết thuộc tính. Chúng làm lãng phí thời gian của CPU và quan trọng hơn, chúng làm lãng phí thời gian của bạn. Cuối cùng, bạn nên tránh viết các phương thức getter và setter rõ ràng và sau đó gói chúng trong một thuộc tính. Thay vào đó, hãy sử dụng trình trang trí
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
63. Đó hiện là cách Pythonic nhất để đi
Loại bỏ các quảng cáo
0) trong tên sẽ cho các nhà phát triển khác biết rằng chúng là thuộc tính không công khai và không nên truy cập bằng cách sử dụng ký hiệu dấu chấm, chẳng hạn như trong
4 để cung cấp các thuộc tính được quản lý với khả năng đọc-ghi. Trong thực tế, bạn chỉ cần cung cấp phương thức getter (“read”) và phương thức setter (“write”) thích hợp cho các thuộc tính của mình để tạo các thuộc tính được quản lý đọc-ghi
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
0. Tuy nhiên, việc lấy bán kính và đường kính trong trình khởi tạo lớp dường như không cần thiết vì bạn có thể tính toán cái này bằng cách sử dụng cái kia. Đây là một
7 đọc-ghi. Trong trường hợp này, phương thức getter chỉ trả về giá trị bán kính. Phương thức setter chuyển đổi giá trị đầu vào cho bán kính và gán nó cho biến không công khai
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
15, là biến bạn sử dụng để lưu trữ dữ liệu cuối cùng
Có một chi tiết tinh tế cần lưu ý trong triển khai mới này của
Bạn cũng có thể tạo các thuộc tính chỉ ghi bằng cách điều chỉnh cách bạn triển khai phương thức getter cho các thuộc tính của mình. Ví dụ: bạn có thể làm cho phương thức getter của mình đưa ra một ngoại lệ mỗi khi người dùng truy cập giá trị thuộc tính cơ bản
Đây là một ví dụ về xử lý mật khẩu với thuộc tính chỉ ghi
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
49 để tạo một chuỗi ngẫu nhiên 32 byte làm muối của hàm băm của bạn. Để tạo mật khẩu băm, bạn sử dụng. Sau đó, bạn lưu trữ mật khẩu được băm trong thuộc tính không công khai
4 làm chức năng và trang trí, đồng thời tìm hiểu về sự khác biệt giữa hai cách tiếp cận này. Bạn cũng đã học cách tạo các thuộc tính chỉ đọc, chỉ đọc và ghi
Trong các phần sau, bạn sẽ viết mã một số ví dụ để giúp bạn hiểu rõ hơn về thực tế các trường hợp sử dụng phổ biến của
4 là xây dựng các thuộc tính được quản lý để xác thực dữ liệu đầu vào trước khi lưu trữ hoặc thậm chí chấp nhận nó dưới dạng đầu vào an toàn. Xác thực dữ liệu là một yêu cầu phổ biến trong mã lấy đầu vào từ người dùng hoặc các nguồn thông tin khác mà bạn cho là không đáng tin cậy
72 để ẩn các chi tiết nội bộ liên quan đến ngữ cảnh mà bạn đang đưa ra ngoại lệ. Từ quan điểm của người dùng cuối, những chi tiết này có thể gây nhầm lẫn và làm cho lớp học của bạn trông không được trau chuốt
Kiểm tra phần trên tài liệu để biết thêm thông tin về chủ đề này
Đó là nó. Bạn có mã lặp đi lặp lại theo các mẫu cụ thể. Sự lặp lại này phá vỡ nguyên tắc DRY (Don't Repeat Yourself), vì vậy bạn sẽ muốn cấu trúc lại mã này để tránh nó. Để làm như vậy, bạn có thể trừu tượng hóa logic lặp đi lặp lại bằng cách sử dụng bộ mô tả
85 làm bộ mô tả quản lý xác thực dữ liệu của bạn ở một nơi duy nhất. Mã này hoạt động giống như cách triển khai trước đó của bạn. Đi trước và cung cấp cho nó một thử
Nói chung, nếu bạn thấy mình đang sao chép và dán các định nghĩa thuộc tính xung quanh mã của mình hoặc nếu bạn phát hiện thấy mã lặp lại như trong ví dụ trên, thì bạn nên cân nhắc sử dụng một bộ mô tả phù hợp
Loại bỏ các quảng cáo
Cung cấp các thuộc tính được tính toán
Nếu bạn cần một thuộc tính có thể xây dựng giá trị của nó một cách linh hoạt bất cứ khi nào bạn truy cập nó, thì
4 là cách tốt nhất. Những loại thuộc tính này thường được gọi là thuộc tính được tính toán. Chúng tiện dụng khi bạn cần chúng trông giống như thuộc tính háo hức, nhưng bạn muốn chúng trở nên lười biếng
Lý do chính để tạo thuộc tính háo hức là để tối ưu hóa chi phí tính toán khi bạn truy cập thuộc tính thường xuyên. Mặt khác, nếu bạn hiếm khi sử dụng một thuộc tính nhất định, thì thuộc tính lười biếng có thể trì hoãn việc tính toán của nó cho đến khi cần thiết, điều này có thể làm cho chương trình của bạn hiệu quả hơn
94 là thuộc tính định dạng và trả về giá của một sản phẩm cụ thể. Để cung cấp định dạng giống như tiền tệ, bạn sử dụng chuỗi f với các tùy chọn định dạng phù hợp
Ghi chú. Ví dụ này sử dụng số dấu phẩy động để biểu thị tiền tệ, đây là cách làm không tốt. Thay vào đó, bạn nên sử dụng từ thư viện chuẩn
Như một ví dụ cuối cùng về các thuộc tính được tính toán, giả sử bạn có một lớp
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
6 làm tọa độ Descartes. Bạn muốn cung cấp tọa độ cực cho điểm của mình để bạn có thể sử dụng chúng trong một số tính toán. Hệ tọa độ cực biểu thị mỗi điểm bằng cách sử dụng khoảng cách đến gốc tọa độ và góc với trục tọa độ nằm ngang
Đây là lớp tọa độ Descartes
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
4 là một công cụ khá tiện dụng. Tuy nhiên, nếu bạn đang tạo một thuộc tính mà bạn sử dụng thường xuyên, thì việc tính toán thuộc tính đó mỗi lần có thể tốn kém và lãng phí. Một chiến lược tốt là lưu trữ chúng sau khi tính toán xong
Bộ nhớ đệm thuộc tính được tính toán
Đôi khi bạn có một thuộc tính tính toán nhất định mà bạn sử dụng thường xuyên. Liên tục lặp lại cùng một phép tính có thể không cần thiết và tốn kém. Để khắc phục sự cố này, bạn có thể lưu giá trị được tính vào bộ đệm ẩn và lưu nó vào một thuộc tính dành riêng không công khai để sử dụng lại sau này
Để ngăn chặn các hành vi không mong muốn, bạn cần nghĩ đến khả năng thay đổi của dữ liệu đầu vào. Nếu bạn có một thuộc tính tính toán giá trị của nó từ các giá trị đầu vào không đổi, thì kết quả sẽ không bao giờ thay đổi. Trong trường hợp đó, bạn có thể tính toán giá trị chỉ một lần
Trong các ví dụ này, bạn tạo một hình tròn có bán kính bằng
property(fget=None,fset=None,fdel=None,doc=None)
07. Thuộc tính
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
0 chỉ tính toán giá trị của nó khi bạn truy cập lần đầu tiên. Đó là lý do tại sao bạn thấy độ trễ trong lần thực thi đầu tiên và không có độ trễ trong lần thực thi thứ hai. Lưu ý rằng ngay cả khi bạn thay đổi giá trị của bán kính, đường kính vẫn giữ nguyên
Nếu dữ liệu đầu vào cho một thuộc tính được tính toán bị thay đổi, thì bạn cần phải tính toán lại thuộc tính đó
6 hiện đang hoạt động bình thường. Nó tính toán đường kính khi bạn truy cập lần đầu tiên và mỗi khi bạn thay đổi bán kính
Một tùy chọn khác để tạo các thuộc tính được lưu trong bộ nhớ cache là sử dụng từ thư viện chuẩn. Chức năng này hoạt động như một công cụ trang trí cho phép bạn chuyển đổi một phương thức thành một thuộc tính được lưu trong bộ nhớ cache. Thuộc tính chỉ tính toán giá trị của nó một lần và lưu trữ nó như một thuộc tính bình thường trong suốt thời gian tồn tại của thể hiện
property(fget=None,fset=None,fdel=None,doc=None)
1
Ở đây,
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
0 tính toán và lưu trữ giá trị của nó khi bạn truy cập lần đầu tiên. Kiểu triển khai này phù hợp với những tính toán trong đó các giá trị đầu vào không thay đổi. Đây là cách nó hoạt động
>>>
property(fget=None,fset=None,fdel=None,doc=None)
2
Khi bạn truy cập vào
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
0, bạn sẽ nhận được giá trị tính toán của nó. Giá trị đó vẫn giữ nguyên từ thời điểm này trở đi. Tuy nhiên, không giống như
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
63 lên trên
property(fget=None,fset=None,fdel=None,doc=None)
24. Sự kết hợp của cả hai bộ trang trí sẽ tạo ra một thuộc tính được lưu trong bộ nhớ cache để ngăn chặn các đột biến
>>>
property(fget=None,fset=None,fdel=None,doc=None)
4
Trong các ví dụ này, khi bạn cố gắng gán một giá trị mới cho
>>> fromcircleimportCircle>>> circle=Circle(42.0)>>> circle.radiusGet radius42.0>>> circle.radius=100.0Set radius>>> circle.radiusGet radius100.0>>> delcircle.radiusDelete radius>>> circle.radiusGet radiusTraceback (most recent call last):...AttributeError: 'Circle' object has no attribute '_radius'>>> help(circle)Help on Circle in module __main__ object:class Circle(builtins.object) ... | radius | The radius property.
0, bạn sẽ nhận được một
>>> frompointimportPoint>>> point=Point(12,5)>>> point.get_x()12>>> point.get_y()5>>> point.set_x(42)>>> point.get_x()42>>> # Non-public attributes are still accessible>>> point._x42>>> point._y5
53 vì chức năng setter đến từ bộ mô tả bên trong của
@decoratordeffunc(a):returna
4
Loại bỏ các quảng cáo
Ghi nhật ký truy cập thuộc tính và đột biến
Đôi khi bạn cần theo dõi mã của mình làm gì và chương trình của bạn chạy như thế nào. Một cách để làm điều đó trong Python là sử dụng
property(fget=None,fset=None,fdel=None,doc=None)
28. Mô-đun này cung cấp tất cả các chức năng bạn cần để ghi nhật ký mã của mình. Nó sẽ cho phép bạn liên tục xem mã và tạo thông tin hữu ích về cách thức hoạt động của nó
Nếu bạn cần theo dõi cách thức và thời điểm bạn truy cập và thay đổi một thuộc tính nhất định, thì bạn cũng có thể tận dụng
Ghi nhật ký dữ liệu hữu ích từ truy cập thuộc tính và biến đổi có thể giúp bạn gỡ lỗi mã của mình. Ghi nhật ký cũng có thể giúp bạn xác định các nguồn đầu vào dữ liệu có vấn đề, phân tích hiệu suất mã của bạn, phát hiện các kiểu sử dụng, v.v.
Quản lý xóa thuộc tính
Bạn cũng có thể tạo các thuộc tính thực hiện chức năng xóa. Đây có thể là một trường hợp sử dụng hiếm hoi của
4, nhưng có một cách để xóa một thuộc tính có thể hữu ích trong một số trường hợp
Giả sử bạn đang triển khai kiểu dữ liệu cây của riêng mình. Cây là một kiểu dữ liệu trừu tượng lưu trữ các phần tử trong một hệ thống phân cấp. Các thành phần cây thường được gọi là các nút. Mỗi nút trong cây có một nút cha, ngoại trừ nút gốc. Các nút có thể có 0 hoặc nhiều nút con
Bây giờ, giả sử bạn cần cung cấp cách xóa hoặc xóa danh sách con của một nút nhất định. Đây là một ví dụ triển khai nút cây sử dụng
4 để cung cấp hầu hết các chức năng của nó, bao gồm khả năng xóa danh sách nút con của nút hiện tại
property(fget=None,fset=None,fdel=None,doc=None)
7
Trong ví dụ này,
property(fget=None,fset=None,fdel=None,doc=None)
38 đại diện cho một nút trong kiểu dữ liệu cây tùy chỉnh của bạn. Mỗi nút lưu các nút con của nó trong danh sách Python. Sau đó, bạn triển khai
property(fget=None,fset=None,fdel=None,doc=None)
39 làm thuộc tính để quản lý danh sách trẻ em cơ bản. Phương thức xóa gọi _______ 46 ______ 40 trong danh sách con để xóa tất cả
>>>
property(fget=None,fset=None,fdel=None,doc=None)
8
Tại đây, trước tiên bạn tạo một nút
property(fget=None,fset=None,fdel=None,doc=None)
41 để bắt đầu điền vào cây. Sau đó, bạn tạo hai nút mới và gán chúng cho
property(fget=None,fset=None,fdel=None,doc=None)
39 bằng danh sách. Câu lệnh kích hoạt phương thức xóa nội bộ của
property(fget=None,fset=None,fdel=None,doc=None)
39 và xóa danh sách
Tạo API lớp tương thích ngược
Như bạn đã biết, thuộc tính biến lời gọi phương thức thành tra cứu thuộc tính trực tiếp. Tính năng này cho phép bạn tạo API sạch và Pythonic cho các lớp học của mình. Bạn có thể hiển thị công khai các thuộc tính của mình mà không cần các phương thức getter và setter
Nếu bạn cần sửa đổi cách bạn tính toán một thuộc tính công khai đã cho, thì bạn có thể biến nó thành một thuộc tính. Các thuộc tính giúp có thể thực hiện xử lý bổ sung, chẳng hạn như xác thực dữ liệu mà không phải sửa đổi các API công khai của bạn
Giả sử bạn đang tạo một ứng dụng kế toán và bạn cần một lớp cơ sở để quản lý tiền tệ. Để đạt được điều này, bạn tạo một lớp
property(fget=None,fset=None,fdel=None,doc=None)
45 hiển thị hai thuộc tính,
property(fget=None,fset=None,fdel=None,doc=None)
46 và
property(fget=None,fset=None,fdel=None,doc=None)
47
property(fget=None,fset=None,fdel=None,doc=None)
9
Lớp này trông sạch sẽ và Pythonic. Bây giờ giả sử rằng yêu cầu của bạn thay đổi và bạn quyết định lưu trữ tổng số xu thay vì đơn vị và xu. Xóa
property(fget=None,fset=None,fdel=None,doc=None)
46 và
property(fget=None,fset=None,fdel=None,doc=None)
47 khỏi API công khai của bạn để sử dụng thứ gì đó như
Giờ đây, lớp của bạn lưu trữ tổng số xu thay vì các đơn vị và xu độc lập. Tuy nhiên, người dùng của bạn vẫn có thể truy cập và thay đổi
property(fget=None,fset=None,fdel=None,doc=None)
46 và
property(fget=None,fset=None,fdel=None,doc=None)
47 trong mã của họ và nhận được kết quả tương tự như trước đây. Đi trước và cung cấp cho nó một thử
Khi bạn viết thứ gì đó mà nhiều người sẽ xây dựng dựa trên đó, bạn cần đảm bảo rằng các sửa đổi đối với triển khai nội bộ không ảnh hưởng đến cách người dùng cuối làm việc với các lớp của bạn
Loại bỏ các quảng cáo
Thuộc tính ghi đè trong các lớp con
Khi bạn tạo các lớp Python bao gồm các thuộc tính và phát hành chúng trong một gói hoặc thư viện, bạn nên mong đợi người dùng của mình thực hiện nhiều việc khác nhau với chúng. Một trong những điều đó có thể là phân lớp chúng để tùy chỉnh các chức năng của chúng. Trong những trường hợp này, người dùng của bạn phải cẩn thận và nhận thức được một dấu hiệu cố ý tinh vi. Nếu bạn ghi đè một phần thuộc tính thì bạn sẽ mất chức năng không ghi đè
Ví dụ: giả sử bạn đang viết mã lớp
property(fget=None,fset=None,fdel=None,doc=None)
54 để quản lý thông tin nhân viên trong hệ thống kế toán nội bộ của công ty bạn. Bạn đã có một lớp tên là
property(fget=None,fset=None,fdel=None,doc=None)
55 và bạn nghĩ về việc phân lớp nó để sử dụng lại các chức năng của nó
38 đã mất phần còn lại của chức năng từ lớp cơ sở. Bạn không còn phương thức setter nữa
Ý tưởng là nếu bạn cần ghi đè một thuộc tính trong một lớp con, thì bạn nên cung cấp tất cả chức năng bạn cần trong phiên bản mới của thuộc tính đó.
Phần kết luận
Thuộc tính là một loại thành viên lớp đặc biệt cung cấp chức năng ở đâu đó giữa các thuộc tính và phương thức thông thường. Các thuộc tính cho phép bạn sửa đổi việc triển khai các thuộc tính cá thể mà không thay đổi API công khai của lớp. Khả năng giữ nguyên các API của bạn giúp bạn tránh vi phạm mã mà người dùng của bạn đã viết trên các phiên bản cũ hơn của lớp học của bạn
Các thuộc tính là cách Pythonic để tạo các thuộc tính được quản lý trong các lớp của bạn. Chúng có một số trường hợp sử dụng trong lập trình trong thế giới thực, khiến chúng trở thành sự bổ sung tuyệt vời cho bộ kỹ năng của bạn với tư cách là nhà phát triển Python
4. Những ví dụ đó bao gồm đầu vào, thuộc tính được tính toán, ghi nhật ký mã của bạn, v.v.
Đánh dấu là đã hoàn thành
Xem ngay Hướng dẫn này có một khóa học video liên quan do nhóm Real Python tạo. Xem nó cùng với hướng dẫn bằng văn bản để hiểu sâu hơn. Quản lý thuộc tính với thuộc tính của Python()
🐍 Thủ thuật Python 💌
Nhận một Thủ thuật Python ngắn và hấp dẫn được gửi đến hộp thư đến của bạn vài ngày một lần. Không có thư rác bao giờ. Hủy đăng ký bất cứ lúc nào. Được quản lý bởi nhóm Real Python
Gửi cho tôi thủ thuật Python »
Giới thiệu về Leodanis Pozo Ramos
Leodanis là một kỹ sư công nghiệp yêu thích Python và phát triển phần mềm. Anh ấy là một nhà phát triển Python tự học với hơn 6 năm kinh nghiệm. Anh ấy là một nhà văn đam mê kỹ thuật với số lượng bài báo được xuất bản ngày càng tăng trên Real Python và các trang web khác
» Tìm hiểu thêm về Leodanis
Mỗi hướng dẫn tại Real Python được tạo bởi một nhóm các nhà phát triển để nó đáp ứng các tiêu chuẩn chất lượng cao của chúng tôi. Các thành viên trong nhóm đã làm việc trong hướng dẫn này là
Aldren
Bartosz
Martin
Sadie
Bậc thầy Kỹ năng Python trong thế giới thực Với quyền truy cập không giới hạn vào Python thực
Tham gia với chúng tôi và có quyền truy cập vào hàng nghìn hướng dẫn, khóa học video thực hành và cộng đồng các Pythonistas chuyên gia
Nâng cao kỹ năng Python của bạn »
Chuyên gia Kỹ năng Python trong thế giới thực
Với quyền truy cập không giới hạn vào Python thực
Tham gia với chúng tôi và có quyền truy cập vào hàng ngàn hướng dẫn, khóa học video thực hành và cộng đồng Pythonistas chuyên gia
Nâng cao kỹ năng Python của bạn »
Bạn nghĩ sao?
Đánh giá bài viết này
Tweet Chia sẻ Chia sẻ Email
Bài học số 1 hoặc điều yêu thích mà bạn đã học được là gì?
Mẹo bình luận. Những nhận xét hữu ích nhất là những nhận xét được viết với mục đích học hỏi hoặc giúp đỡ các sinh viên khác. và nhận câu trả lời cho các câu hỏi phổ biến trong cổng thông tin hỗ trợ của chúng tôi
Khi nào nên sử dụng Setattr trong Python?
Hàm setattr() của Python được sử dụng để đặt giá trị cho thuộc tính của đối tượng . Nó nhận ba đối số là một đối tượng, một chuỗi và một giá trị tùy ý và không trả về giá trị nào. Nó hữu ích khi chúng ta muốn thêm một thuộc tính mới vào một đối tượng và đặt giá trị cho nó.
__ Setattr __ trong Python là gì?
Phương thức setattr() của Python được dùng để gán giá trị cho thuộc tính đối tượng .