Using properties in python

Properties in Python

Some object-oriented languages such as Java and C# support private object attributes; which cannot be directly accessed from outside. Programmers often have to write getter and setter methods to access such private attributes.

However in Python, all the attributes and methods are public, so it is useless to write getters or setters.

If you want to prevent direct access to an attribute, you should define it as a property.

It is a simple way to customize access to an attribute.

Define a Property

Let’s see how properties can be defined in Python. Here is a simple example in which the Person class is defined.

This class has a single attribute named hidden_name which we do not want people to access directly. Hence, two methods are defined in the class – a getter called get_name() and a setter called set_name() .

class Person(): def __init__(self, value): self.hidden_name = value # getter function def get_name(self): print('Getting name:') return self.hidden_name # setter function def set_name(self, value): print('Setting name to', value) self.hidden_name = value # make a property name = property(get_name, set_name)

The get_name() and set_name() methods act like normal getter and setter until this line.

name = property(get_name, set_name)

It creates a new class attribute called ‘name’ and defines the two methods as properties.

Now when you refer to the name attribute of any Person object, Python actually calls the get_name() method.

p = Person('Bob') print(p.name) # Prints Getting name: Bob

When you assign a value to the name attribute, the set_name() method is called.

p.name = "Sam" # Prints Setting name to Sam print(p.name) # Prints Getting name: Sam

A great feature of a property is that it looks like a normal attribute, but when you access it, it automatically triggers the getter and setter methods.

property() Function

You generate a property by calling the property() built-in function, passing in three methods (getter, setter and deleter) as well as the docstring for the property.

The property() function has the following syntax:

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

This creates a class attribute called attrib and defines the three methods as properties.

Now, when you reference x.attrib , Python calls the fget method.

When you assign x.attrib = value , Python calls the fset method and passes value as an argument.

When you execute del x.attrib , Python calls the fdel method.

Python uses the argument you passed as doc as the docstring of the attribute.

Rewriting Person Class

Let’s rewrite our previous example, including the deleter method and docstring.

class Person(): def __init__(self, value): self.hidden_name = value # getter function def get_name(self): print('Getting name:') return self.hidden_name # setter function def set_name(self, value): print('Setting name to', value) self.hidden_name = value # deleter function def del_name(self): print('Deleting name') del self.hidden_name # make a property name = property(get_name, set_name, del_name, doc='name of the person')

Here is an example of the new class in use:

p = Person('Bob') # calls the getter print(p.name) # Prints Getting name: Bob # calls the setter p.name = 'Sam' # Prints Setting name to Sam # docstring print('Docstring:', Person.name.__doc__) # Prints Docstring: name of the person # calls the deleter del p.name # Prints Deleting name

@property – Property as a Decorator

A more elegant syntax to define properties in a class is to use property as a decorator

Читайте также:  Python строковое представление словаря

In the next example, we’ll define three different methods, each called name() but preceded by different decorators:

  • @property decorator goes before the getter method
  • @name.setter decorator goes before the setter method
  • @name.deleter decorator goes before the deleter method

Here’s how they actually look in the code:

class Person(): def __init__(self, value): self.hidden_name = value @property def name(self): print('Getting name:') return self.hidden_name @name.setter def name(self, value): print('Setting name to', value) self.hidden_name = value @name.deleter def name(self): print('Deleting name') del self.hidden_name

Here the first method is a getter, and establishes name as being a property. The other two methods attach setter and deleter to the name property.

You can still access name as if it were an attribute:

p = Person('Bob') # calls the getter print(p.name) # Prints Getting name: Bob # calls the setter p.name = 'Sam' # Prints Setting name to Sam # calls the deleter del p.name # Prints Deleting name

Please note that you cannot define @name.setter and @name.deleter decorators unless you already establish name as a property using @property decorator.

Real World Example

Properties are generally used in cases where you want to add extra processing (e.g., type checking or validation) to the getting or setting of an instance attribute.

For example, below code defines a property that adds simple type checking to an attribute:

class Person: def __init__(self, value): self.name = value @property def name(self): return self._name @name.setter def name(self, value): if not isinstance(value, str): raise TypeError('Expected a string') self._name = value @name.deleter def name(self): raise AttributeError("Can't delete attribute") p = Person(42) # Triggers TypeError: Expected a string p = Person('Bob') print(p.name) # Prints Bob p.name = 42 # Triggers TypeError: Expected a string del p.name # Triggers AttributeError: Can't delete attribute

1. Type checking is performed in the setter function. It checks whether the type of the assigned value is a string. If it is other than string, the TypeError exception is raised.

p.name = 42 # Triggers TypeError: Expected a string

2. AttributeError exception is raised when user tries to delete an attribute.

del p.name # Triggers AttributeError: Can't delete attribute

3. Type checking is performed during initialization as well.

p = Person(42) # Triggers TypeError: Expected a string 

The whole point of our example is to apply type checking when setting an attribute which means that checking should also be done during initialization.

Читайте также:  Java map code example

That’s why the __init__() method sets self.name instead of self._name . By setting self name, the __init__() method automatically calls the setter method.

Computed Attributes

Properties can also be a way to define computed attributes – attributes that are not actually stored, but are calculated dynamically on demand.

Let’s define a Rectangle class that has two normal attributes (width and height) and one computed attribute (area)

class Rectangle(object): def __init__(self, width, height): self.width = width self.height = height @property def area(self): return self.width * self.height

