Câu trả lời:
Thuộc tính là một loại thuộc tính đặc biệt. Về cơ bản, khi Python gặp đoạn mã sau:
spam = SomeObject()
print(spam.eggs)
nó sẽ tra cứu eggstrong spam, và sau đó kiểm tra eggsđể xem nếu nó có một __get__, __set__hoặc __delete__phương pháp - nếu có, đó là một tài sản. Nếu nó là một thuộc tính, thay vì chỉ trả về eggsđối tượng (như đối với bất kỳ thuộc tính nào khác), nó sẽ gọi __get__phương thức (vì chúng ta đang thực hiện tra cứu) và trả về bất cứ phương thức nào trả về.
Thông tin thêm về mô hình dữ liệu và mô tả của Python .
Với một thuộc tính, bạn có toàn quyền kiểm soát các phương thức getter, setter và deleter của nó, mà bạn không có (nếu không sử dụng caveats) với một thuộc tính.
class A(object):
_x = 0
'''A._x is an attribute'''
@property
def x(self):
'''
A.x is a property
This is the getter method
'''
return self._x
@x.setter
def x(self, value):
"""
This is the setter method
where I can check it's not assigned a value < 0
"""
if value < 0:
raise ValueError("Must be >= 0")
self._x = value
>>> a = A()
>>> a._x = -1
>>> a.x = -1
Traceback (most recent call last):
File "ex.py", line 15, in <module>
a.x = -1
File "ex.py", line 9, in x
raise ValueError("Must be >= 0")
ValueError: Must be >= 0
x. Một chiều. Nếu người dùng của lớp phát hiện ra về _x, họ sẽ tự chịu rủi ro khi sử dụng nó.
Nói chung, thuật ngữ nói chung một thuộc tính và thuộc tính là cùng một thứ. Tuy nhiên, có một trình trang trí thuộc tính trong Python cung cấp quyền truy cập getter / setter vào một thuộc tính (hoặc dữ liệu khác).
class MyObject(object):
# This is a normal attribute
foo = 1
@property
def bar(self):
return self.foo
@bar.setter
def bar(self, value):
self.foo = value
obj = MyObject()
assert obj.foo == 1
assert obj.bar == obj.foo
obj.bar = 2
assert obj.foo == 2
assert obj.bar == obj.foo
Thuộc tính cho phép bạn nhận và thiết lập các giá trị như các thuộc tính thông thường, nhưng bên dưới có một phương thức được gọi là dịch nó thành getter và setter cho bạn. Nó thực sự chỉ là một sự tiện lợi để cắt giảm bản tóm tắt của việc gọi getters và setters.
Ví dụ, hãy nói rằng bạn có một lớp chứa một số tọa độ x và y cho thứ bạn cần. Để đặt chúng, bạn có thể muốn làm một cái gì đó như:
myObj.x = 5
myObj.y = 10
Điều đó dễ nhìn và suy nghĩ hơn nhiều so với viết:
myObj.setX(5)
myObj.setY(10)
Vấn đề là, điều gì sẽ xảy ra nếu một ngày nào đó lớp của bạn thay đổi đến mức bạn cần phải bù x và y của mình theo một giá trị nào đó? Bây giờ bạn sẽ cần phải đi vào và thay đổi định nghĩa lớp của bạn và tất cả các mã gọi nó, điều này có thể thực sự tốn thời gian và dễ bị lỗi. Thuộc tính cho phép bạn sử dụng cú pháp trước trong khi cung cấp cho bạn sự linh hoạt thay đổi của cú pháp sau.
Trong Python, bạn có thể định nghĩa getters, setters và xóa các phương thức với hàm property. Nếu bạn chỉ muốn thuộc tính đọc, đó cũng là một trang trí @property bạn có thể thêm ở trên phương thức của bạn.
Tóm lại, tôi đã học được 2 điểm khác biệt từ trang Bernd Klein:
1. Tài sản là một cách thuận tiện hơn để thực hiện đóng gói dữ liệu.
vd
#Old codes
obj1.length=obj1.length+obj2.length
#New codes(Using private attibutes and getter and setter)
obj1.set_lenght(obj1.get_length()+obj2.get_length()) #=> this is ugly
Nếu bạn sử dụng @property và @ lenght.setter => bạn không cần thay đổi các mã cũ đó
2. Một thuộc tính có thể gói gọn nhiều thuộc tính
class Person:
def __init__(self, name, physic_health, mental_health):
self.name=name
self.__physic_health=physic_health #physic_health is real value in range [0, 5.0]
self.__mental_health=mental_health #mental_health is real value in range [0, 5.0]
@property
def condition(self):
health=self.__physic_health+self.__mental_health
if(health<5.0):
return "I feel bad!"
elif health<8.0:
return "I am ok!"
else:
return "Great!"
Trong ví dụ này, __physic_healthvà __mental_healthlà riêng tư và không thể được truy cập trực tiếp từ bên ngoài, cách duy nhất bên ngoài lớp tương tác với chúng là thuộc tính thông quacondition
Ngoài ra còn có một sự khác biệt không rõ ràng mà tôi sử dụng để lưu trữ hoặc làm mới dữ liệu, thường thì chúng ta có một chức năng được kết nối với thuộc tính lớp. Chẳng hạn, tôi cần đọc tệp một lần và giữ nội dung được gán cho thuộc tính để giá trị được lưu trữ:
class Misc():
def __init__(self):
self.test = self.test_func()
def test_func(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Đầu ra:
func running
func value
func value
Chúng tôi đã truy cập thuộc tính hai lần nhưng chức năng của chúng tôi chỉ bị bắn một lần. Thay đổi ví dụ trên để sử dụng thuộc tính sẽ khiến giá trị của thuộc tính được làm mới mỗi khi bạn truy cập vào nó:
class Misc():
@property
def test(self):
print 'func running'
return 'func value'
cl = Misc()
print cl.test
print cl.test
Đầu ra:
func running
func value
func running
func value