介绍python描述符的意义

1. Python描述符介绍

在Python中,描述符是一种重要的概念,可以理解为实现了特定协议的对象属性。描述符的应用极大地提升了Python语言的功能和灵活性,是Python高级编程必须掌握的技能之一。

一个描述符必须实现其中的一种或多种协议,其中最重要的是__get____set____delete__

class Descriptor:

def __get__(self, instance, owner):

pass

def __set__(self, instance, value):

pass

def __delete__(self, instance):

pass

其中,__get__方法在访问属性值时被调用,__set__方法在修改属性值时被调用,__delete__方法在删除属性值时被调用。

2. 描述符的意义

描述符的应用有很多方面,从面向对象设计的角度来看,描述符可以让类实现对属性的访问控制,保证数据的正确性和安全性,从而提高代码的可维护性和可扩展性。

比如我们可以用描述符实现一个温度计,限制温度的范围在0到100度之间。

2.1 实现一个温度计

class Temperature:

def __init__(self, celsius=0):

self.celsius = celsius

def to_fahrenheit(self):

"""将温度从摄氏度转为华氏度"""

return (self.celsius * 1.8) + 32

def __get__(self, instance, owner):

return self.celsius

def __set__(self, instance, celsius):

if celsius < 0:

self.celsius = 0

elif celsius > 100:

self.celsius = 100

else:

self.celsius = celsius

class Thermostat:

temperature = Temperature()

t = Thermostat()

t.temperature = 50

assert t.temperature == 50

t.temperature = 150

assert t.temperature == 100

t.temperature = -50

assert t.temperature == 0

从上述实例可以看出,描述符实现温度计的过程中,通过__set__方法限制了温度范围在0到100度之间,做到了对属性的访问控制,保证了温度计的数据正确性和安全性。

2.2 动态绑定描述符

除了类属性的赋值,我们还可以在运行时动态绑定描述符,实现动态属性赋值。

class Descriptor:

def __get__(self, instance, owner):

if instance is None:

return self

return instance.__dict__[self.attr]

def __set__(self, instance, value):

instance.__dict__[self.attr] = value

class Point:

def __init__(self, x=0, y=0):

self.x = x

self.y = y

p = Point()

Descriptor.attr = 'x'

p.x = Descriptor()

p.x = 10

assert p.x == 10

从上述实例可以看出,我们在运行时给描述符动态绑定了对象属性,并将其赋值为一个新的Descriptor对象,实现了属性动态赋值。

3. 描述符的应用场景

描述符的应用场景非常广泛,它可以用来实现各种高级编程技能,比如实现一个ORM框架、实现一个缓存系统、实现一个代理对象等。

3.1 实现一个ORM框架

ORM(Object-Relational Mapping)框架是将面向对象的数据模型转换为关系型数据库模型的一种技术。

我们可以使用Python描述符来实现一个简单的ORM框架,如下所示:

class Field:

def __init__(self, name, column_type):

self.name = name

self.column_type = column_type

def __str__(self):

return f'{self.name}: {self.column_type}'

class StringField(Field):

def __init__(self, name):

super().__init__(name, 'varchar(100)')

class IntegerField(Field):

def __init__(self, name):

super().__init__(name, 'bigint')

class ModelMetaclass(type):

def __new__(cls, name, bases, attrs):

if name == 'Model':

return type.__new__(cls, name, bases, attrs)

mappings = {}

for k, v in attrs.items():

if isinstance(v, Field):

mappings[k] = v

for k in mappings.keys():

attrs.pop(k)

attrs['__mappings__'] = mappings

attrs['__table__'] = name.lower()

return type.__new__(cls, name, bases, attrs)

class Model(metaclass=ModelMetaclass):

def __init__(self, **kwargs):

for k, v in kwargs.items():

setattr(self, k, v)

def save(self):

fields = []

values = []

for k, v in self.__mappings__.items():

fields.append(v.name)

values.append(getattr(self, k, None))

sql = f'insert into {self.__table__} ({", ".join(fields)}) values ({", ".join(["{}"] * len(fields))})'

print(f'{sql}: {values}')

class User(Model):

id = IntegerField('id')

name = StringField('name')

email = StringField('email')

password = StringField('password')

u = User(id=123, name='Bob', email='bob@123.com', password='******')

u.save()

从上述实例可以看出,我们使用描述符实现了一个简单的ORM框架,其中Field是描述符类,其派生类StringFieldIntegerField分别表示字符串类型和整数类型的数据字段。同时我们还使用了Python元类ModelMetaclass来动态生成ORM类,在生成ORM类时将其属性与Field描述符对象建立映射关系。

3.2 实现一个缓存系统

缓存是提高系统性能的一种重要方法,Python描述符可以作为缓存系统的重要组成部分。

class cache:

def __init__(self, f):

self.f = f

self.cache = {}

def __call__(self, *args, **kwargs):

key = str(args) + str(kwargs)

if key in self.cache:

return self.cache[key]

else:

result = self.f(*args, **kwargs)

self.cache[key] = result

return result

@cache

def fibonacci(n):

if n in (0, 1):

return n

return fibonacci(n-1) + fibonacci(n-2)

fibonacci(1) # 1

fibonacci(2) # 1

fibonacci(3) # 2

fibonacci(4) # 3

fibonacci(5) # 5

从上述实例可以看出,我们使用Python描述符cache来实现了一个简单的缓存系统。在函数fibonacci中使用了装饰器@cache,每次调用函数fibonacci时,首先查看缓存中是否已经有了结果,如果有则直接返回结果,如果没有则计算结果并将其缓存起来。

3.3 实现一个代理对象

Python描述符作为实现代理对象的重要组成部分,可以被用来实现各种高级编程技巧,比如代理模式、装饰器模式等。

class LazyProperty:

def __init__(self, func):

self.func = func

def __get__(self, instance, owner):

if instance is None:

return self

else:

value = self.func(instance)

setattr(instance, self.func.__name__, value)

return value

class User:

def __init__(self, name):

self._name = name

@LazyProperty

def name(self):

print('Calculating name')

return self._name

def greet(self):

return f'Hello, {self.name}'

u = User('Bob')

assert u.greet() == 'Hello, Bob'

assert u.greet() == 'Hello, Bob'

从上述实例可以看出,我们使用Python描述符LazyProperty实现了一个简单的代理对象。在类User中,我们将函数name包装成一个LazyProperty描述符对象,以实现懒加载功能,即在第一次调用name属性时进行计算,而后直接返回计算结果。

4. 总结

Python描述符是实现高级编程技巧的重要手段,包括但不限于实现对属性的访问控制、实现ORM框架、实现缓存系统、实现代理对象等。加强对Python描述符机制的学习,可以提高Python编程的高级水平和代码质量。

免责声明:本文来自互联网,本站所有信息(包括但不限于文字、视频、音频、数据及图表),不保证该信息的准确性、真实性、完整性、有效性、及时性、原创性等,版权归属于原作者,如无意侵犯媒体或个人知识产权,请来电或致函告之,本站将在第一时间处理。猿码集站发布此文目的在于促进信息交流,此文观点与本站立场无关,不承担任何责任。

后端开发标签