Let’s create a Rectangle object with an initial value for its width and height.

Now you can call the area as if it were an attribute:

Here’s the fun part: you can change the width and height of the rectangle at any time, and the area property will be computed accordingly:

r.width = 3 r.height = 6 print(r.area) # Prints 18

As you can see we have not specified setter property for an attribute, we can’t set it from the outside. This is handy for read-only attributes:

r.area = 18 # Triggers AttributeError: can't set attribute

Extending a Property in a Subclass

Because a property is not a single method but a collection of getter, setter, and deleter methods, extending it in a subclass introduces many problems.

You need to figure out whether you will redefine all of the methods together or just one of the methods.

Here is an example of a class that inherits from Person and extends the name property with new functionality:

class Person(): def __init__(self, value): self.hidden_name = value @property def name(self): print('Getting name:') return self.hidden_name @name.setter def name(self, value): print('Setting name to', value) self.hidden_name = value @name.deleter def name(self): print('Deleting name') del self.hidden_name class SubPerson(Person): @property def name(self): print('Inside subperson getter') return super().name @name.setter def name(self, value): print('Inside subperson setter') super(SubPerson, SubPerson).name.__set__(self, value) @name.deleter def name(self): print('Inside subperson deleter') super(SubPerson, SubPerson).name.__delete__(self)

Here is an example of the new class in use:

s = SubPerson('Bob') # calls the getter print(s.name) # Prints Inside subperson getter # Prints Getting name: Bob # calls the setter s.name = 'Sam' # Prints Inside subperson setter # Prints Setting name to Sam # calls the deleter del s.name # Prints Inside subperson deleter # Prints Deleting name

In above example, all of the property methods are redefined together. Within each method, super() function is used to call the superclass’s implementation.

If you only want to redefine one of the methods, it’s not enough to use @property by itself, use code such as the following:

class SubPerson(Person): @Person.name.getter def name(self): print('Inside subperson getter') return super().name

When you do this, only the getter method is replaced and the remaining methods of the property are copied.

Читайте также:  Сколько элементов в css

If you just want to redefine the setter, use this code:

class SubPerson(Person): @Person.name.setter def name(self, value): print('Inside subperson setter') super(SubPerson, SubPerson).name.__set__(self, value)

Источник

Функция property() в Python

Перевод материала подготовлен в рамках онлайн-курса «Python Developer. Basic«.

Приглашаем всех желающих на двухдневный онлайн-интенсив «Разработка десктоп-приложения с помощью библиотеки Tkinter». На интенсиве получим начальные навыки бэкенд-разработки на Python, а также начнем разработку десктоп-приложения с помощью библиотеки Tkinter. По итогам 2х дней сможем создать инвестиционное приложение для просмотра актуальной цены на необходимые акции или валюту. Присоединяйтесь!

Функция property() используется для определения свойств в классах.

Метод property() обеспечивает интерфейс для атрибутов экземпляра класса. Он инкапсулирует атрибуты экземпляров и предоставляет свойства, аналогично тому, как это работает в Java и C#.

Метод property() принимает на вход методы get , set и delete , и возвращает объекты класса property .

  1. fget : (опционально) Функция для получения значения атрибута. Значение по умолчанию None.
  2. fset : (опционально) Функция для задания значения атрибута. Значение по умолчанию None.
  3. fdel : (опционально) Функция для удаления значения атрибута. Значение по умолчанию None.
  4. doc : (опционально) Строка, содержащая документацию. Значение по умолчанию None.

Возврат значений:

Возвращает атрибут свойства из заданных геттера, сеттера и функции удаления.

Следующий пример показывает, как создать свойство в Python с помощью функции property() .

class person: def __init__(self): self.__name='' def setname(self, name): print('setname() called') self.__name=name def getname(self): print('getname() called') return self.__name name=property(getname, setname)

В примере выше property(getname, setname) возвращает объект свойства и присваивает ему имя. Таким образом, свойство name скрывает приватный экземпляр __name . Доступ к свойству name осуществляется напрямую, но внутри вызывается метод getname() или setname() , как показано ниже.

>>> from person import person >>> p1=person() >>> p1.name="Steve" setname() called >>> p1.name getname() called 'Steve'

Как видно выше, метод getname() вызывается автоматически, когда мы обращаемся к свойству name . Аналогично метод setname вызывается, когда мы присваиваем значение свойству name . Он скрывает атрибут класса __name .

Аналогично можно написать метод удаления свойства, как в коде ниже.

class person: def __init__(self, name): self.__name=name def setname(self, name): print('setname() called') self.__name=name def getname(self): print('getname() called') return self.__name def delname(self): print('delname() called') del self.__name # Set property to use get_name, set_name # and del_name methods name=property(getname, setname, delname)

Функция delname() будет вызвана, когда вы удалите свойство name .

>>> from person import person >>> p1=person() >>> p1.name="Steve" setname() called >>> del p1.name delname() called

Таким образом, мы можем определить свойство в классе с помощью функции property() в Python.

Декоратор @property упрощает объявление свойства и позволяет сделать это не вызывая функцию property() .

Узнать подробнее о курсе «Python Developer. Basic«

Регистрация на двухдневный онлайн-интенсив «Разработка десктоп-приложения с помощью библиотеки Tkinter»: День 1, День 2.

Источник

Оцените статью