你问的是 Python 中的内置函数 setattrgetattrhasattrdelattr,以及对应的魔法方法 __setattr____getattr____delattr__,再加上类似操作字典的魔法方法 __setitem____getitem____delitem__ 的用法和区别。下面帮你系统总结讲清楚:


一、内置函数 vs 魔法方法区别

函数/方法作用用于对象属性还是容器元素
setattr(obj, name, value)设置对象 obj 的属性 name 为 value对象属性
getattr(obj, name[, default])获取对象 obj 的属性 name,找不到时可返回 default对象属性
hasattr(obj, name)判断对象 obj 是否有属性 name对象属性
delattr(obj, name)删除对象 obj 的属性 name对象属性
obj.__setattr__(name, value)魔法方法,重载属性赋值时触发对象属性
obj.__getattr__(name)魔法方法,访问不存在属性时触发对象属性(访问未定义属性的回退方法)
obj.__delattr__(name)魔法方法,删除属性时触发对象属性
obj.__setitem__(key, value)魔法方法,重载索引赋值(如 obj[key] = value容器元素(字典、列表等的索引操作)
obj.__getitem__(key)魔法方法,重载索引访问(如 obj[key]容器元素
obj.__delitem__(key)魔法方法,重载索引删除(如 del obj[key]容器元素

二、内置函数详细用法

class MyClass:
    pass

obj = MyClass()

# setattr(obj, attr_name, value) 等同于 obj.attr_name = value
setattr(obj, 'x', 10)
print(obj.x)  # 10

# getattr(obj, attr_name) 等同于 obj.attr_name,带默认值避免异常
print(getattr(obj, 'x'))  # 10
print(getattr(obj, 'y', 'default'))  # default

# hasattr(obj, attr_name) 判断属性是否存在
print(hasattr(obj, 'x'))  # True
print(hasattr(obj, 'y'))  # False

# delattr(obj, attr_name) 等同于 del obj.attr_name
delattr(obj, 'x')
print(hasattr(obj, 'x'))  # False

三、魔法方法详细用法和区别

1. __setattr__(self, name, value)

  • 任何通过 obj.attr = value 的赋值都会调用它。
  • 如果你重写它,通常要调用 super().__setattr__(name, value) 或 self.__dict__[name] = value 否则会导致无限递归。

示例:

class MyClass:
    def __setattr__(self, name, value):
        print(f'Setting {name} to {value}')
        super().__setattr__(name, value)

obj = MyClass()
obj.x = 5  # 输出: Setting x to 5

2. __getattr__(self, name)

  • 只在访问不存在的属性时调用。
  • 不是属性访问的通用钩子(那是 __getattribute__)。
  • 常用来动态返回属性或实现代理。

示例:

class MyClass:
    def __getattr__(self, name):
        print(f'{name} not found, return 42')
        return 42

obj = MyClass()
print(obj.x)  # 输出: x not found, return 42  然后返回 42

3. __delattr__(self, name)

  • 任何 del obj.attr 操作会调用它。

示例:

class MyClass:
    def __delattr__(self, name):
        print(f'Deleting {name}')
        super().__delattr__(name)

obj = MyClass()
obj.x = 5
del obj.x  # 输出: Deleting x

四、__setitem____getitem____delitem__ 用法

  • 用于重载容器的索引访问操作,如 obj[key] = valueobj[key]del obj[key]
  • 多用于自定义字典、列表等容器类型

示例:

class MyDict:
    def __init__(self):
        self.data = {}

    def __setitem__(self, key, value):
        print(f'Setting key {key} to {value}')
        self.data[key] = value

    def __getitem__(self, key):
        print(f'Getting key {key}')
        return self.data[key]

    def __delitem__(self, key):
        print(f'Deleting key {key}')
        del self.data[key]

d = MyDict()
d['a'] = 10     # Setting key a to 10
print(d['a'])   # Getting key a  \n 10
del d['a']      # Deleting key a

五、总结对比

场景使用示例触发方式作用域
设置属性obj.attr = val触发 __setattr__ 或 setattr()对象属性
访问属性obj.attr触发 __getattr__ / __getattribute__ 或 getattr()对象属性
删除属性del obj.attr触发 __delattr__ 或 delattr()对象属性
访问容器元素(索引)obj[key]触发 __getitem__容器索引(字典/列表等)
设置容器元素(索引)obj[key] = val触发 __setitem__容器索引
删除容器元素(索引)del obj[key]触发 __delitem__容器索引

如果你需要,我还可以帮你写一份深度的示例代码,或帮你解读 __getattribute__ 和 __missing__ 等高级用法,告诉我!

好的!下面给你一份更深度的示例,包含:

  • __setattr____getattr____getattribute____delattr__ 的详细示范,展示它们调用顺序和区别
  • 自定义容器的 __setitem____getitem____delitem__ 示例
  • 以及特殊方法 __missing__(字典找不到键时调用)

1. 属性访问相关魔法方法演示

class Demo:
    def __init__(self):
        self.x = 10

    def __setattr__(self, name, value):
        print(f"__setattr__ called: {name} = {value}")
        super().__setattr__(name, value)  # 避免递归

    def __getattr__(self, name):
        print(f"__getattr__ called for missing attribute: {name}")
        return f"default_for_{name}"

    def __getattribute__(self, name):
        print(f"__getattribute__ called: {name}")
        # 始终调用父类的 __getattribute__ 避免死循环
        return super().__getattribute__(name)

    def __delattr__(self, name):
        print(f"__delattr__ called: {name}")
        super().__delattr__(name)


obj = Demo()
print(obj.x)         # 触发 __getattribute__,访问已有属性
print(obj.y)         # 触发 __getattribute__ 后找不到,再触发 __getattr__
obj.z = 100          # 触发 __setattr__
del obj.x            # 触发 __delattr__

输出示例:

__setattr__ called: x = 10
__getattribute__ called: x
10
__getattribute__ called: y
__getattr__ called for missing attribute: y
default_for_y
__setattr__ called: z = 100
__delattr__ called: x

2. 自定义容器示例:字典样式访问

class MyDict:
    def __init__(self):
        self.data = {}

    def __setitem__(self, key, value):
        print(f"__setitem__: Setting key {key} = {value}")
        self.data[key] = value

    def __getitem__(self, key):
        print(f"__getitem__: Getting key {key}")
        return self.data[key]

    def __delitem__(self, key):
        print(f"__delitem__: Deleting key {key}")
        del self.data[key]

    def __missing__(self, key):
        print(f"__missing__: Key {key} not found")
        return f"default_for_{key}"

d = MyDict()
d['a'] = 1
print(d['a'])
try:
    print(d['b'])  # 会触发 KeyError,没有自动调用 __missing__
except KeyError:
    print("KeyError caught")

# Python内置dict的__missing__示例:
class MyDictWithMissing(dict):
    def __missing__(self, key):
        print(f"__missing__: Key {key} not found in dict")
        return f"default_for_{key}"

d2 = MyDictWithMissing()
d2['a'] = 10
print(d2['a'])
print(d2['b'])  # 调用 __missing__ 返回默认值

3. 总结

方法调用场景备注
__setattr__obj.attr = val设置属性时调用,重载赋值操作
__getattribute__任何访问属性都会调用用于全局捕获访问,需避免递归调用
__getattr__访问不存在的属性时调用作为找不到属性的“补救”手段
__delattr__del obj.attr删除属性时调用
__setitem__obj[key] = val容器设置元素
__getitem__obj[key]容器访问元素
__delitem__del obj[key]容器删除元素
__missing__字典找不到键时调用(内置dict)只对内置 dict 有效,提供默认值

明白了!我帮你写一个动态代理类示例,演示如何用 __getattr____setattr____getitem____setitem__等魔法方法,实现一个“万能代理对象”,能动态代理属性和字典键的访问与设置,还示范懒加载和日志功能。


动态代理类示例(属性与字典键通吃)

class DynamicProxy:
    def __init__(self, target=None):
        # 真实数据存放处(属性和键值都存在这里)
        super().__setattr__('_data', {})
        # 也可以传入一个初始目标对象,用于代理
        super().__setattr__('_target', target)

    # 属性赋值代理
    def __setattr__(self, name, value):
        print(f"[__setattr__] Setting attribute '{name}' = {value}")
        self._data[name] = value

    # 属性访问代理
    def __getattr__(self, name):
        print(f"[__getattr__] Getting attribute '{name}'")
        if name in self._data:
            return self._data[name]
        # 模拟懒加载
        if self._target:
            print(f"Delegating to target for '{name}'")
            return getattr(self._target, name)
        raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")

    # 删除属性代理
    def __delattr__(self, name):
        print(f"[__delattr__] Deleting attribute '{name}'")
        if name in self._data:
            del self._data[name]
        else:
            raise AttributeError(f"'{type(self).__name__}' object has no attribute '{name}'")

    # 键值赋值代理
    def __setitem__(self, key, value):
        print(f"[__setitem__] Setting key '{key}' = {value}")
        self._data[key] = value

    # 键值访问代理
    def __getitem__(self, key):
        print(f"[__getitem__] Getting key '{key}'")
        if key in self._data:
            return self._data[key]
        if self._target and hasattr(self._target, '__getitem__'):
            print(f"Delegating to target for key '{key}'")
            return self._target[key]
        raise KeyError(key)

    # 键值删除代理
    def __delitem__(self, key):
        print(f"[__delitem__] Deleting key '{key}'")
        if key in self._data:
            del self._data[key]
        else:
            raise KeyError(key)

# 示例用法

proxy = DynamicProxy()

# 属性赋值和访问
proxy.name = "Alice"
print(proxy.name)

# 字典键赋值和访问
proxy['age'] = 30
print(proxy['age'])

# 删除属性
del proxy.name
try:
    print(proxy.name)
except AttributeError as e:
    print(e)

# 删除键值
del proxy['age']
try:
    print(proxy['age'])
except KeyError as e:
    print(e)

# 演示代理另一个对象
class RealObj:
    def __init__(self):
        self.existing = "I exist"
    def __getitem__(self, key):
        return f"Value for {key} from RealObj"

real = RealObj()
proxy2 = DynamicProxy(real)

print(proxy2.existing)      # 通过 __getattr__ 委托
print(proxy2['somekey'])    # 通过 __getitem__ 委托

说明

  • __setattr__ 和 __getattr__ 实现属性动态代理和日志
  • __setitem__ 和 __getitem__ 实现字典键代理和日志
  • 支持懒加载(访问不存在的属性时代理到目标对象)
  • 支持删除属性和键
  • 这是实现 ORM、RPC、远程代理、缓存代理等模式的基础

明白!下面给你示范一个带异步调用代理属性动态计算缓存的高级动态代理类示例。它支持:

  • 属性/方法的异步调用自动转发
  • 计算属性缓存(第一次计算后存储结果,下次直接用缓存)
  • 异步方法支持用 async def 编写的目标方法
  • 同时支持普通属性和字典键访问

高级动态代理类(异步 + 缓存)

import asyncio
import inspect

class AsyncDynamicProxy:
    def __init__(self, target=None):
        super().__setattr__('_data', {})          # 存普通属性/缓存
        super().__setattr__('_target', target)    # 代理目标对象
        super().__setattr__('_cache', {})         # 缓存计算属性

    def __setattr__(self, name, value):
        print(f"[__setattr__] Setting attribute '{name}' = {value}")
        self._data[name] = value

    def __getattr__(self, name):
        print(f"[__getattr__] Getting attribute '{name}'")

        # 优先返回缓存值
        if name in self._cache:
            print(f"Return cached value for '{name}'")
            return self._cache[name]

        # 查找普通存储值
        if name in self._data:
            return self._data[name]

        # 代理目标属性(方法或属性)
        if self._target:
            attr = getattr(self._target, name)

            # 如果是协程函数,返回包装异步调用的函数
            if inspect.iscoroutinefunction(attr):
                async def async_wrapper(*args, **kwargs):
                    print(f"Async proxy calling method '{name}'")
                    result = await attr(*args, **kwargs)
                    # 缓存结果(可选)
                    self._cache[name] = result
                    return result
                return async_wrapper

            # 如果是普通函数,返回同步包装函数
            if callable(attr):
                def sync_wrapper(*args, **kwargs):
                    print(f"Sync proxy calling method '{name}'")
                    result = attr(*args, **kwargs)
                    # 缓存结果(可选)
                    self._cache[name] = result
                    return result
                return sync_wrapper

            # 普通属性直接返回
            self._cache[name] = attr  # 缓存属性值
            return attr

        raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")

    def __delattr__(self, name):
        print(f"[__delattr__] Deleting attribute '{name}'")
        self._data.pop(name, None)
        self._cache.pop(name, None)

    def __setitem__(self, key, value):
        print(f"[__setitem__] Setting key '{key}' = {value}")
        self._data[key] = value

    def __getitem__(self, key):
        print(f"[__getitem__] Getting key '{key}'")
        if key in self._data:
            return self._data[key]
        if self._target and hasattr(self._target, '__getitem__'):
            return self._target[key]
        raise KeyError(key)

    def __delitem__(self, key):
        print(f"[__delitem__] Deleting key '{key}'")
        if key in self._data:
            del self._data[key]
        else:
            raise KeyError(key)

# 测试用的异步目标对象
class RealAsyncObj:
    def __init__(self):
        self.value = 42

    async def async_method(self, x):
        print(f"RealAsyncObj.async_method called with {x}")
        await asyncio.sleep(1)
        return x * 2

    def sync_method(self, y):
        print(f"RealAsyncObj.sync_method called with {y}")
        return y + 10

async def main():
    real = RealAsyncObj()
    proxy = AsyncDynamicProxy(real)

    print(proxy.value)  # 访问属性

    # 调用同步方法
    print(proxy.sync_method(5))

    # 调用异步方法
    result = await proxy.async_method(7)
    print(f"Async method result: {result}")

    # 再调用,测试缓存(这里演示缓存作用)
    print(proxy.value)  # 直接从缓存取
    print(proxy.sync_method(5))  # 缓存结果未清除,直接返回缓存

    # 设置新属性
    proxy.new_attr = 'hello'
    print(proxy.new_attr)

asyncio.run(main())

说明

  • 访问代理的普通属性、方法时自动缓存结果,减少重复计算
  • 对异步方法用 async_wrapper 包装,调用时自动 await,符合异步语义
  • 同步方法用同步包装,自动打印调用日志
  • 支持通过 __setattr__ 动态设置属性,自动写入内部数据字典
  • 通过 _cache 字典缓存计算结果,你可以根据需求自行控制缓存更新策略

明白!下面给你示范一个更完善的动态代理类,支持:

  • 缓存失效策略(基于时间,自动过期)
  • 异步属性懒加载(访问时异步计算并缓存结果)
  • 结合 __call__ 支持函数/方法代理

代码示例

import asyncio
import inspect
import time
from functools import wraps

class AdvancedAsyncProxy:
    def __init__(self, target=None, cache_expire=5):
        super().__setattr__('_data', {})          # 普通属性存储
        super().__setattr__('_target', target)    # 目标对象
        super().__setattr__('_cache', {})         # 缓存数据 {key: (value, expire_timestamp)}
        super().__setattr__('_cache_expire', cache_expire)  # 缓存过期时间秒

    def _is_cache_valid(self, key):
        if key not in self._cache:
            return False
        value, expire = self._cache[key]
        return expire is None or expire > time.time()

    def _set_cache(self, key, value):
        expire = time.time() + self._cache_expire if self._cache_expire else None
        self._cache[key] = (value, expire)

    def __setattr__(self, name, value):
        print(f"[__setattr__] Setting attribute '{name}' = {value}")
        self._data[name] = value
        self._cache.pop(name, None)  # 设置新值,清除缓存

    def __getattr__(self, name):
        print(f"[__getattr__] Getting attribute '{name}'")

        if self._is_cache_valid(name):
            print(f"Return cached value for '{name}'")
            return self._cache[name][0]

        if name in self._data:
            self._set_cache(name, self._data[name])
            return self._data[name]

        if self._target:
            attr = getattr(self._target, name)

            # 支持异步属性懒加载(异步函数)
            if inspect.iscoroutinefunction(attr):
                async def async_wrapper(*args, **kwargs):
                    print(f"Async proxy calling method '{name}'")
                    result = await attr(*args, **kwargs)
                    self._set_cache(name, result)
                    return result
                self._set_cache(name, async_wrapper)
                return async_wrapper

            if callable(attr):
                @wraps(attr)
                def sync_wrapper(*args, **kwargs):
                    print(f"Sync proxy calling method '{name}'")
                    result = attr(*args, **kwargs)
                    self._set_cache(name, result)
                    return result
                self._set_cache(name, sync_wrapper)
                return sync_wrapper

            # 普通属性缓存
            self._set_cache(name, attr)
            return attr

        raise AttributeError(f"'{type(self).__name__}' has no attribute '{name}'")

    def __delattr__(self, name):
        print(f"[__delattr__] Deleting attribute '{name}'")
        self._data.pop(name, None)
        self._cache.pop(name, None)

    def __setitem__(self, key, value):
        print(f"[__setitem__] Setting key '{key}' = {value}")
        self._data[key] = value
        self._cache.pop(key, None)

    def __getitem__(self, key):
        print(f"[__getitem__] Getting key '{key}'")
        if self._is_cache_valid(key):
            return self._cache[key][0]

        if key in self._data:
            self._set_cache(key, self._data[key])
            return self._data[key]

        if self._target and hasattr(self._target, '__getitem__'):
            return self._target[key]
        raise KeyError(key)

    def __delitem__(self, key):
        print(f"[__delitem__] Deleting key '{key}'")
        self._data.pop(key, None)
        self._cache.pop(key, None)

    # 支持函数/方法直接调用代理
    def __call__(self, *args, **kwargs):
        print(f"[__call__] Called with args={args}, kwargs={kwargs}")
        if callable(self._target):
            result = self._target(*args, **kwargs)
            # 简单缓存调用结果,过期时间内复用
            self._set_cache('__call__', result)
            return result
        raise TypeError(f"'{type(self).__name__}' object is not callable")

# 测试用目标对象
class TestObj:
    def __init__(self):
        self.val = 100

    async def async_double(self, x):
        print("TestObj.async_double running...")
        await asyncio.sleep(1)
        return x * 2

    def sync_add(self, y):
        print("TestObj.sync_add running...")
        return y + 10

    def __call__(self, z):
        print("TestObj.__call__ running...")
        return z * z

async def test():
    obj = TestObj()
    proxy = AdvancedAsyncProxy(obj, cache_expire=3)

    print(proxy.val)         # 访问缓存或目标属性
    print(proxy.val)         # 走缓存

    print(proxy.sync_add(5)) # 调用同步方法
    print(proxy.sync_add(5)) # 走缓存

    result = await proxy.async_double(7)  # 调用异步方法
    print(f"Async double result: {result}")
    result = await proxy.async_double(7)  # 走缓存(这里缓存的是 async_wrapper,没再调用目标)

    print(proxy(4))          # 调用 __call__ 代理
    print(proxy(4))          # 走缓存

    # 等待缓存过期
    print("Waiting for cache to expire...")
    await asyncio.sleep(4)

    # 缓存过期后重新调用
    print(proxy.val)
    print(proxy.sync_add(5))
    result = await proxy.async_double(7)
    print(f"Async double after cache expired: {result}")

asyncio.run(test())

核心功能点说明

  • 缓存机制_cache字典存储值和过期时间,访问时检查是否有效
  • 缓存失效:超过 cache_expire 秒自动失效,重新调用目标方法或访问目标属性
  • 异步懒加载:异步函数调用时自动 await,并缓存返回的协程函数包装器
  • __call__支持:代理目标可被当函数调用,结果缓存
  • 同步方法:包装并缓存返回值
  • 缓存清除:属性或键被重新赋值时,清除缓存

需要我帮你把它封装成可配置的库,或者做更复杂的调用链跟踪/日志系统,随时告诉我!