Python @property: Cách sử dụng và tại sao? - Programiz

Trong hướng dẫn này, bạn sẽ tìm hiểu về trình trang trí Python @property; một cách hay để sử dụng getters và setters trong lập trình hướng đối tượng.

Lập trình Python cung cấp cho chúng ta trình @propertytrang trí tích hợp giúp việc sử dụng getter và setters dễ dàng hơn nhiều trong Lập trình hướng đối tượng.

Trước khi đi vào chi tiết về @propertydecorator là gì , trước tiên chúng ta hãy xây dựng trực giác về lý do tại sao nó lại cần thiết ngay từ đầu.

Lớp học không có người nhận và người định cư

Giả sử rằng chúng tôi quyết định tạo ra một lớp bảo quản nhiệt độ bằng độ C. Nó cũng sẽ thực hiện một phương pháp để chuyển đổi nhiệt độ thành độ F. Một cách để thực hiện việc này như sau:

 class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32

Chúng ta có thể tạo các đối tượng từ lớp này và thao tác temperaturethuộc tính như chúng ta muốn:

 # Basic method of setting and getting attributes in Python class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # Create a new object human = Celsius() # Set the temperature human.temperature = 37 # Get the temperature attribute print(human.temperature) # Get the to_fahrenheit method print(human.to_fahrenheit())

Đầu ra

 37 98.60000000000001

Các chữ số thập phân thừa khi chuyển đổi thành Fahrenheit là do lỗi số học dấu phẩy động. Để tìm hiểu thêm, hãy truy cập Lỗi số học dấu chấm động của Python.

Bất cứ khi nào chúng ta gán hoặc truy xuất bất kỳ thuộc tính đối tượng nào temperaturenhư được hiển thị ở trên, Python sẽ tìm kiếm nó trong __dict__thuộc tính từ điển có sẵn của đối tượng .

 >>> human.__dict__ ('temperature': 37)

Do đó, man.temperaturetrong nội bộ trở thành man.__dict__('temperature').

Sử dụng Getters và Setters

Giả sử chúng ta muốn mở rộng khả năng sử dụng của lớp Celsius được định nghĩa ở trên. Chúng ta biết rằng nhiệt độ của bất kỳ vật thể nào không thể đạt dưới -273,15 độ C (Độ không tuyệt đối trong Nhiệt động lực học)

Hãy cập nhật mã của chúng tôi để thực hiện ràng buộc giá trị này.

Một giải pháp rõ ràng cho hạn chế trên là ẩn thuộc tính temperature(đặt nó ở chế độ riêng tư) và xác định các phương thức getter và setter mới để thao tác với nó. Điều này có thể được thực hiện như sau:

 # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value

Như chúng ta thấy, phương pháp trên giới thiệu hai phương pháp mới get_temperature()set_temperature().

Hơn nữa, temperatuređã được thay thế bằng _temperature. Dấu gạch dưới _ở đầu được sử dụng để biểu thị các biến riêng trong Python.

Bây giờ, hãy sử dụng triển khai này:

 # Making Getters and Setter methods class Celsius: def __init__(self, temperature=0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # getter method def get_temperature(self): return self._temperature # setter method def set_temperature(self, value): if value < -273.15: raise ValueError("Temperature below -273.15 is not possible.") self._temperature = value # Create a new object, set_temperature() internally called by __init__ human = Celsius(37) # Get the temperature attribute via a getter print(human.get_temperature()) # Get the to_fahrenheit method, get_temperature() called by the method itself print(human.to_fahrenheit()) # new constraint implementation human.set_temperature(-300) # Get the to_fahreheit method print(human.to_fahrenheit())

Đầu ra

 37 98.60000000000001 Truy xuất nguồn gốc (lần gọi gần đây nhất): Tệp "", dòng 30, trong Tệp "", dòng 16, trong Giá trị nhiệt độ set_temperor: Nhiệt độ dưới -273,15 là không thể.

Bản cập nhật này đã thực hiện thành công hạn chế mới. Chúng tôi không còn được phép đặt nhiệt độ dưới -273,15 độ C.

Lưu ý : Các biến private không thực sự tồn tại trong Python. Đơn giản là có các tiêu chuẩn cần tuân thủ. Bản thân ngôn ngữ không áp dụng bất kỳ hạn chế nào.

 >>> human._temperature = -300 >>> human.get_temperature() -300

Tuy nhiên, vấn đề lớn hơn với bản cập nhật trên là tất cả các chương trình mà thực hiện lớp trước chúng tôi phải thay đổi mã của họ từ obj.temperatuređến obj.get_temperature()và tất cả các biểu thức như obj.temperature = valđể obj.set_temperature(val).

Việc tái cấu trúc này có thể gây ra sự cố trong khi xử lý hàng trăm nghìn dòng mã.

Nói chung, bản cập nhật mới của chúng tôi không tương thích ngược. Đây là nơi @propertycần giải cứu.

Lớp tài sản

