首页 > 编程语言 >Python - Creating Managed Attributes using properties

Python - Creating Managed Attributes using properties

时间:2024-07-30 19:10:00浏览次数:13  
标签:Managed Creating Python age self will variable method name

Creating Managed Attributes using properties

Properties can be used to create data attributes with special functionality. If you want some extra functionality (like type checking, data validation or transformation) while getting or setting a data attribute, you can define a property which creates a managed attribute. The user can access and modify this managed attribute with regular syntax (e.g. print(MyClass.x) or MyClass.x = 3), but behind the scene some method will be automatically executed while setting or getting the attribute. Property allows us to access data like a variable, but the accessing is handled internally by methods. This way, we can control attribute access by attaching custom behavior.

Suppose we have developed this class Person, with two instance variables name and age, and the method display.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    def display(self):
        print(self.name, self.age)


if __name__ == '__main__':
    p = Person('Raj', 30)
    p.display()

Let us assume that this is a big class that is being used by many clients. After some time, we as the implementors of the class want to restrain the value of age. We want to ensure that whenever age is assigned a value, that value should be within the range 20 - 80.

A solution to this could be to make age a private variable and use getter and setter methods to access and update this private variable. Setters (also know as mutators) and getters (also know as accessors) are generally used in object-oriented languages to restrict access to private variables and they allow you to control how these variables are accessed and updated.

We modify the class and make age a private variable by prefixing it with an underscore, so now client is not supposed to access it directly. We define a method set_age that will be used to assign a value to the private variable _age, and we define another method get_age that will be used to access the value of variable _age. In the set_age method we can put the validation code.

class Person:
    def __init__(self, name, age):
        self.name = name
        self._age = age

    def display(self):
        print(self.name, self._age)

    def set_age(self, new_age):
        if 20 <= new_age <= 80:
            self._age = new_age
        else:
            raise ValueError('Age must be between 20 and 80')

    def get_age(self):
        return self._age


if __name__ == '__main__':
    p = Person('Raj', 30)
    p.display()

Now, whenever the user wants to change the age, he will do it through the set_age method, and the data validation will be done.

>>> p.set_age(100)

ValueError: Age must be between 20 and 80

>>> p.set_age(12)

ValueError: Age must be between 20 and 80

>>> p.set_age(25)

>>> p.display()

Raj 25

So, by defining the setter and getter methods, we could successfully implement the new restriction on age.

Earlier when there was no restriction, and age was a public variable, if the user had to increase the current age by 1, he would simply write:

p.age +=1

Now in the modified class, we have setter and getter methods so to increase the value of age, user has to write this:

p.set_age(p.get_age() + 1)

These types of expressions are confusing and decrease readability. There is still a problem in our modified class. When the user creates a new object, he can send any value for the age because there is no data validation done in the initializer.

p1 = Person('Dev', 2000)

So, we need to perform the data validation in the initializer also by calling the set_age method.

    def __init__(self, name, age):
         self.name = name
         self.set_age(age)

Now the data validation will be done at the time of creation of a new object also. It seems that we have solved the problem of restricting the value of age. Now users of our class will not be able to enter any value of age outside the range 20-80. But remember our Person class is being used by several clients, and there is lot of existing code that accesses age directly, for example p.age = 30 or print(p.age). The new changes in your class will break this client code and it will have to be rewritten with statements like p.set_age(30) and print(p.get_age(). You have changed the user interface and so your new update is not backward compatible. This refactoring can cause problems in your client code.

To avoid this problem, in other object-oriented languages, programmers would start their class design with private attributes along with getters and setters that do nothing except getting and setting the value of the private variable. These setters and getters do not perform any extra processing and they are not needed at the outset but they have to be added because they might be needed later, when you need some processing to be done while setting and getting an attribute. This design makes sure that if in future you have to add any data validation, then the existing client code will not break. The clients will already be accessing data through setters and getters, so you can change the implementation without changing the interface and breaking your client’s code.

The getter and setter methods can also be used to make an attribute read only or write only. If you define only the getter method for a private variable and don’t define the setter method for it then the variable becomes read only, users will be able to read that variable but cannot update it. As we have seen, setters and getters also allow data validation, i.e., the setter method can control what value can be assigned to the variable and getter method can change the way the variable is represented when it is accessed. In most other languages, getter are setter methods are common and they are used to protect and validate your private data.

This setter and getter methods approach is not preferred in Python, the Pythonic way of going about this whole thing would be to create a property. Properties allow us to write our class in a way that does not require the user of the class to call setter and getter methods.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, new_age):
        if 20 <= new_age <= 80:
            self._age = new_age
        else:
            raise ValueError('Age must be between 20 and 80')

    def display(self):
        print(self.name, self._age)

