基本说明
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 = value
class MyClass:
attr = Descriptor()
obj = MyClass()
obj.attr = 42
print(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 = value
class 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.0
print(t.celsius) # 输出 25.0
print(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) # 输出 2
print(c2.count) # 输出 2
Counter.count = 100
print(c1.count) # 输出 100
print(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 = value
class 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) # 输出 25
p.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的描述器内容请搜索aitechtogether.com以前的文章或继续浏览下面的相关文章希望大家以后多多支持aitechtogether.com!