Một cách hay để giải quyết vấn đề trên là sử dụng propertylớp. Đây là cách chúng tôi có thể cập nhật mã của mình:

 # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value… ") return self._temperature # setter def set_temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature)

Chúng tôi đã thêm một print()chức năng bên trong get_temperature()set_temperature()để quan sát rõ ràng rằng chúng đang được thực thi.

Dòng cuối cùng của mã tạo một đối tượng thuộc tính temperature. Nói một cách đơn giản, thuộc tính đính kèm một số mã ( get_temperatureset_temperature) vào thuộc tính thành viên accesses ( temperature).

Hãy sử dụng mã cập nhật này:

 # using property class class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 # getter def get_temperature(self): print("Getting value… ") return self._temperature # setter def set_temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273.15 is not possible") self._temperature = value # creating a property object temperature = property(get_temperature, set_temperature) human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) human.temperature = -300

Đầu ra

 Giá trị cài đặt… Đang nhận giá trị… 37 Đang nhận giá trị… 98.60000000000001 Giá trị cài đặt… Truy xuất nguồn gốc (lần gọi gần đây nhất): Tệp "", dòng 31, trong Tệp "", dòng 18, trong Giá trị nhiệt độ set_temperor: Nhiệt độ dưới -273 không thực hiện được

Như chúng ta có thể thấy, bất kỳ mã nào truy xuất giá trị của temperaturesẽ tự động gọi get_temperature()thay vì tra cứu từ điển (__dict__). Tương tự, bất kỳ mã nào được gán giá trị cho temperaturesẽ tự động gọi set_temperature().

Chúng ta thậm chí có thể thấy ở trên nó set_temperature()đã được gọi ngay cả khi chúng ta tạo một đối tượng.

 >>> human = Celsius(37) Setting value… 

Bạn có đoán được tại sao không?

Lý do là khi một đối tượng được tạo, __init__()phương thức sẽ được gọi. Phương pháp này có dòng self.temperature = temperature. Biểu thức này tự động gọi set_temperature().

Tương tự, mọi quyền truy cập như c.temperaturetự động gọi get_temperature(). Đây là những gì thuộc tính làm. Đây là một vài ví dụ khác.

 >>> human.temperature Getting value 37 >>> human.temperature = 37 Setting value >>> c.to_fahrenheit() Getting value 98.60000000000001

Bằng cách sử dụng property, chúng ta có thể thấy rằng không cần sửa đổi khi thực hiện ràng buộc giá trị. Do đó, việc triển khai của chúng tôi là tương thích ngược.

Note: The actual temperature value is stored in the private _temperature variable. The temperature attribute is a property object which provides an interface to this private variable.

The @property Decorator

In Python, property() is a built-in function that creates and returns a property object. The syntax of this function is:

 property(fget=None, fset=None, fdel=None, doc=None)

where,

  • fget is function to get value of the attribute
  • fset is function to set value of the attribute
  • fdel is function to delete the attribute
  • doc is a string (like a comment)

As seen from the implementation, these function arguments are optional. So, a property object can simply be created as follows.

 >>> property() 

A property object has three methods, getter(), setter(), and deleter() to specify fget, fset and fdel at a later point. This means, the line:

 temperature = property(get_temperature,set_temperature)

can be broken down as:

 # make empty property temperature = property() # assign fget temperature = temperature.getter(get_temperature) # assign fset temperature = temperature.setter(set_temperature)

Hai đoạn mã này tương đương nhau.

Các lập trình viên quen thuộc với Trình trang trí Python có thể nhận ra rằng cấu trúc trên có thể được triển khai dưới dạng trình trang trí.

Chúng tôi thậm chí không thể xác định tên get_temperatureset_temperaturevì chúng không cần thiết và gây ô nhiễm không gian tên lớp.

Đối với điều này, chúng tôi sử dụng lại temperaturetên trong khi xác định các hàm getter và setter của chúng tôi. Hãy xem cách thực hiện điều này như một người trang trí:

 # Using @property decorator class Celsius: def __init__(self, temperature=0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 @property def temperature(self): print("Getting value… ") return self._temperature @temperature.setter def temperature(self, value): print("Setting value… ") if value < -273.15: raise ValueError("Temperature below -273 is not possible") self._temperature = value # create an object human = Celsius(37) print(human.temperature) print(human.to_fahrenheit()) coldest_thing = Celsius(-300)

Đầu ra

 Giá trị cài đặt… Đang nhận giá trị… 37 Nhận giá trị… 98.60000000000001 Giá trị cài đặt… Truy xuất nguồn gốc (lần gọi gần đây nhất): Tệp "", dòng 29, trong Tệp "", dòng 4, trong __init__ Tệp "", dòng 18, trong nhiệt độ ValueError: Nhiệt độ dưới -273 là không thể

Cách thực hiện trên rất đơn giản và hiệu quả. Đó là cách được khuyến khích để sử dụng property.

thú vị bài viết...