1. Python魔法方法入门指南
作为一名Python开发者,你可能经常听到"魔法方法"这个词,但真正理解并善用它们的人并不多。魔法方法(Magic Methods)是Python中那些以双下划线开头和结尾的特殊方法,它们赋予了类"魔法"般的行为能力。
提示:魔法方法也被称为"双下方法"(dunder methods),这个名称来源于它们的命名方式——双下划线包围。
1.1 什么是魔法方法
魔法方法是Python数据模型的核心组成部分。当你使用len()函数时,实际上是调用了对象的__len__方法;当你使用+运算符时,背后是__add__方法在工作。这些方法让Python的对象能够与语言的核心特性无缝集成。
python复制class MyList:
def __len__(self):
return 10
my_list = MyList()
print(len(my_list)) # 输出: 10
在这个简单例子中,我们通过实现__len__方法,让自定义的MyList类能够响应len()函数。这就是魔法方法的基本工作原理——它们为Python的运算符和内置函数提供了钩子。
1.2 为什么需要学习魔法方法
掌握魔法方法能带来三大优势:
- 代码更Pythonic:让你的类表现得像内置类型一样自然
- 功能更强大:实现运算符重载、上下文管理等高级特性
- 接口更一致:遵循Python的数据模型,与其他库更好交互
2. 常用魔法方法详解
2.1 对象初始化与表示
2.1.1 __init__ vs __new__
__init__可能是最广为人知的魔法方法,它负责对象的初始化。但很多人不知道的是,在__init__之前还有一个__new__方法。
python复制class Person:
def __new__(cls, *args, **kwargs):
print("创建新实例")
instance = super().__new__(cls)
return instance
def __init__(self, name):
print("初始化实例")
self.name = name
p = Person("Alice")
注意:
__new__是类方法(虽然不用@classmethod装饰),它返回实例;__init__是实例方法,不返回任何值。
2.1.2 对象表示方法
为了让对象有更好的可读性,Python提供了几种表示方法:
__str__:str(obj)和print(obj)时调用,面向用户__repr__:repr(obj)时调用,面向开发者__format__:format(obj)和f-string时调用
python复制class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __str__(self):
return f"点({self.x}, {self.y})"
def __repr__(self):
return f"Point(x={self.x}, y={self.y})"
def __format__(self, format_spec):
if format_spec == 'r':
return f"({self.y}, {self.x})"
return f"({self.x}, {self.y})"
p = Point(3, 4)
print(str(p)) # 点(3, 4)
print(repr(p)) # Point(x=3, y=4)
print(f"{p:r}") # (4, 3)
2.2 运算符重载
Python允许通过魔法方法重载大多数运算符,这让自定义类型可以像内置类型一样使用运算符。
2.2.1 算术运算符
| 运算符 | 魔法方法 | 反向方法 | 就地操作 |
|---|---|---|---|
| + | __add__ |
__radd__ |
__iadd__ |
| - | __sub__ |
__rsub__ |
__isub__ |
| * | __mul__ |
__rmul__ |
__imul__ |
| / | __truediv__ |
__rtruediv__ |
__itruediv__ |
python复制class Vector:
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Vector):
return Vector(self.x + other.x, self.y + other.y)
return NotImplemented
def __mul__(self, scalar):
if isinstance(scalar, (int, float)):
return Vector(self.x * scalar, self.y * scalar)
return NotImplemented
def __rmul__(self, scalar):
return self.__mul__(scalar)
v1 = Vector(1, 2)
v2 = Vector(3, 4)
print(v1 + v2) # 输出: <Vector object at ...>
print(3 * v1) # 输出: <Vector object at ...>
实操心得:实现算术运算时,记得处理不支持的类型并返回NotImplemented,这样Python会尝试调用反向方法或抛出TypeError。
2.2.2 比较运算符
比较运算符的魔法方法也很直观:
| 运算符 | 魔法方法 |
|---|---|
| < | __lt__ |
| <= | __le__ |
| == | __eq__ |
| != | __ne__ |
| > | __gt__ |
| >= | __ge__ |
python复制class Temperature:
def __init__(self, celsius):
self.celsius = celsius
def __lt__(self, other):
if isinstance(other, Temperature):
return self.celsius < other.celsius
return NotImplemented
def __eq__(self, other):
if isinstance(other, Temperature):
return self.celsius == other.celsius
return NotImplemented
t1 = Temperature(20)
t2 = Temperature(30)
print(t1 < t2) # True
print(t1 == t2) # False
2.3 容器类型魔法方法
要让自定义类表现得像列表或字典这样的容器,需要实现以下方法:
2.3.1 基本容器方法
python复制class ShoppingCart:
def __init__(self):
self.items = []
def __len__(self):
return len(self.items)
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __delitem__(self, index):
del self.items[index]
def __contains__(self, item):
return item in self.items
def __iter__(self):
return iter(self.items)
cart = ShoppingCart()
cart.items = ['苹果', '香蕉', '橙子']
print(len(cart)) # 3
print(cart[1]) # 香蕉
print('苹果' in cart) # True
2.3.2 更完整的容器实现
python复制class BinaryNode:
def __init__(self, value):
self.value = value
self.left = None
self.right = None
def __iter__(self):
if self.left:
yield from self.left
yield self.value
if self.right:
yield from self.right
def __contains__(self, value):
if value == self.value:
return True
elif value < self.value and self.left:
return value in self.left
elif value > self.value and self.right:
return value in self.right
return False
# 构建一个简单的二叉搜索树
root = BinaryNode(5)
root.left = BinaryNode(3)
root.right = BinaryNode(7)
root.left.left = BinaryNode(2)
root.left.right = BinaryNode(4)
root.right.left = BinaryNode(6)
root.right.right = BinaryNode(8)
print(list(root)) # [2, 3, 4, 5, 6, 7, 8]
print(4 in root) # True
print(9 in root) # False
2.4 上下文管理协议
Python的with语句背后是__enter__和__exit__魔法方法。
python复制class Timer:
def __enter__(self):
import time
self.start = time.time()
return self
def __exit__(self, exc_type, exc_val, exc_tb):
import time
self.elapsed = time.time() - self.start
print(f"耗时: {self.elapsed:.2f}秒")
return False # 不抑制异常
with Timer() as t:
import time
time.sleep(1)
# 输出: 耗时: 1.00秒
注意事项:
__exit__方法返回True会抑制with块中发生的异常,通常应该返回False或None。
3. 高级魔法方法应用
3.1 属性访问控制
Python提供了精细的属性访问控制方法:
python复制class ProtectedObject:
def __init__(self):
self._protected = "受保护属性"
self.__private = "私有属性"
@property
def protected(self):
print("访问受保护属性")
return self._protected
@protected.setter
def protected(self, value):
print("设置受保护属性")
self._protected = value
def __getattr__(self, name):
print(f"访问不存在的属性: {name}")
return None
def __setattr__(self, name, value):
if name.startswith('__'):
raise AttributeError("不能设置私有属性")
super().__setattr__(name, value)
def __delattr__(self, name):
if name.startswith('_'):
raise AttributeError("不能删除保护属性")
super().__delattr__(name)
obj = ProtectedObject()
print(obj.protected) # 访问受保护属性 -> 受保护属性
obj.protected = "新值" # 设置受保护属性
print(obj.nonexistent) # 访问不存在的属性: nonexistent -> None
3.2 描述符协议
描述符是实现了__get__、__set__或__delete__方法的类,它们是@property的底层实现。
python复制class PositiveNumber:
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
return instance.__dict__[self.name]
def __set__(self, instance, value):
if value <= 0:
raise ValueError("必须是正数")
instance.__dict__[self.name] = value
class Circle:
radius = PositiveNumber()
def __init__(self, radius):
self.radius = radius
c = Circle(5)
print(c.radius) # 5
try:
c.radius = -1 # ValueError: 必须是正数
except ValueError as e:
print(e)
3.3 类创建与单例模式
__new__方法还可以用于实现设计模式,比如单例:
python复制class Singleton:
_instance = None
def __new__(cls, *args, **kwargs):
if cls._instance is None:
cls._instance = super().__new__(cls)
return cls._instance
def __init__(self, name):
if not hasattr(self, 'name'): # 防止重复初始化
self.name = name
a = Singleton("第一个")
b = Singleton("第二个")
print(a is b) # True
print(a.name) # 第一个
print(b.name) # 第一个
4. 魔法方法实战技巧
4.1 模拟数值类型
通过实现数值相关的魔法方法,可以让自定义类型支持各种数学运算。
python复制class Fraction:
def __init__(self, numerator, denominator=1):
self.numerator = numerator
self.denominator = denominator
self._simplify()
def _simplify(self):
def gcd(a, b):
while b:
a, b = b, a % b
return a
common = gcd(self.numerator, self.denominator)
self.numerator //= common
self.denominator //= common
def __add__(self, other):
if isinstance(other, int):
other = Fraction(other)
if not isinstance(other, Fraction):
return NotImplemented
new_num = self.numerator * other.denominator + other.numerator * self.denominator
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den)
def __sub__(self, other):
if isinstance(other, int):
other = Fraction(other)
if not isinstance(other, Fraction):
return NotImplemented
new_num = self.numerator * other.denominator - other.numerator * self.denominator
new_den = self.denominator * other.denominator
return Fraction(new_num, new_den)
def __mul__(self, other):
if isinstance(other, int):
other = Fraction(other)
if not isinstance(other, Fraction):
return NotImplemented
return Fraction(self.numerator * other.numerator, self.denominator * other.denominator)
def __truediv__(self, other):
if isinstance(other, int):
other = Fraction(other)
if not isinstance(other, Fraction):
return NotImplemented
return Fraction(self.numerator * other.denominator, self.denominator * other.numerator)
def __eq__(self, other):
if isinstance(other, int):
other = Fraction(other)
if not isinstance(other, Fraction):
return NotImplemented
return (self.numerator == other.numerator and
self.denominator == other.denominator)
def __str__(self):
if self.denominator == 1:
return str(self.numerator)
return f"{self.numerator}/{self.denominator}"
def __repr__(self):
return f"Fraction({self.numerator}, {self.denominator})"
f1 = Fraction(1, 2)
f2 = Fraction(3, 4)
print(f1 + f2) # 5/4
print(f1 - f2) # -1/4
print(f1 * f2) # 3/8
print(f1 / f2) # 2/3
print(f1 + 1) # 3/2
4.2 实现缓存属性
结合__getattr__和描述符,可以实现高效的缓存属性。
python复制class cached_property:
def __init__(self, func):
self.func = func
self.name = func.__name__
def __get__(self, instance, owner):
if instance is None:
return self
value = self.func(instance)
instance.__dict__[self.name] = value
return value
class DataSet:
def __init__(self, data):
self.data = data
@cached_property
def stats(self):
print("计算统计数据...")
import time
time.sleep(2) # 模拟耗时计算
return {
'mean': sum(self.data) / len(self.data),
'max': max(self.data),
'min': min(self.data)
}
ds = DataSet([1, 2, 3, 4, 5])
print(ds.stats) # 第一次访问会计算
print(ds.stats) # 第二次访问直接返回缓存结果
4.3 动态属性访问
通过__getattribute__和__getattr__可以实现动态属性访问。
python复制class DynamicAttributes:
def __init__(self):
self._data = {}
def __getattribute__(self, name):
try:
return super().__getattribute__(name)
except AttributeError:
if name in self._data:
return self._data[name]
raise
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
self._data[name] = value
def __delattr__(self, name):
if name in self._data:
del self._data[name]
else:
super().__delattr__(name)
dyn = DynamicAttributes()
dyn.name = "动态属性"
print(dyn.name) # 动态属性
del dyn.name
try:
print(dyn.name) # AttributeError
except AttributeError as e:
print(f"属性不存在: {e}")
5. 魔法方法最佳实践
5.1 魔法方法使用原则
- 一致性原则:魔法方法的行为应该与Python内置类型保持一致
- 最小惊讶原则:避免实现令人惊讶的行为
- 明确性原则:只在确实需要时才实现魔法方法
- 性能考虑:某些魔法方法(如
__getattr__)会影响性能,需谨慎使用
5.2 常见陷阱与解决方案
5.2.1 无限递归问题
python复制class BadExample:
def __init__(self):
self.data = {}
def __getattribute__(self, name):
# 错误实现:会导致无限递归
return self.data[name] # 这里又调用了__getattribute__
class GoodExample:
def __init__(self):
# 必须使用super().__setattr__来避免递归
super().__setattr__('data', {})
def __getattribute__(self, name):
data = super().__getattribute__('data')
if name in data:
return data[name]
return super().__getattribute__(name)
good = GoodExample()
good.data['test'] = 'value'
print(good.test) # value
5.2.2 哈希与相等性
当实现__eq__时,通常也应该实现__hash__,否则对象将不可哈希(不能用作字典键或集合元素)。
python复制class Point:
def __init__(self, x, y):
self.x = x
self.y = y
def __eq__(self, other):
if not isinstance(other, Point):
return NotImplemented
return self.x == other.x and self.y == other.y
def __hash__(self):
return hash((self.x, self.y))
p1 = Point(1, 2)
p2 = Point(1, 2)
print(p1 == p2) # True
points = {p1, p2}
print(len(points)) # 1
5.3 性能优化技巧
- 避免不必要的魔法方法:只实现确实需要的方法
- 使用
__slots__减少内存占用:对于大量实例的类特别有效 - 缓存计算结果:如前面
cached_property的例子 - 使用内置函数:在魔法方法实现中优先使用内置函数
python复制class Optimized:
__slots__ = ['x', 'y'] # 固定属性列表,节省内存
def __init__(self, x, y):
self.x = x
self.y = y
def __add__(self, other):
if isinstance(other, Optimized):
return Optimized(self.x + other.x, self.y + other.y)
return NotImplemented
def __iter__(self):
yield self.x
yield self.y
opt = Optimized(1, 2)
print(list(opt)) # [1, 2]
6. 魔法方法在实际项目中的应用
6.1 Django模型中的魔法方法
Django的ORM大量使用了魔法方法。例如,查询集(QuerySet)的链式调用就是通过__iter__、__getitem__等方法实现的。
python复制# 类似Django QuerySet的实现
class QuerySet:
def __init__(self, model=None, query=None):
self.model = model
self._query = query or {}
self._result_cache = None
def filter(self, **kwargs):
new_query = self._query.copy()
new_query.update(kwargs)
return self.__class__(model=self.model, query=new_query)
def __iter__(self):
if self._result_cache is None:
print(f"执行查询: {self._query}")
self._result_cache = [
self.model(**{'id': 1, 'name': 'Alice'}),
self.model(**{'id': 2, 'name': 'Bob'})
]
return iter(self._result_cache)
def __getitem__(self, index):
if isinstance(index, slice):
return list(self)[index]
return list(self)[index]
class User:
def __init__(self, **kwargs):
self.__dict__.update(kwargs)
def __repr__(self):
return f"User(id={self.id}, name='{self.name}')"
User.objects = QuerySet(model=User)
users = User.objects.filter(active=True)[:2]
print(list(users))
# 执行查询: {'active': True}
# [User(id=1, name='Alice'), User(id=2, name='Bob')]
6.2 Flask的路由装饰器
Flask使用__call__方法让路由装饰器工作:
python复制class Route:
def __init__(self, app, path):
self.app = app
self.path = path
def __call__(self, view_func):
self.app.add_url_rule(self.path, view_func=view_func)
return view_func
class Flask:
def __init__(self):
self.routes = {}
def add_url_rule(self, path, view_func):
self.routes[path] = view_func
def route(self, path):
return Route(self, path)
def run(self):
print("模拟Flask运行...")
while True:
path = input("请输入路径: ")
if path == 'exit':
break
if path in self.routes:
print(f"执行视图: {self.routes[path]()}")
else:
print("404 Not Found")
app = Flask()
@app.route('/')
def home():
return "首页"
@app.route('/about')
def about():
return "关于我们"
app.run()
6.3 Pandas的链式调用
Pandas的链式调用风格也是通过魔法方法实现的:
python复制class DataFrame:
def __init__(self, data):
self.data = data
def __getitem__(self, key):
if isinstance(key, str):
return Column(self, key)
return DataFrame({k: self.data[k] for k in key})
def filter(self, condition):
filtered = {k: [v for v, c in zip(self.data[k], condition)]
for k in self.data}
return DataFrame(filtered)
def __repr__(self):
return f"DataFrame({self.data})"
class Column:
def __init__(self, df, name):
self.df = df
self.name = name
def __eq__(self, value):
return [x == value for x in self.df.data[self.name]]
def __repr__(self):
return f"Column({self.name})"
data = {
'name': ['Alice', 'Bob', 'Charlie'],
'age': [25, 30, 35],
'city': ['NY', 'LA', 'CH']
}
df = DataFrame(data)
result = df[['name', 'city']].filter(df['age'] > 28)
print(result) # DataFrame({'name': ['Bob', 'Charlie'], 'city': ['LA', 'CH']})
7. 魔法方法调试技巧
7.1 查看对象支持的魔法方法
使用dir()函数可以查看对象支持的所有方法和属性,包括魔法方法。
python复制class Example:
def __init__(self):
pass
def __str__(self):
return "示例"
print(dir(Example()))
# 输出中包含 '__str__', '__init__' 等魔法方法
7.2 使用inspect模块
inspect模块提供了更强大的内省功能。
python复制import inspect
class Demo:
def method(self):
pass
print(inspect.getmembers(Demo, predicate=inspect.isfunction))
# 输出类中定义的方法,包括魔法方法
7.3 调试__getattr__和__getattribute__
当调试这些方法时,需要特别小心避免无限递归。
python复制class DebugAttributes:
def __init__(self):
self._data = {}
def __getattribute__(self, name):
print(f"尝试获取属性: {name}")
try:
return super().__getattribute__(name)
except AttributeError:
if name in self._data:
return self._data[name]
raise
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
print(f"设置属性: {name} = {value}")
self._data[name] = value
debug = DebugAttributes()
debug.test = "调试值"
print(debug.test)
8. 魔法方法性能考量
8.1 魔法方法的性能影响
某些魔法方法会对性能产生显著影响:
__getattribute__:每次属性访问都会调用,影响很大__getattr__:只在属性不存在时调用,影响较小__setattr__:每次属性设置都会调用__call__:使实例可调用,但比普通方法调用稍慢
8.2 性能优化示例
python复制import time
class SlowClass:
def __getattribute__(self, name):
return super().__getattribute__(name)
def method(self):
pass
class FastClass:
__slots__ = []
def method(self):
pass
def test_performance(cls, n=1000000):
obj = cls()
start = time.time()
for _ in range(n):
obj.method()
return time.time() - start
slow_time = test_performance(SlowClass)
fast_time = test_performance(FastClass)
print(f"慢速类: {slow_time:.3f}秒")
print(f"快速类: {fast_time:.3f}秒")
print(f"差异: {slow_time/fast_time:.1f}倍")
在实际项目中,应该权衡魔法方法带来的便利性和性能影响,特别是在高频调用的代码路径中。
9. 魔法方法与元类
元类也依赖于魔法方法,特别是__new__和__init__。
python复制class Meta(type):
def __new__(mcls, name, bases, namespace):
print(f"创建类: {name}")
namespace['version'] = 1.0
return super().__new__(mcls, name, bases, namespace)
def __init__(cls, name, bases, namespace):
super().__init__(name, bases, namespace)
print(f"初始化类: {name}")
class Base(metaclass=Meta):
pass
class Derived(Base):
pass
print(Derived.version) # 1.0
元类可以用于类注册、接口验证、自动添加方法等高级功能。
10. 魔法方法在异步编程中的应用
Python的异步编程也使用了魔法方法,如__aiter__和__anext__用于异步迭代。
python复制import asyncio
class AsyncCounter:
def __init__(self, stop):
self.stop = stop
self.current = 0
def __aiter__(self):
return self
async def __anext__(self):
if self.current >= self.stop:
raise StopAsyncIteration
await asyncio.sleep(0.1)
self.current += 1
return self.current
async def main():
async for number in AsyncCounter(5):
print(number)
asyncio.run(main())
11. 魔法方法在上下文管理器中的应用
除了基本的__enter__和__exit__,Python 3.7+还引入了__aenter__和__aexit__用于异步上下文管理。
python复制import asyncio
class AsyncTimer:
async def __aenter__(self):
self.start = asyncio.get_event_loop().time()
return self
async def __aexit__(self, exc_type, exc, tb):
self.elapsed = asyncio.get_event_loop().time() - self.start
print(f"异步耗时: {self.elapsed:.2f}秒")
return False
async def async_task():
async with AsyncTimer() as timer:
await asyncio.sleep(1)
asyncio.run(async_task())
12. 魔法方法在描述符中的应用
描述符协议(__get__, __set__, __delete__)本身就是一组魔法方法,可以用来创建强大的属性访问控制。
python复制class ValidatedAttribute:
def __init__(self, validator):
self.validator = validator
self.name = None
def __set_name__(self, owner, name):
self.name = name
def __get__(self, instance, owner):
if instance is None:
return self
return instance.__dict__[self.name]
def __set__(self, instance, value):
if not self.validator(value):
raise ValueError(f"无效值: {value}")
instance.__dict__[self.name] = value
def is_positive(x):
return x > 0
class Account:
balance = ValidatedAttribute(is_positive)
def __init__(self, initial_balance):
self.balance = initial_balance
acc = Account(100)
print(acc.balance) # 100
try:
acc.balance = -50 # ValueError: 无效值: -50
except ValueError as e:
print(e)
13. 魔法方法在模式匹配中的应用
Python 3.10引入的模式匹配功能也使用了魔法方法__match_args__。
python复制class Point:
__match_args__ = ('x', 'y')
def __init__(self, x, y):
self.x = x
self.y = y
def check_point(p):
match p:
case Point(0, 0):
return "原点"
case Point(x, 0):
return f"X轴上: {x}"
case Point(0, y):
return f"Y轴上: {y}"
case Point(x, y):
return f"点({x}, {y})"
case _:
return "不是点"
print(check_point(Point(0, 0))) # 原点
print(check_point(Point(3, 0))) # X轴上: 3
print(check_point(Point(0, 4))) # Y轴上: 4
print(check_point(Point(2, 3))) # 点(2, 3)
14. 魔法方法在数据类中的应用
Python的数据类(dataclass)自动生成多个魔法方法,如__init__、__repr__、__eq__等。
python复制from dataclasses import dataclass
@dataclass
class InventoryItem:
name: str
unit_price: float
quantity: int = 0
def total_cost(self) -> float:
return self.unit_price * self.quantity
item = InventoryItem("Widget", 3.5, 10)
print(item) # InventoryItem(name='Widget', unit_price=3.5, quantity=10)
print(item.total_cost()) # 35.0
15. 魔法方法在枚举中的应用
枚举类型也使用了魔法方法来实现其特殊行为。
python复制from enum import Enum, auto
class Color(Enum):
RED = auto()
GREEN = auto()
BLUE = auto()
def __str__(self):
return f"{self.name.lower()} color"
@classmethod
def favorite(cls):
return cls.GREEN
print(Color.RED) # red color
print(Color.favorite()) # Color.GREEN
16. 魔法方法在自定义异常中的应用
自定义异常可以通过魔法方法提供更丰富的功能。
python复制class ValidationError(Exception):
def __init__(self, message, errors=None):
super().__init__(message)
self.errors = errors or {}
def __str__(self):
if not self.errors:
return super().__str__()
errors = "\n".join(f"- {k}: {v}" for k, v in self.errors.items())
return f"{super().__str__()}\n详细错误:\n{errors}"
try:
raise ValidationError("验证失败", {"email": "无效格式", "age": "必须大于18"})
except ValidationError as e:
print(e)
17. 魔法方法在缓存中的应用
通过魔法方法可以实现智能缓存功能。
python复制class cached_property:
def __init__(self, func):
self.func = func
self.attr_name = f"_{func.__name__}"
def __get__(self, instance, owner):
if instance is None:
return self
if hasattr(instance, self.attr_name):
return getattr(instance, self.attr_name)
value = self.func(instance)
setattr(instance, self.attr_name, value)
return value
def __set__(self, instance, value):
setattr(instance, self.attr_name, value)
class Circle:
def __init__(self, radius):
self.radius = radius
@cached_property
def area(self):
print("计算面积")
return 3.14159 * self.radius ** 2
@cached_property
def circumference(self):
print("计算周长")
return 2 * 3.14159 * self.radius
c = Circle(5)
print(c.area) # 第一次访问会计算
print(c.area) # 第二次访问直接返回缓存值
c.radius = 10
print(c.area) # 缓存未清除,返回旧值
c.area = 314.159 # 手动设置新值
print(c.area) # 返回手动设置的值
18. 魔法方法在代理模式中的应用
魔法方法可以用来实现代理模式,控制对另一个对象的访问。
python复制class Proxy:
def __init__(self, obj):
self._obj = obj
def __getattr__(self, name):
print(f"访问属性: {name}")
return getattr(self._obj, name)
def __setattr__(self, name, value):
if name.startswith('_'):
super().__setattr__(name, value)
else:
print(f"设置属性: {name} = {value}")
setattr(self._obj, name, value)
def __delattr__(self, name):
if name.startswith('_'):
super().__delattr__(name)
else:
print(f"删除属性: {name}")
delattr(self._obj, name)
class Original:
def __init__(self):
self.value = 42
def method(self):
return "原始方法"
original = Original()
proxy = Proxy(original)
print(proxy.value) # 访问属性: value -> 42
proxy.value = 100 # 设置属性: value = 100
print(original.value) # 100
print(proxy.method())