属性的基本概念

类属性(Class Attribute)

  • 定义:直接定义在类体中的属性,所有实例共享。

  • 特点:

    • 存储在类对象的 __dict__ 中。
    • 实例可访问,但若同名实例属性存在,则实例属性优先。
  • 示例:

    1
    2
    3
    4
    5
    6
    class C:
    kind = "human" # 类属性

    a = C(); b = C()
    a.kind = "robot" # 给实例加了新的属性,不影响类属性
    print(b.kind) # "human"

实例属性(Instance Attribute)

  • 定义:定义在方法(通常是 __init__)内部,绑定到具体实例。

  • 示例:

    1
    2
    3
    class C:
    def __init__(self, name):
    self.name = name # 实例属性

属性的可见性与命名约定

公有属性(public)

无前缀,例如 name,可以被外部直接访问。

受保护属性(protected)

以下划线 _ 开头,如 _cache

  • 表示“内部使用,不推荐外部访问”;
  • 无语法限制,仅是约定。

私有属性(private)

以下双下划线 __ 开头,如 __secret

  • Python 会自动进行名称改写(name mangling)

    • __secret_ClassName__secret
  • 用于避免子类覆盖同名属性。

  • 示例:

    1
    2
    3
    4
    5
    class A:
    def __init__(self):
    self.__x = 10 # 实际为 self._A__x
    a = A()
    print(a._A__x) # 可以访问,但不建议

结论:Python 的“访问修饰符”是一种约定,不是强制机制。

属性修饰器

Python 没有传统意义的“修饰符关键字”,但通过**装饰器(decorator)**实现不同级别的属性行为控制。

@property:把方法当作属性用

  • 用于定义“只读属性”或“受控访问属性”。
1
2
3
4
5
6
7
8
9
10
11
12
13
class Person:
def __init__(self, name):
self._name = name

@property
def name(self): # getter
return self._name

@name.setter
def name(self, value): # setter
if not value:
raise ValueError("name cannot be empty")
self._name = value

优点:安全封装,兼容旧接口,从方法改成属性不影响调用方。

@classmethod:作用于类本身

  • 第一个参数是 cls,而不是 self
  • 典型用途:类属性操作、工厂方法。
1
2
3
4
5
6
class Example:
count = 0

@classmethod
def increment(cls):
cls.count += 1

@staticmethod:类内普通函数

  • self、无 cls 参数。
  • 逻辑上归属类,但不依赖类或实例。
1
2
3
class Math:
@staticmethod
def add(a, b): return a + b

@cached_property:懒加载属性

  • Python 3.8+ 加入。
  • 只计算一次并缓存结果。
  • 用于昂贵或只需计算一次的属性。
1
2
3
4
5
6
7
from functools import cached_property

class Data:
@cached_property
def heavy_value(self):
print("computing...")
return 42

@classproperty:自定义属性

  • Python 标准库无此装饰器,但可自定义:
1
2
3
4
5
6
7
8
9
10
class classproperty:
def __init__(self, func):
self.func = func
def __get__(self, obj, cls):
return self.func(cls)

class Example:
@classproperty
def name(cls):
return cls.__name__

描述符机制

描述符是 Python 属性控制的底层协议,一切属性逻辑(包括 property、classmethod)都基于它。

定义标准

一个类若定义以下任一方法,就成为描述符:

  • __get__(self, instance, owner)
  • __set__(self, instance, value)
  • __delete__(self, instance)

类型

  • 数据描述符:实现了 __set__ / __delete__(优先级高)
  • 非数据描述符:只实现 __get__(优先级低)

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class UpperCase:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
instance.__dict__[self.name] = value.upper()

class Person:
name = UpperCase('name')

p = Person()
p.name = 'alice'
print(p.name) # 'ALICE'

特殊属性访问方法

方法名 触发时机 用途
__getattribute__ 访问任何属性时(最先) 通用拦截(慎用)
__getattr__ 属性未找到时 提供默认值或动态生成
__setattr__ 属性赋值时 自定义赋值逻辑
__delattr__ 删除属性时 自定义删除逻辑

通常只重写 __getattr__ 来支持懒加载动态属性

__slots__ 描述符

1
2
class Point:
__slots__ = ('x', 'y')
  • 禁止动态添加新属性;
  • 节省内存(不再用 __dict__);
  • 不能直接多继承(除非都定义兼容的 slots)。

类型提示与静态约束

修饰符 来源 用途
ClassVar typing 声明该变量为类属性(不用于实例)
Final typing 声明常量,不应被重写(静态检查)
1
2
3
4
5
from typing import ClassVar, Final

class C:
counter: ClassVar[int] = 0
PI: Final = 3.14159

元类控制类属性

元类可以在类创建时动态修改或验证类属性。

1
2
3
4
5
class RegistryMeta(type):
registry = {}
def __init__(cls, name, bases, ns):
super().__init__(name, bases, ns)
RegistryMeta.registry[name] = cls

只读属性与不可变对象

  1. 使用 @property 无 setter;

  2. 使用自定义描述符;

  3. 使用 @dataclass(frozen=True)

    1
    2
    3
    4
    5
    from dataclasses import dataclass
    @dataclass(frozen=True)
    class Point:
    x: int
    y: int

属性查找顺序

当访问 obj.attr 时:

  1. 查找类的 数据描述符__set__ 存在)。
  2. 查找实例字典 obj.__dict__
  3. 查找类或父类的 非数据描述符
  4. 查找类的普通属性。
  5. 若仍未找到 → 调用 __getattr__

综合示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
from typing import ClassVar, Final

class classproperty:
def __init__(self, f):
self.f = f
def __get__(self, obj, cls):
return self.f(cls)

class Upper:
def __init__(self, name):
self.name = name
def __get__(self, instance, owner):
return instance.__dict__.get(self.name)
def __set__(self, instance, value):
instance.__dict__[self.name] = value.upper()

class Person:
kind: ClassVar[str] = "human"
VERSION: Final = 1.0

name = Upper('name')
_internal_cache = {}
__very_private = "top secret"

def __init__(self, name):
self.name = name

@property
def greeting(self):
return f"Hi, {self.name}"

@classmethod
def species(cls):
return cls.kind

@staticmethod
def util(x): return x * 2

@classproperty
def clsname(cls):
return cls.__name__

p = Person("alice")
print(p.greeting) # Hi, ALICE
print(Person.clsname) # Person
print(Person.kind) # human

总结表

类型 关键机制 用途
_name 命名约定 内部使用
__name 名称改写 防止冲突
@property 描述符语法糖 受控访问
@classmethod 类方法 类级操作
@staticmethod 静态函数 工具方法
描述符类 __get__/__set__ 自定义属性行为
__slots__ 限制属性/节省内存 优化
ClassVar / Final 类型提示 静态约束
元类 类创建时控制 高级用法