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 教程。