English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
Python имеет великолепную концепцию, называемую свойством, которая упрощает жизнь面向对象的 программистов.
Прежде чем определить и подробно понять, что такое @property, давайте разберемся, почему его использование необходимо в первую очередь.
Предположим, что вы решилиСоздать одинКласс для хранения температуры в градусах Цельсия. Он также будет реализовывать метод преобразования температуры в градусы по Фаренгейту. Один из методов таков.
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32
Мы можем создавать объекты из этого класса и манипулировать атрибутом temperature по мере необходимости. Попробуйте это в Python shell.
>>> # Создать новый объект >>> man = Celsius() >>> # Установить температуру >>> man.temperature = 37 >>> # Получить температуру >>> man.temperature 37 >>> # Получить градусы по Фаренгейту >>> man.to_fahrenheit() 98.60000000000001
Избыточные десятичные места при преобразовании в фahrenheit возникают из-за ошибки плавающей точки (в Python interpreter при попытке 1.1 + 2.2).
Как показано выше, каждый раз, когда мы назначаем или получаем любую атрибут объекта (напримерtemperature) Python будет выполнять поиск в словаре __dict__ объекта.
>>> man.__dict__ {'temperature': 37}
Таким образом, man.temperature внутри становится man.__dict__['temperature'].
Теперь, предположим, что наш курс очень популярен среди клиентов, и они начали использовать его в своих программах. Они сделали различные назначения объектов.
Однажды, один из надежных клиентов пришел к нам, предложив, что температура не должна быть ниже -273 градусов Цельсия (студенты, специализирующиеся на термодинамике, могут сказать, что это на самом деле -273.15 градуса), также известной как абсолютный ноль. Он также предложил нам реализовать это ограничение значения. Как компания, стремящаяся к удовлетворенности клиентов, мы с радостью рассмотрели это предложение и выпустили версию 1.01 (апгрейд существующего класса).
Одной из очевидных методик решения этих ограничений является скрытие атрибута temperature (сделав его приватным) и определение новых getter и setter интерфейсов для его работы. Это можно сделать следующим образом.
class Celsius: def __init__(self, temperature = 0): self.set_temperature(temperature) def to_fahrenheit(self): return (self.get_temperature() * 1.8) + 32 # new update def get_temperature(self): return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("-273 градуса是不可能存在的") self._temperature = value
Мы можем видеть выше, что метод get_temperature() и set_temperature() уже определены, кроме того, используется _temperature вместо temperature. Подчеркнутая (underscore) буква _ в начале означает, что это私有ная переменная в Python.
>>> c = Celsius(-277) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible >>> c = Celsius(37) >>> c.get_temperature() 37 >>> c.set_temperature(10) >>> c.set_temperature(-300) Traceback (most recent call last): ... ValueError: Temperature below -273 is not possible
Этот апдейт успешно实施了 новые ограничения. Мы больше не можем устанавливать температуру ниже -273.
Обратите внимание, что в Python не существует частных переменных. Достаточно просто следовать некоторым нормам. Язык сам по себе не имеет ограничений.
>>> c._temperature = -300 >>> c.get_temperature() -300
Но это не большая проблема. Самой большой проблемой этого обновления является то, что все клиенты, которые реализовали предыдущий класс в своей программе, должны изменить obj.temperature на obj.get_temperature() и все назначения (например, obj.temperature изменено на obj.set_temperature(val)).
Эта рефакторизация может доставить клиентам проблемы с несколькими сотнями тысяч строк кода.
В общем, наше новое обновление не совместимо с предыдущими версиями. Это место, где работает @property.
Метод обработки Python для этой проблемы - использование property. Мы можем реализовать это так.
class Celsius: def __init__(self, temperature = 0): self.temperature = temperature def to_fahrenheit(self): return (self.temperature * 1.8) + 32 def get_temperature(self): print("Полученная стоимость") return self._temperature def set_temperature(self, value): if value < -273: raise ValueError("Невозможно -273 градуса") print("Установленное значение") self._temperature = value temperature = property(get_temperature, set_temperature)
И как только это запустится, в shell будет отправлен следующий код.
>>> c = Celsius()
Мы добавили функцию print() в get temperature() и set temperature(), чтобы ясно видеть их выполнение.
Последняя строка кода создает объект свойства temperature. Кратко говоря, свойство добавляет некоторые коды (get_temperature и set_temperature) к доступу к члену свойства (temperature).
Любой код для извлечения значения температуры автоматически вызовет get_temperature() вместо поиска в словаре (__dict__). Точно так же, любой код для назначения значения температуры автоматически вызовет set_temperature(). Это одна из очень крутых функций Python.
Мы можем видеть, что при создании объекта также вызывается set_temperature().
Можешь угадать почему?
Причина в том, что при создании объекта вызывается метод __init__(). В этом методе self.temperature = temperature. Это автоматическое присвоение называется set_temperature().
>>> c.temperature Получение значения 0
Таким образом, любое обращение к c.temperature автоматически вызовет get_temperature(). Это и есть функция свойств. Вот несколько примеров.
>>> c.temperature = 37 Установка значения >>> c.to_fahrenheit() Получение значения 98.60000000000001
Используя свойства, мы можем видеть, что мы изменили класс и реализовали ограничение значений, не изменяя клиентский код. Таким образом, наше реализация является обратносоответствующей.
最后请注意,实际温度值存储在私有变量_temperature中。 Свойство temperature является объектом свойств, который предоставляет интерфейс к этому приватному переменной.
В Python функция property() является вbuilt-in функцией, предназначенной для создания и возврата объекта свойств. Сигнатура этой функции
property(fget=None, fset=None, fdel=None, doc=None)
В них fget - функция для получения значения свойства, fset - функция для установки значения свойства, fdel - функция для удаления свойства, doc - строка (например, комментарий). Из реализации видно, что эти функции являются опциональными. Поэтому можно просто создать объект свойств следующим образом.
>>> property() <объект свойств в 0x0000000003239B38>
Объект свойств имеет три метода, getter(), setter() и deleter(), которые используются для указания fget, fset и fdel позже. Это означает
temperature = property(get_temperature, set_temperature)
Это также можно разделить на
# Создание пустого свойства temperature = property() # Установка fget temperature = temperature.getter(get_temperature) # Установка fset temperature = temperature.setter(set_temperature)
Эти два кода эквивалентны.
ЗнакомствоДекораторы в PythonПрограммисты могут понять, что указанные структуры могут быть реализованы как декораторы.
Мы можем пойти дальше и не определять имена get_temperature, set_temperature, так как они не нужны и могут повлиять на пространство имен класса. Для этого мы повторно используем имя temperature при определении функций getter и setter. Это можно сделать.
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("Полученное значение") return self._temperature @temperature.setter def temperature(self, value): if value < -273: raise ValueError("Невозможно -273 градуса") print("Установленное значение") self._temperature = value
Указанный способ — это простой и рекомендованный способ создания свойств. В поисках свойств в Python вы, возможно, встретите такие типы конструкций.
Хорошо, сегодня все.