这是一个经典且很容易踩坑的 Python 设计问题,核心矛盾在于:
同一个 B 实例,在不同“使用者(A / C)”里,需要有“互不干扰的附加属性”
为什么「直接给 B 实例加属性」是错误的?
很多人第一反应是:
class A:
def __init__(self, b):
b.id = 1
self.b = b
class C:
def __init__(self, b):
b.id = 2
self.b = b
问题:
-
b是同一个实例 -
Python 的实例属性是 全局可见的
-
后写的会覆盖先写的
b = B()
a = A(b)
c = C(b)
print(a.b.id) # 2 被 C 覆盖
实例属性没有“作用域”概念
正确的核心思路(非常重要)
不要把“属于 A 的状态”存到 B 实例本身
你真正需要的是:
A 和 C 各自维护一份“B → 附加属性”的映射
也就是说:
-
B 只是 key
-
属性属于 A / C 自己
-
彼此完全隔离
方案一(最推荐):在 A 中维护 WeakKeyDictionary
这是最 Pythonic、最安全、最符合你需求的方案
示例代码
from weakref import WeakKeyDictionary
class B:
pass
class A:
def __init__(self):
self._ids = WeakKeyDictionary()
def set_id(self, b, value):
self._ids[b] = value
def get_id(self, b):
return self._ids.get(b)
class C:
def __init__(self):
self._ids = WeakKeyDictionary()
def set_id(self, b, value):
self._ids[b] = value
def get_id(self, b):
return self._ids.get(b)
使用
b = B()
a = A()
c = C()
a.set_id(b, 100)
c.set_id(b, 200)
print(a.get_id(b)) # 100
print(c.get_id(b)) # 200
为什么这是最佳实践?
属性只属于 A / C
不污染 B
不会互相覆盖
WeakKeyDictionary 不会造成内存泄漏(B 被销毁时自动清理)
方案二:给 B 做「命名空间隔离」(不推荐,但可理解)
class A:
def __init__(self, b):
b.__dict__.setdefault('_a_private', {})['id'] = 1
self.b = b
class C:
def __init__(self, b):
b.__dict__.setdefault('_c_private', {})['id'] = 2
self.b = b
print(b._a_private['id']) # 1
print(b._c_private['id']) # 2
问题:
-
强耦合
-
污染 B
-
私有约定靠“自觉”
-
不适合复杂系统
方案三:包装(Wrapper / Proxy)(适合强类型场景)
class AView:
def __init__(self, b):
self.b = b
self.id = 1
class CView:
def __init__(self, b):
self.b = b
self.id = 2
优点:
-
完全隔离
-
非常清晰
缺点:
-
需要额外对象
-
不适合你明确说的“给 B 实例附加属性”
最终结论(最直接的设计建议)
如果你的目标是:
同一个 B 实例
在 A / C 中拥有各自独立的“附加属性”
不相互污染、不覆盖
唯一正确的思路是:
附加属性属于「使用者」,不是被使用对象
推荐使用:
WeakKeyDictionary[B, Any]
这是 Python 官方为这种“外部元数据附加”场景准备的工具。


被折叠的 条评论
为什么被折叠?



