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
是描述符类,其派生类StringField
和IntegerField
分别表示字符串类型和整数类型的数据字段。同时我们还使用了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编程的高级水平和代码质量。