欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Python-通过属性描述符管理实例属性

程序员文章站 2022-04-27 16:57:50
上一篇博客中,我们通过@property装饰器来管理实例的属性,这使得我们不仅可以通过obj.attr的方式来访问,并且可以设置属性的存储规则。并且由于@property和@attr.setter装饰器的存在,我们也很容易能够找到处理业务逻辑相关的函数。但当大量的属性都需要相同的存储逻辑来进行控制时,使用@property装饰器仍然无法避免代码的重复。例如:学生类的age和grades属性都必须是整数并且大于0.下面通过属性描述符的形式来管理实例属性,看看有什么区别class IntField(obje...

上一篇博客中,我们通过@property装饰器来管理实例的属性,这使得我们不仅可以通过obj.attr的方式来访问,并且可以设置属性的存储规则。并且由于@property和@attr.setter装饰器的存在,我们也很容易能够找到处理业务逻辑相关的函数。但当大量的属性都需要相同的存储逻辑来进行控制时,使用@property装饰器仍然无法避免代码的重复。例如:学生类的age和grades属性都必须是整数并且大于0.
下面通过属性描述符的形式来管理实例属性,看看有什么区别

class IntField(object):

    def __init__(self,attribute):
        self.attribute = attribute

    def __get__(self, instance, owner):
        print("get:" + str(instance) + "的" + str(self.attribute),owner)
        return instance.__dict__[self.attribute]

    def __set__(self, instance, value):
        print("set:" + str(instance) + "的" + str(self.attribute))
        if isinstance(value,int) and value >=0:
            instance.__dict__[self.attribute] = value
            self.values = value
        else:
            raise ValueError


class Student(object):

    age = IntField("age")
    grades = IntField("grades")

    def __init__(self,name,age,grades):
        self.name = name
        self.age =age # 调用age.setter方法
        self.grades =grades

    def graduate(self):
        if self.grades >= 60:
            print("允许毕业")
        else:
            print("留级查看")

s = Student("jack",22,88)
s2 = Student("tom",25,77)
print(s.age)
print(s2.age)
print(s.__dict__)
print(s2.__dict__)
set:<__main__.Student object at 0x000000000220C4E0>的age
set:<__main__.Student object at 0x000000000220C4E0>的grades
set:<__main__.Student object at 0x000000000281A198>的age
set:<__main__.Student object at 0x000000000281A198>的grades
get:<__main__.Student object at 0x000000000220C4E0>的age <class '__main__.Student'>
22
get:<__main__.Student object at 0x000000000281A198>的age <class '__main__.Student'>
25
{'name': 'jack', 'age': 22, 'grades': 88}
{'name': 'tom', 'age': 25, 'grades': 77}

在分析上面一个 例子前我们先了解关于属性描述符的概念

  • 描述符类:实现了描述符协议的类。只要实现__get__,__set__和__delete__3个方法中的任意一个,这个类就是描述符。如上面例子中的IntField类。它能实现对多个属性运用相同存取逻辑的一种方式。通俗来说就是:创建一个实例,作为另一个类的类属性
  • 托管类:将描述符类的实例作为类属性的类,如上面例子中的Student类,
  • 数据描述符(data descriptor):如果一个对象同时定义了__get__和__set__方法 ,它被称做数据描述符
  • 非数据描述符(non-data descriptor):只定义__get__方法的对象则被称之为非数据描述符

分析:

  • 当我们通过s.age这种方式来访问实例属性的时候,实际上是调用描述符实例的__get__方法进行访问的
  • 当我们通过s.age = age 来给实例属性赋值的时候,实际上是调用的描述符实例的__set__方法,并且在__set__方法中,传入了实例对象instance和该对象所属的类owner。在__set__方法中来定义属性的存储规则,然后通过instance.__dict__[self.attribute] = value这种形式来进行实例属性的绑定。这样就保证了实例s和s2的相互独立,互不影响。
  • 如果我们需要给Student类添加一个新的属性学分credit,学分为整数,且大于0。存储规则同age和grades相同。这样我们只需要在Student类中添加一个类属性credit = IntField(“credit”)。这样比@property装饰器要简洁许多。

本文地址:https://blog.csdn.net/weixin_43989215/article/details/107303683