Свойства Python

Краткое описание: в этом уроке вы узнаете о классе Python property и о том, как использовать его для определения свойств класса.

Введение в свойства класса

Ниже определен класс Person, который имеет два атрибута name и age, и создан новый экземпляр класса Person:

class Person: def __init__(self, name, age): self.name = name self.age = age john = Person('John', 18)
Code language: Python (python)

Поскольку age является атрибутом экземпляра класса Person, вы можете присвоить ему новое значение следующим образом:

john.age = 19
Code language: Python (python)

Следующее присвоение также технически допустимо:

john.age = -1
Code language: Python (python)

Однако возраст семантически неверен.

Чтобы убедиться, что возраст не равен нулю или отрицателен, вы используете оператор if, чтобы добавить проверку следующим образом:

age = -1 if age <= 0: raise ValueError('The age must be positive') else: john.age = age
Code language: Python (python)

И это нужно делать каждый раз, когда вы хотите присвоить значение атрибуту age. Это повторяется и затрудняет поддержку.

Чтобы избежать этого повторения, вы можете определить пару методов, называемых getter и setter.

Getter и setter

Методы getter и setter предоставляют интерфейс для доступа к атрибуту экземпляра:

  • getter возвращает значение атрибута.
  • Метод setter устанавливает новое значение атрибута.

В нашем примере вы можете сделать атрибут age приватным (по соглашению) и определить getter и setter для работы с атрибутом age.

Ниже показан новый класс Person с геттером и сеттером для атрибута age:

class Person: def __init__(self, name, age): self.name = name self.set_age(age) def set_age(self, age): if age <= 0: raise ValueError('The age must be positive') self._age = age def get_age(self): return self._age
Code language: Python (python)

Как это работает.

В классе Person set_age() является задатчиком, а get_age() — получателем. По соглашению геттер и сеттер имеют следующие имена: get_<attribute>() и set_<attribute>().

В методе set_age() мы вызываем ошибку ValueError, если age меньше или равен нулю. В противном случае мы присваиваем аргумент age атрибуту _age:

def set_age(self, age): if age <= 0: raise ValueError('The age must be positive') self._age = age
Code language: Python (python)

Метод get_age() возвращает значение атрибута _age:

def get_age(self): return self._age
Code language: Python (python)

В методе __init__() мы вызываем метод-установщик set_age() для инициализации атрибута _age:

def __init__(self, name, age): self.name = name self.set_age(age)
Code language: Python (python)

Ниже приведена попытка присвоить недопустимое значение атрибуту age:

john = Person('John', 18) john.set_age(-19)
Code language: Python (python)

И Python выдает ValueError, как и ожидалось.

ValueError: The age must be positive
Code language: Python (python)

Этот код работает просто отлично. Но у него есть проблема обратной совместимости.

Предположим, вы выпустили класс Person в течение некоторого времени, и другие разработчики уже используют его. А теперь вы добавляете геттер и сеттер, и весь код, использующий Person, больше не будет работать.

Чтобы определить метод getter и setter и при этом добиться обратной совместимости, вы можете использовать класс property().

Класс свойств Python

Класс property возвращает объект property. Класс property() имеет следующий синтаксис:

property(fget=None, fset=None, fdel=None, doc=None)
Code language: Python (python)

property() имеет следующие параметры:

  • fget — функция для получения значения атрибута, или метод getter.
  • fset — функция для установки значения атрибута, или метод setter.
  • fdel — функция для удаления атрибута.
  • doc — это doc-строка, т.е. комментарий.

Ниже используется функция property() для определения свойства age для класса Person.

class Person: def __init__(self, name, age): self.name = name self.age = age def set_age(self, age): if age <= 0: raise ValueError('The age must be positive') self._age = age def get_age(self): return self._age age = property(fget=get_age, fset=set_age)
Code language: Python (python)

В классе Person мы создаем новый объект свойства, вызывая функцию property(), и присваиваем объект свойства атрибуту age. Обратите внимание, что age является атрибутом класса, а не атрибутом экземпляра.

Ниже показано, что Person.age является объектом property:

print(Person.age)
Code language: Python (python)

Вывод:

<property object at 0x000001F5F5149180>
Code language: Python (python)

Ниже создается новый экземпляр класса Person и доступ к атрибуту age:

john = Person('John', 18)
Code language: Python (python)

В john.__dict__ хранятся атрибуты экземпляра объекта john. Ниже показано содержимое john.__dict__ :

print(john.__dict__)
Code language: Python (python)

Вывод:

{'_age': 18, 'name': 'John'}
Code language: Python (python)

Как видно из вывода, john.__dict__ не имеет атрибута age.

Следующее действие присваивает значение атрибуту age объекта john:

john.age = 19
Code language: Python (python)

В этом случае Python сначала ищет атрибут age в john.__dict__. Поскольку Python не находит атрибут age в john.__dict__, он затем найдет атрибут age в Person.__dict__.

В Person.__dict__ хранятся атрибуты класса Person. Ниже показано содержимое Person.__dict__:

pprint(Person.__dict__)
Code language: Python (python)

Вывод:

mappingproxy({'__dict__': <attribute '__dict__' of 'Person' objects>, '__doc__': None, '__init__': <function Person.__init__ at 0x000002242F5B2670>, '__module__': '__main__', '__weakref__': <attribute '__weakref__' of 'Person' objects>, 'age': <property object at 0x000002242EE39180>, 'get_age': <function Person.get_age at 0x000002242F5B2790>, 'set_age': <function Person.set_age at 0x000002242F5B2700>})
Code language: Python (python)

Поскольку Python находит атрибут age в Person.__dict__, он вызывает объект свойства age.

Когда вы присваиваете значение объекту age:

john.age = 19
Code language: Python (python)

Python вызовет функцию, назначенную аргументу fset, которая является set_age().

Аналогично, при чтении из объекта свойства age Python выполнит функцию, назначенную аргументу fget, который является методом get_age().

Используя класс property(), мы можем добавить свойство в класс, сохраняя при этом обратную совместимость. На практике вы сначала определите атрибуты. Позже, при необходимости, вы можете добавить свойство в класс.

Складываем все вместе.

from pprint import pprint class Person: def __init__(self, name, age): self.name = name self.age = age def set_age(self, age): if age <= 0: raise ValueError('The age must be positive') self._age = age def get_age(self): return self._age age = property(fget=get_age, fset=set_age) print(Person.age) john = Person('John', 18) pprint(john.__dict__) john.age = 19 pprint(Person.__dict__)
Code language: Python (python)

Резюме

  • Используйте класс Python property() для определения свойства класса.

Оцените статью
devanswers.ru
Добавить комментарий