Python __get__ 方法
最后修改于 2025 年 4 月 8 日
本综合指南探讨 Python 的 __get__ 方法,它是描述器的核心特殊方法。我们将介绍基本用法、属性实现、方法绑定和实际示例。
基本定义
__get__ 方法是 Python 描述器协议的一部分。当访问作为描述器的属性时,会调用它。描述器允许自定义属性访问。
主要特性:它接受三个参数(self、instance 和 owner),返回属性值,并用于实现属性、方法和其他属性访问模式。
基本描述器实现
这是一个简单的描述器,展示了 __get__ 的实际应用。它记录属性访问,同时保持正常的行为。
class LoggedAccess:
def __init__(self, value):
self.value = value
def __get__(self, instance, owner):
print(f"Accessing {self.value} from {instance}")
return self.value
class MyClass:
attr = LoggedAccess(42)
obj = MyClass()
print(obj.attr) # Prints access message and returns 42
这个例子演示了基本的描述器模式。当访问 obj.attr 时,Python 会使用实例和所有者类调用 LoggedAccess.__get__。
当通过类而不是实例访问时,instance 参数为 None。owner 参数是定义描述器的类。
实现属性
property 内置函数是使用描述器实现的。下面是如何使用 __get__ 创建类似属性的描述器。
class MyProperty:
def __init__(self, getter):
self.getter = getter
def __get__(self, instance, owner):
if instance is None:
return self
return self.getter(instance)
class Circle:
def __init__(self, radius):
self.radius = radius
@MyProperty
def diameter(self):
return self.radius * 2
circle = Circle(5)
print(circle.diameter) # 10
这个自定义属性描述器存储 getter 函数,并在访问属性时调用它。instance is None 检查处理类访问。
@MyProperty 装饰器的作用与 @property 类似,展示了 Python 的属性系统是如何构建在描述器之上的。
使用 __get__ 进行方法绑定
Python 使用 __get__ 来实现方法绑定。下面是如何模拟方法绑定行为。
class Method:
def __init__(self, func):
self.func = func
def __get__(self, instance, owner):
if instance is None:
return self
from functools import partial
return partial(self.func, instance)
class MyClass:
def __init__(self, value):
self.value = value
@Method
def show(self):
print(f"Value: {self.value}")
obj = MyClass(10)
obj.show() # Value: 10
这个例子展示了 Python 如何将方法绑定到实例。当访问方法时,__get__ 返回一个 partial 函数,并将实例作为第一个参数绑定。
来自 functools 的 partial 函数创建一个新的函数,其中实例已预先绑定,模拟 Python 的方法绑定。
缓存属性描述器
这是一个实用的描述器,它在首次访问后缓存计算的属性,从而优化昂贵计算的性能。
class CachedProperty:
def __init__(self, func):
self.func = func
self.cache_name = f"_cache_{func.__name__}"
def __get__(self, instance, owner):
if instance is None:
return self
if not hasattr(instance, self.cache_name):
setattr(instance, self.cache_name, self.func(instance))
return getattr(instance, self.cache_name)
class Data:
def __init__(self, data):
self.data = data
@CachedProperty
def processed_data(self):
print("Processing data...")
return [x * 2 for x in self.data]
d = Data([1, 2, 3])
print(d.processed_data) # Processes and prints
print(d.processed_data) # Returns cached value
这个描述器将计算的值存储在实例的命名空间中。后续访问返回缓存的值,而不是重新计算。
缓存名称是动态生成的,以避免冲突。这种模式对于首次访问后不会改变的昂贵计算非常有用。
类型检查描述器
描述器可以强制执行属性访问的类型检查。此示例确保属性始终具有特定类型。
class Typed:
def __init__(self, name, expected_type):
self.name = name
self.expected_type = expected_type
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not isinstance(value, self.expected_type):
raise TypeError(f"Expected {self.expected_type}")
instance.__dict__[self.name] = value
class Person:
name = Typed("name", str)
age = Typed("age", int)
def __init__(self, name, age):
self.name = name
self.age = age
p = Person("Alice", 30)
# p.age = "thirty" # Raises TypeError
这个描述器将值存储在实例的 __dict__ 中,同时强制执行类型约束。__get__ 方法检索存储的值。
描述器在实例的字典中维护实际数据,同时通过描述器协议控制访问。
最佳实践
- 处理 instance=None: 始终检查类级别的访问
- 正确存储实例数据: 使用 instance.__dict__ 以避免无限递归
- 记录描述器行为: 清楚地解释预期行为
- 考虑性能: 描述器会增加属性访问的开销
- 尽可能使用现有工具: 对于简单的情况,首选 @property
资料来源
作者
列出所有 Python 教程。