We have added two special methods, and both are named age. Before the header line of these methods, we have added a line starting with ‘@’ symbol. The line @property makes the first method a getter method, and the line @age.setter makes the second method a setter method.

Now after this modification, the name age has become a property, we can access it like we access an instance variable. There is no need to call it like a method by using parentheses. The actual value of age is stored in the private variable named _age. The age attribute is a property which provides an interface to this private variable. The name of the property should be different from the attribute where we store our data.

Whenever we reference the attribute named age, the method with the line @property will be executed and whenever we assign something to it, the method with the line @age.setter will be executed. The method with @property is the getter method and the method with @age.setter is the setter method for the property.

 

The user of the class can now access age as if it were an instance variable.

>>> p = Person('Raj', 30)

>>> p.age + 1

31

>>> p.age = 40

>>> p.age = 200

ValueError: Age must be between 20 and 80

>>> p.age()

TypeError: 'int' object is not callable

 

Creating read only attributes using properties

Another use of property is that you can make an attribute read-only or write-only. If you provide only the getter method, not the setter method, the property becomes a read only property. This way we can protect our private attribute from any sort of modification by the client, while still giving the access to read the value of the attribute.

 

Creating Computed attributes using properties

A common use of property is to create dynamically computed attributes, the values of these attributes are not actually stored, they are computed on request.

class Rectangle():
    def __init__(self, length, breadth):
       self.length = length
       self.breadth = breadth
       self.diagonal = (self.length * self.length + self.breadth * self.breadth) ** 0.5 

    def area(self):
        return self.length * self.breadth

    def perimeter(self):
        return 2 * (self.length + self.breadth)

In this Rectangle class we have three instance variables, length, breadth and diagonal, and two methods area and perimeter. The value of instance variable diagonal is computed from the values of instance variables length and breadth.

>>> r = Rectangle(2, 5)

>>> r.diagonal

5.385164807134504

>>> r.area()

10

>>> r.perimeter()

14

Now let us change length:

>>> r.length = 10

>>> r.diagonal

5.385164807134504

We changed length, but value of diagonal has not changed.

>>> r.area()

50

>>> r.perimeter()

30

Area and perimeter have changed because they are implemented as methods.

So, if you change the value of an instance variable, any other instance variable that is computed from it will not automatically update. Here in this class if we change length or breadth, then diagonal will not change accordingly. One solution could be to implement diagonal as a method. But then we will not be able to access it as an instance variable; whenever we want to access it, we have to put parentheses. This will also break any client code that has used diagonal as an instance variable. The solution is to turn it into a property.

    @property
    def diagonal(self):
        return (self.length * self.length + self.breadth * self.breadth) ** 0.5 

Now we can continue to access diagonal as an instance variable; whenever we will access diagonal, its value will be calculated and we will get the updated value. So, changes in length and breadth will be reflected in the diagonal.

>>> r = Rectangle(2, 5)

>>> r.diagonal

5.385164807134504

>>> r.length = 10

>>> r.diagonal

11.180339887498949

There is no need to define the setter method, because we do not expect the user to change the diagonal.

 

Deleter method of property

We can also define a deleter method for the property, this deleter method defines what happens when a property is deleted. To create the deleter method, you have to define a method with the same name as the property and add the decorator with the word deleter in it.

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age

    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, new_age):
        if 20 <= new_age <= 80:
            self._age = new_age
        else:
            raise ValueError('Age must be between 20 and 80')

    @age.deleter
    def age(self):
        del self._age
        print('age deleted')

    def display(self):
        print(self.name, self._age)

