Cách pythonic để sử dụng getters và setters là gì?
Cách "Pythonic" không phải là sử dụng "getters" và "setters", mà là sử dụng các thuộc tính đơn giản, như câu hỏi minh họa và del
để xóa (nhưng các tên được thay đổi để bảo vệ ... nội dung vô tội):
value = 'something'
obj.attribute = value
value = obj.attribute
del obj.attribute
Nếu sau này, bạn muốn sửa đổi cài đặt và nhận, bạn có thể làm như vậy mà không phải thay đổi mã người dùng, bằng cách sử dụng trình property
trang trí:
class Obj:
"""property demo"""
#
@property # first decorate the getter method
def attribute(self): # This getter method name is *the* name
return self._attribute
#
@attribute.setter # the property decorates with `.setter` now
def attribute(self, value): # name, e.g. "attribute", is the same
self._attribute = value # the "value" name isn't special
#
@attribute.deleter # decorate with `.deleter`
def attribute(self): # again, the method name is the same
del self._attribute
(Mỗi cách sử dụng trang trí sao chép và cập nhật đối tượng thuộc tính trước đó, vì vậy lưu ý rằng bạn nên sử dụng cùng tên cho mỗi bộ, nhận và xóa chức năng / phương thức.
Sau khi xác định ở trên, cài đặt gốc, nhận và xóa mã là như nhau:
obj = Obj()
obj.attribute = value
the_value = obj.attribute
del obj.attribute
Bạn nên tránh điều này:
def set_property(property,value):
def get_property(property):
Thứ nhất, ở trên không hoạt động, bởi vì bạn không cung cấp một đối số cho trường hợp thuộc tính sẽ được đặt thành (thường self
), đó sẽ là:
class Obj:
def set_property(self, property, value): # don't do this
...
def get_property(self, property): # don't do this either
...
Thứ hai, điều này trùng lặp mục đích của hai phương pháp đặc biệt, __setattr__
và __getattr__
.
Thứ ba, chúng tôi cũng có setattr
và getattr
được xây dựng trong các chức năng.
setattr(object, 'property_name', value)
getattr(object, 'property_name', default_value) # default is optional
Các @property
trang trí là để tạo ra getters và setters.
Ví dụ: chúng tôi có thể sửa đổi hành vi cài đặt để đặt các hạn chế giá trị được đặt:
class Protective(object):
@property
def protected_value(self):
return self._protected_value
@protected_value.setter
def protected_value(self, value):
if acceptable(value): # e.g. type or range check
self._protected_value = value
Nói chung, chúng tôi muốn tránh sử dụng property
và chỉ sử dụng các thuộc tính trực tiếp.
Đây là những gì được mong đợi bởi người dùng Python. Theo quy tắc ít gây ngạc nhiên nhất, bạn nên cố gắng cung cấp cho người dùng những gì họ mong đợi trừ khi bạn có một lý do rất thuyết phục ngược lại.
Trình diễn
Ví dụ: giả sử chúng tôi cần thuộc tính được bảo vệ của đối tượng của chúng tôi là một số nguyên nằm trong khoảng từ 0 đến 100, và ngăn chặn việc xóa nó, với các thông báo phù hợp để thông báo cho người dùng về cách sử dụng đúng:
class Protective(object):
"""protected property demo"""
#
def __init__(self, start_protected_value=0):
self.protected_value = start_protected_value
#
@property
def protected_value(self):
return self._protected_value
#
@protected_value.setter
def protected_value(self, value):
if value != int(value):
raise TypeError("protected_value must be an integer")
if 0 <= value <= 100:
self._protected_value = int(value)
else:
raise ValueError("protected_value must be " +
"between 0 and 100 inclusive")
#
@protected_value.deleter
def protected_value(self):
raise AttributeError("do not delete, protected_value can be set to 0")
(Lưu ý __init__
đề cập đến self.protected_value
nhưng các phương thức thuộc tính đề cập đến self._protected_value
. Điều này là để __init__
sử dụng thuộc tính thông qua API công khai, đảm bảo nó được "bảo vệ".)
Và cách sử dụng:
>>> p1 = Protective(3)
>>> p1.protected_value
3
>>> p1 = Protective(5.0)
>>> p1.protected_value
5
>>> p2 = Protective(-5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in __init__
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> p1.protected_value = 7.3
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 17, in protected_value
TypeError: protected_value must be an integer
>>> p1.protected_value = 101
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 15, in protected_value
ValueError: protectected_value must be between 0 and 100 inclusive
>>> del p1.protected_value
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 18, in protected_value
AttributeError: do not delete, protected_value can be set to 0
Làm tên quan trọng?
Có họ làm . .setter
và .deleter
tạo bản sao của tài sản ban đầu. Điều này cho phép các lớp con sửa đổi hành vi đúng cách mà không thay đổi hành vi trong cha mẹ.
class Obj:
"""property demo"""
#
@property
def get_only(self):
return self._attribute
#
@get_only.setter
def get_or_set(self, value):
self._attribute = value
#
@get_or_set.deleter
def get_set_or_delete(self):
del self._attribute
Bây giờ để làm việc này, bạn phải sử dụng các tên tương ứng:
obj = Obj()
# obj.get_only = 'value' # would error
obj.get_or_set = 'value'
obj.get_set_or_delete = 'new value'
the_value = obj.get_only
del obj.get_set_or_delete
# del obj.get_or_set # would error
Tôi không chắc nơi này sẽ hữu ích, nhưng trường hợp sử dụng là nếu bạn muốn một thuộc tính get, set và / hoặc chỉ xóa. Có lẽ tốt nhất để gắn bó với cùng một tài sản có cùng tên.
Phần kết luận
Bắt đầu với các thuộc tính đơn giản.
Nếu sau này bạn cần chức năng xung quanh cài đặt, nhận và xóa, bạn có thể thêm nó với trình trang trí thuộc tính.
Tránh các chức năng được đặt tên set_...
và get_...
- đó là những gì thuộc tính dành cho.