全部学科
NodeJS全栈
nodejs
Python全栈
python
小程序首页
📅 2026-05-19 8 分钟 ✍️ juanwangdev

Python 属性访问控制

属性访问控制通过魔术方法拦截属性的获取、设置和删除操作。

getattr:属性获取拦截

当访问不存在的属性时触发:

Python
class Dynamic:
    def __getattr__(self, name):
        print(f"获取不存在属性: {name}")
        return f"动态属性 {name}"


d = Dynamic()
d.existing = "存在"
print(d.existing)  # 存在(直接访问,不触发 __getattr__)
print(d.missing)   # 获取不存在属性: missing → 动态属性 missing

惰性加载示例

Python
class LazyLoader:
    def __init__(self):
        self._loaded = {}

    def __getattr__(self, name):
        if name.startswith('get_'):
            key = name[4:]
            if key not in self._loaded:
                print(f"加载 {key}...")
                self._loaded[key] = f"{key} 的数据"
            return lambda: self._loaded[key]
        raise AttributeError(f"无属性 {name}")


loader = LazyLoader()
get_user = loader.get_user  # 加载 user...
print(get_user())           # user 的数据
get_user = loader.get_user  # 不再加载(已缓存)

getattribute:无条件拦截

拦截所有属性访问(包括存在的属性):

Python
class AccessLogger:
    def __getattribute__(self, name):
        print(f"访问属性: {name}")
        return super().__getattribute__(name)  # 必须用 super()


obj = AccessLogger()
obj.x = 10
print(obj.x)  # 访问属性: x → 10

__getattribute__ 会拦截所有访问,必须用 super() 获取真实属性,否则会无限递归。

getattr vs getattribute

方法触发条件
__getattr__属性不存在时
__getattribute__所有属性访问

setattr:属性设置拦截

拦截所有属性赋值:

Python
class Validator:
    def __setattr__(self, name, value):
        if name == 'age' and not isinstance(value, int):
            raise TypeError("age 必须是整数")
        if name == 'age' and value < 0:
            raise ValueError("age 不能为负")
        # 存储到 __dict__ 避免递归
        self.__dict__[name] = value


v = Validator()
v.age = 25     # 正常
v.age = -5     # ValueError
v.age = "25"   # TypeError
v.name = "OK"  # 正常

避免递归的方法

Python
class SafeSetAttr:
    def __setattr__(self, name, value):
        # 方法1:使用 __dict__
        self.__dict__[name] = value

        # 方法2:使用 super()
        super().__setattr__(name, value)

        # 错误:self.name = value 会导致无限递归

delattr:属性删除拦截

Python
class Protected:
    def __init__(self):
        self.protected = True
        self.normal = "可删除"

    def __delattr__(self, name):
        if name == 'protected':
            raise AttributeError("不能删除 protected")
        super().__delattr__(name)


p = Protected()
del p.normal      # 正常删除
del p.protected   # AttributeError

完整示例:代理类

Python
class Proxy:
    "代理类:拦截所有操作"

    def __init__(self, target):
        self._target = target

    def __getattr__(self, name):
        print(f"代理获取: {name}")
        return getattr(self._target, name)

    def __setattr__(self, name, value):
        if name == '_target':
            super().__setattr__(name, value)
        else:
            print(f"代理设置: {name} = {value}")
            setattr(self._target, name, value)

    def __delattr__(self, name):
        print(f"代理删除: {name}")
        delattr(self._target, name)


class RealObject:
    def __init__(self):
        self.data = "原始数据"


real = RealObject()
proxy = Proxy(real)

proxy.data          # 代理获取: data
proxy.new_attr = 10 # 代理设置: new_attr = 10
del proxy.new_attr  # 代理删除: new_attr

完整示例:只读属性

Python
class ReadOnly:
    def __init__(self, data):
        # 初始化时绕过 __setattr__
        self.__dict__['_data'] = data
        self.__dict__['_locked'] = True

    def __setattr__(self, name, value):
        if self.__dict__.get('_locked'):
            raise AttributeError("对象已锁定,不可修改")
        super().__setattr__(name, value)

    def __delattr__(self, name):
        if self.__dict__.get('_locked'):
            raise AttributeError("对象已锁定,不可删除")
        super().__delattr__(name)


obj = ReadOnly({"a": 1})
print(obj._data)    # {"a": 1}
obj._data = {}      # AttributeError
del obj._data       # AttributeError

完整示例:类型检查

Python
class Typed:
    "带类型检查的类"

    _types = {
        'name': str,
        'age': int,
        'score': (int, float)
    }

    def __setattr__(self, name, value):
        if name in self._types:
            expected = self._types[name]
            if not isinstance(value, expected):
                raise TypeError(f"{name} 必须是 {expected}")
        super().__setattr__(name, value)


class Student(Typed):
    def __init__(self, name, age, score):
        self.name = name
        self.age = age
        self.score = score


s = Student("Alice", 20, 95.5)
s.age = "twenty"  # TypeError: age 必须是 int
s.extra = "OK"    # 不检查不在 _types 中的属性

属性查找顺序

Python
1. __getattribute__(无条件拦截)
2. data descriptor(描述符 __get__)
3. 实例 __dict__
4. non-data descriptor(描述符 __get__)
5. 类 __dict__ 及继承链
6. __getattr__(不存在时触发)

与 property 的区别

方式适用场景灵活度
@property单个属性的精细控制简单清晰
__getattr__动态属性、代理全局拦截
__setattr__批量验证、类型检查全局拦截
text
# property 方式:针对单个属性
class Person:
    @property
    def age(self):
        return self._age

    @age.setter
    def age(self, value):
        if value < 0:
            raise ValueError("age 不能为负")
        self._age = value


# __setattr__ 方式:全局拦截
class Person2:
    def __setattr__(self, name, value):
        if name == 'age' and value < 0:
            raise ValueError("age 不能为负")
        self.__dict__[name] = value

要点总结

方法触发时机注意事项
__getattr__属性不存在不拦截已存在属性
__getattribute__所有访问必须用 super() 防递归
__setattr__所有赋值存入 dict 防递归
__delattr__所有删除用 super() 删除

__setattr____getattribute__ 内部必须避免直接访问 self 属性,否则会无限递归。

D:\git2\jwdev\articles\PYTHON\进阶\面向对象编程\属性访问控制.md

📝 发现内容有误?点击此处直接编辑

← 上一篇 Python 多态与动态绑定
下一篇 → Python 抽象基类 ABC
想查看更多题目和详细解析?
小程序提供完整的题库、模拟考试和详细解析
马上就来

长按或扫描二维码,立即体验

扫码体验小程序
马上就来
使用微信扫描二维码
立即体验完整题库