这篇教程python的描述器descriptor详解写得很实用,希望能帮到您。
基本说明Python的描述器(descriptor)是一种Python对象,可以通过定义一组特定的方法来管理另一个对象的访问。描述器可以用于控制属性的读取、写入和删除等操作,同时还可以用于实现计算属性、类属性、属性别名等高级功能。 在Python中,描述器是通过实现__get__() 、__set__() 和__delete__() 方法的对象来定义的。当一个描述器被绑定到一个类的属性上时,Python会自动将其转化为描述器对象,并在访问该属性时调用对应的描述器方法。 class Descriptor: def __get__(self, instance, owner): print("Getting the value") return self.value def __set__(self, instance, value): print("Setting the value") self.value = valueclass MyClass: attr = Descriptor()obj = MyClass()obj.attr = 42print(obj.attr) 描述器是一种强大的Python语言特性,可以用于实现各种高级功能,例如: - 计算属性:描述器可以根据其他属性的值动态计算出一个属性的值,而不是存储属性的值。这可以帮助我们简化代码,并且可以在不改变接口的情况下改变属性的实现方式。
- 类属性:描述器可以让我们将属性绑定到类上,而不是绑定到实例上。这可以让我们在所有实例之间共享属性值,并且可以在运行时动态更改属性的值。
- 属性别名:描述器可以让我们定义一个属性的别名,让一个属性具有多个名称。这可以帮助我们简化代码,并且可以在不改变接口的情况下更改属性的名称。
- 数据验证:描述器可以让我们在设置属性值之前验证输入数据,确保它们符合我们的预期格式和类型。这可以提高代码的健壮性,并且可以帮助我们避免一些常见的错误。
示例Demo1假设我们有一个Temperature 类,用于表示温度。该类有一个名为celsius 的属性,表示摄氏温度。我们希望实现以下功能: - 计算属性
fahrenheit ,表示华氏温度,它应该是一个只读属性,可以通过摄氏温度自动计算得出。 - 限制
celsius 属性的取值范围在-273.15℃到1000℃之间,如果尝试设置超出此范围的值,应该引发ValueError 异常。
下面是使用描述器实现以上功能的示例代码: class Celsius: def __init__(self, value=0.0): self._value = value def __get__(self, instance, owner): return self._value def __set__(self, instance, value): if value < -273.15 or value > 1000.0: raise ValueError("Temperature out of range") self._value = valueclass Temperature: celsius = Celsius() @property def fahrenheit(self): return self.celsius * 1.8 + 32 在这个示例中,我们定义了一个Celsius 类,它是一个描述器,用于限制Temperature 类的celsius 属性的取值范围。在Celsius 类中,我们实现了__get__() 和__set__() 方法,分别在读取和设置celsius 属性时被调用。在__set__() 方法中,我们检查输入的值是否在允许的范围内,如果不是,则引发一个异常。 然后,我们定义了一个Temperature 类,它有一个celsius 属性,它被绑定到Celsius 类的实例上。我们还定义了一个只读属性fahrenheit ,它可以通过celsius 属性自动计算得出。 现在,我们可以创建一个Temperature 对象,并设置其celsius 属性,如下所示: t = Temperature()t.celsius = 25.0print(t.celsius) # 输出 25.0print(t.fahrenheit) # 输出 77.0 在这个示例中,我们创建了一个Temperature 对象,并将其celsius 属性设置为25.0。然后,我们打印了celsius 和fahrenheit 属性的值,它们分别是25.0和77.0,符合我们预期的结果。 如果我们尝试设置一个超出允许范围的值,例如-300.0,会引发一个异常,如下所示: t.celsius = -300.0 # 引发 ValueError: Temperature out of range
示例Demo2下面是一个示例,演示如何使用描述器将属性绑定到类上。 假设我们有一个名为Counter 的类,它用于计数器操作,可以用于记录创建的对象数或者其他类级别的计数。 我们可以使用描述器将计数器属性绑定到Counter 类上,如下所示: class Counter: _count = 0 class CountDescriptor: def __get__(self, instance, owner): return owner._count def __set__(self, instance, value): owner = type(instance) owner._count = value count = CountDescriptor() def __init__(self): type(self)._count += 1 在这个示例中,我们定义了一个Counter 类,它有一个名为count 的属性,它被绑定到了一个内部的CountDescriptor 描述器类上。 在CountDescriptor 类中,我们实现了__get__() 和__set__() 方法,它们分别在读取和设置count 属性时被调用。在__get__() 方法中,我们返回Counter 类的_count 属性的值。在__set__() 方法中,我们通过获取实例的类型(即Counter 类),并设置其_count 属性的值来设置计数器的值。 然后,在Counter 类的__init__() 方法中,我们在创建对象时自动增加计数器的值。 现在,我们可以创建多个Counter 对象,并访问它们的count 属性,如下所示: c1 = Counter()c2 = Counter()print(c1.count) # 输出 2print(c2.count) # 输出 2Counter.count = 100print(c1.count) # 输出 100print(c2.count) # 输出 100 在这个示例中,我们创建了两个Counter 对象,并分别将它们存储在c1 和c2 变量中。然后,我们打印它们的count 属性,它们的值都是2,表示我们已经创建了两个Counter 对象。 接着,我们将Counter 类的count 属性设置为100,这将改变所有对象的计数器值。然后,我们再次打印c1 和c2 的count 属性,它们的值都变成了100,说明我们成功地将属性绑定到了类上,实现了所有实例之间共享属性值的效果。
示例Demo3下面是一个示例,演示如何使用描述器实现属性别名。 假设我们有一个Person 类,它有一个名为name 的属性,表示人的名字。现在我们想要为name 属性定义一个别名,叫做full_name ,以便在某些情况下,我们可以使用full_name 属性来代替name 属性。 我们可以使用描述器来实现这个功能,如下所示: class NameAlias: def __init__(self, name): self.name = name def __get__(self, instance, owner): return getattr(instance, self.name) def __set__(self, instance, value): setattr(instance, self.name, value)class Person: def __init__(self, name): self.name = name full_name = NameAlias("name") 在这个示例中,我们定义了一个NameAlias 描述器类,它用于实现属性别名功能。在NameAlias 类中,我们实现了__get__() 和__set__() 方法,它们分别在读取和设置full_name 属性时被调用。在__get__() 方法中,我们使用getattr() 函数获取对象的name 属性值,并返回它。在__set__() 方法中,我们使用setattr() 函数设置对象的name 属性值为传入的值。 然后,在Person 类中,我们定义了一个名为full_name 的属性,它被绑定到了一个NameAlias 实例上,用于实现属性别名。在Person 类的__init__() 方法中,我们初始化name 属性的值。现在,我们可以创建一个Person 对象,并访问它的name 和full_name 属性,如下所示: p = Person("Alice")print(p.name) # 输出 "Alice"print(p.full_name) # 输出 "Alice"p.full_name = "Bob"print(p.name) # 输出 "Bob"print(p.full_name) # 输出 "Bob" 在这个示例中,我们创建了一个Person 对象,并将其存储在p 变量中。然后,我们打印它的name 和full_name 属性,它们的值都是"Alice",表示它们是等价的。接着,我们将p 的full_name 属性设置为"Bob",这将同时改变name 属性的值。然后,我们再次打印name 和full_name 属性,它们的值都是"Bob",说明我们成功地实现了属性别名的功能。
示例Demo4下面是一个示例,演示如何使用描述器实现数据验证功能。 假设我们有一个Person 类,它有一个名为age 的属性,表示人的年龄。现在我们想要对age 属性的输入数据进行验证,以确保它的值在0到150之间。 我们可以使用描述器来实现这个功能,如下所示: class AgeValidator: def __get__(self, instance, owner): return instance._age def __set__(self, instance, value): if not isinstance(value, int) or value < 0 or value > 150: raise ValueError("Invalid age") instance._age = valueclass Person: def __init__(self, name, age): self.name = name self.age = age age = AgeValidator() 在这个示例中,我们定义了一个AgeValidator 描述器类,它用于实现数据验证功能。在AgeValidator 类中,我们实现了__get__() 和__set__() 方法,它们分别在读取和设置age 属性时被调用。在__set__() 方法中,我们首先检查输入的值是否是整数,并且是否在0到150之间。如果输入数据不符合要求,我们将引发一个ValueError 异常。否则,我们将设置instance._age 属性的值为传入的值。 然后,在Person 类中,我们定义了一个名为age 的属性,它被绑定到了一个AgeValidator 实例上,用于实现数据验证功能。在Person 类的__init__() 方法中,我们初始化name 和age 属性的值。现在,我们可以创建一个Person 对象,并尝试设置其age 属性的值,如下所示: p = Person("Alice", 25)print(p.age) # 输出 25p.age = 200 # 引发 ValueError: Invalid age 在这个示例中,我们创建了一个Person 对象,并将其存储在p 变量中。然后,我们打印它的age 属性,它的值是25。接着,我们尝试将p 的age 属性设置为200,这将引发一个ValueError 异常,因为200超出了允许的范围。这说明我们成功地使用描述器实现了数据验证功能,可以避免一些常见的错误。 在这个示例中,Person 类中有两个名为age 的定义,一个是在__init__() 方法中进行初始化的实例属性,另一个是通过描述器AgeValidator 绑定到Person 类上的类属性。 当我们在__init__() 方法中设置self.age = age 时,它实际上是在创建一个实例属性,而不是类属性。这个实例属性只对当前对象有效,而不对所有Person 对象都有效。 而当我们在Person 类中定义了一个名为age 的描述器属性时,它实际上是在创建一个类属性,这个属性可以被所有Person 对象所共享,它定义了age 属性的读写行为,可以对属性值进行验证,保证数据的正确性。 因此,这两个age 属性虽然名字相同,但它们的作用范围和用途不同。实例属性是用来存储每个对象的不同数据,而类属性则是用来实现属性的共享和控制。描述器可以让我们将类属性绑定到一个自定义的属性读写行为上,从而实现更灵活的属性操作。 到此这篇关于python的描述器descriptor详解的文章就介绍到这了,更多相关python的描述器内容请搜索wanshiok.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持wanshiok.com! 使用Python和OpenCV进行图像处理和分析 在Python中使用代理IP的方法详解 |