The deleter method will be executed, when the attribute is deleted.

>>> p = Person('Jill', 25)

>>> print(p.age)

25

>>> del p.age

age deleted

 

标签:Managed,Creating,Python,age,self,will,variable,method,name
From: https://www.cnblogs.com/zhangzhihui/p/18333173

相关文章

  • Python - Static Methods
    Sometimeswehavetowritemethodsthatarerelatedtotheclassbutdonotneedanyaccesstoinstanceorclassdataforperformingtheirwork.Thesemethodscouldbesomehelperorutilitymethodsthatareusedinsidetheclassbuttheycanperformthei......
  • python中列表的学习
    列表目录列表列表的定义创建列表列表的索引列表的切片内置函数列表的遍历列表的常用方法(变量.方法名())列表的定义List(列表)是Python中使用最频繁的数据类型,在其他语言中通常叫做数组专门用于存储一串信息列表用[]定义,数据之间使用﹐分隔列表的索引从О开始索引就是数据在列......
  • Python 字节串转Hex字符串(一个久远的问题点总结)
    时间:2024.07.30作者:Yuan  这是一个今天看来似乎有点久远的问题,但是值得被记录和澄清一下!  那是在2022年1月份参与的一个项目中遇到的问题,大概需求是利用SHT40-AD1B-R2芯片,读取环境温度。其实就是通过i2c与这个温度传感器建立通讯,然后读取温湿度信息,对于上位机的......
  • [附开题]flask框架的汽车零件维修管理信息平台t6rr1(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着汽车工业的快速发展和汽车保有量的持续增长,汽车零件维修管理已成为汽车后市场的重要组成部分。传统的手工记录和管理方式已难以满足现......
  • [附开题]flask框架的汽车售后管理系统2888o(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着汽车产业的快速发展和消费者购车需求的日益增长,汽车售后服务已成为车企和经销商提升客户满意度、增强品牌忠诚度的重要环节。然而,传统......
  • [附开题]flask框架的社区服务管理系统0f6i9(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着城市化进程的深入和居民生活水平的提高,社区服务需求日益多样化与复杂化。传统的社区服务模式已难以满足居民对于高效、便捷、个性化服......
  • [附开题]flask框架的社区服务系统ff00q(python+源码)
    本系统(程序+源码+数据库+调试部署+开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。系统程序文件列表开题报告内容研究背景随着社会的快速发展和居民生活水平的提高,社区作为城市的基本单元,其服务功能日益丰富和多样化。然而,传统社区服务方式在应对日益增长的居民......
  • Python基础知识笔记---保留字
    保留字,也称关键字,是指被编程语言内部定义并保留使用的标识符。一、保留字概览  二、保留字用途 1.`False`:表示布尔值假。2.`None`:表示空值或无值。3.`True`:表示布尔值真。4.`not`:布尔逻辑操作符,对条件表达式取反。5.`or`:布尔逻辑操作符,用于连接两个条件表达式......
  • 用Python写一个植物大战僵尸
    导语:哈喽,哈喽~植物大战僵尸的人气可谓是经久不衰,晃着脑袋生产阳光的向日葵,突突突⚾⚾⚾吐着子弹的豌豆射手!​行动迟缓种类丰富的僵尸……印象最深的是“僵尸吃掉了你的脑子!”还有疯狂的戴夫,无一不唤醒着我们的童年记忆​。下面用python还原你的记忆中的童年!功能实现如下:......
  • 基于Python实现的深度学习技术在水文水质领域应用
    当前,深度学习作为人工智能的热门技术发展迅速,以其强大的非线性和不确定性处理能力在图像识别、语音识别、自然语言处理等领域取得了显著的成效。它是一种端到端的处理方法,在训练算法的指导下,深层神经网络自发地从原始数据中进行抽象,提炼关键特征,对结果做出预报,中间过程不需要人......