English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Основной учебник Python

Контроль потоков Python

Функции в Python

Типы данных в Python

Файловые операции Python

Объекты и классы Python

Даты и время Python

Продвинутые знания Python

Python Референсная книга

@property в Python

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 (апгрейд существующего класса).

Использование getter и setter

Одной из очевидных методик решения этих ограничений является скрытие атрибута 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.

Сила @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 является объектом свойств, который предоставляет интерфейс к этому приватному переменной.

Подробнее о property

В 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 вы, возможно, встретите такие типы конструкций.

Хорошо, сегодня все.