属性、property和属性描述符

1、实例属性和类属性的区别

在 Python 中,类属性是定义在类级别上的变量或常量,它们是所有该类实例共享的值。而实例属性是定义在实例级别上的变量或常量,每个实例都有其自己的值。

 区别主要在于:
  1. 值的存储位置:类属性存储在类的命名空间中,而实例属性存储在实例的命名空间中。
  2. 访问方式:类属性可以通过类名或实例访问,但实例属性只能通过实例访问。
  3. 继承:子类会继承父类的类属性,但不会继承父类的实例属性。

示例:

class MyClass:
    class_attribute = 'class_value'

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

my_instance_1 = MyClass('instance_value_1')
my_instance_2 = MyClass('instance_value_2')

print(MyClass.class_attribute)  # 输出:'class_value'
print(my_instance_1.class_attribute)  # 输出:'class_value'
print(my_instance_1.instance_attribute)  # 输出:'instance_value_1'
print(my_instance_2.instance_attribute)  # 输出:'instance_value_2'

在上述代码中,class_attribute 是一个类属性,被 MyClass 的所有实例共享。而 instance_attribute 则是一个实例属性,每个实例都有一份自己的值。

 

2、property应用场景

  1. 计算属性:有时候我们需要根据对象的状态计算出某个属性的值,这时候可以使用 property 将一个方法转换成一个只读属性,以便在访问这个属性时动态计算出其值。

  2. 数据校验:当我们想要限制对象的某个属性的取值范围或格式时,可以使用属性的 setter 方法对输入数据进行校验,并确保它们满足要求。

  3. 防止意外修改:有时候我们希望某些属性只能被读取,而不能被修改,可以使用 property 将其设置为只读属性。

  4. 封装内部实现细节:属性还可以用来隐藏对象内部的实现细节,从而提高代码的安全性和可维护性。

3、属性描述符和proprety的区别

属性描述符和property都是Python中用于定义类属性的机制,但它们的实现方式不同。

属性描述符是一个类,该类定义了三个方法:__get__()__set__()__delete__()。这些方法允许您控制属性的访问、修改和删除。属性描述符可以与任何类属性一起使用,并且一个属性描述符可以被多个类属性共享。当一个属性描述符被赋值给一个类属性时,它会替换该属性的默认行为。例如,在一个类中定义一个属性描述符用于限制属性的取值范围:

 
class Range:
    def __init__(self, min_value, max_value):
        self.min_value = min_value
        self.max_value = max_value

    def __get__(self, instance, owner):
        return instance.__dict__[self.name]

    def __set__(self, instance, value):
        if value < self.min_value or value > self.max_value:
            raise ValueError("Value out of range")
        instance.__dict__[self.name] = value

    def __set_name__(self, owner, name):
        self.name = name

class MyClass:
    x = Range(0, 10)

my_obj = MyClass()
my_obj.x = 5
print(my_obj.x)
my_obj.x = 15 # Raises ValueError: Value out of range

在这个例子中,属性描述符Range控制了属性x的取值范围。

 

另一方面,property是一个内置函数,它提供了一种简化属性访问的方式。与属性描述符不同,property不是一个类,而是一个包装器函数,它接受三个可选参数:fgetfsetfdel。这些参数分别指定获取、设置和删除属性时所调用的方法。如果只需要定义读取属性的方法,则可以省略fsetfdel。例如:

 
class MyClass:
    def __init__(self, x):
        self._x = x

    @property
    def x(self):
        print("Getting x")
        return self._x

    @x.setter
    def x(self, value):
        print("Setting x")
        self._x = value

my_obj = MyClass(5)
print(my_obj.x) # Calls getter method
my_obj.x = 10 # Calls setter method

在这个例子中,property包装器定义了一个名为x的属性,使用@property修饰器标记getter方法,使用@x.setter修饰器标记setter方法。这样就可以像访问普通属性一样来访问和修改x属性。当访问x属性时,会自动调用getter方法;当修改x属性时,会自动调用setter方法。

总的来说,属性描述符提供了更灵活的属性控制机制,而property则提供了更简单、更易于使用的语法糖。

 

 4、属性描述符的触发

描述符是一种可以自定义属性访问的方式,它能够通过属性访问来拦截对一个对象属性的读取操作、赋值操作或删除操作,从而实现自定义逻辑。要想识别到属性调用,需要在描述符类中实现__get____set____delete__这三个方法中的至少一个。

 其中,__get__(self, instance, owner)方法会在获取属性值时被调用,它接收两个参数:instance表示实例对象,owner表示定义该属性的类,如果该属性是通过类直接访问,那么instance为None;如果该属性是通过实例访问,那么instance为实例本身。__set__(self, instance, value)方法会在给属性赋值时被调用,它接收三个参数:instance表示实例对象,value表示要赋的值。__delete__(self, instance)方法会在删除属性时被调用,它接收两个参数:instance表示实例对象。

当使用描述符时,只要将其作为一个类的属性,那么就能够在该属性被访问、赋值或删除时自动触发相应的方法。例如,下面是一个简单的描述符类,它能够记录属性访问的次数:

class CountAccess:
    def __init__(self, name):
        self.name = name
        self.count = 0

    def __get__(self, instance, owner):
        self.count += 1
        return getattr(instance, self.name)

    def __set__(self, instance, value):
        self.count += 1
        setattr(instance, self.name, value)

    def __delete__(self, instance):
        self.count += 1
        delattr(instance, self.name)

class MyClass:
    x = CountAccess('x')

当通过实例访问x属性时,就会自动触发CountAccess中相应的方法,例如:

obj = MyClass()
obj.x = 10  # 触发 CountAccess.__set__ 方法,记录访问次数并设置 obj 的 x 属性值为 10
print(obj.x)  # 触发 CountAccess.__get__ 方法,记录访问次数并获取 obj 的 x 属性值
del obj.x  # 触发 CountAccess.__delete__ 方法,记录访问次数并删除 obj 的 x 属性

 

 
 
 

 

posted @ 2023-06-11 13:49  JvvYou  阅读(32)  评论(0编辑  收藏  举报