如何通过属性函数动态为类添加方法,实现类似SQLAlchemy relationship的效果
实现方案
你要的功能可以通过Python的描述符协议实现,Python 3.6以上版本新增的__set_name__方法可以直接拿到属性所属的类和属性名,完全符合你的需求,不需要提前预知类和属性信息,和SQLAlchemy relationship()的实现思路一致。
方案1:基于__set_name__(推荐,Python 3.6+)
这是最简实现,不需要元类或者父类继承:
class GetterDescriptor: def __init__(self, default=None): self.default = default # 存储属性名和生成的getter方法名,__set_name__调用时赋值 self.attr_name = None self.getter_name = None def __set_name__(self, owner, name): # 类创建时自动调用,owner为所属类,name为属性绑定的变量名 self.attr_name = name self.getter_name = f"get_{name}" # 动态为类添加get_xxx方法 def getter(self): # 这里可以替换为你需要的复杂逻辑,比如get_contact的业务逻辑 return getattr(self, f"_{self.attr_name}", self.default) setattr(owner, self.getter_name, getter) def __get__(self, instance, owner): if instance is None: return self return getattr(instance, f"_{self.attr_name}", self.default) def __set__(self, instance, value): # 赋值时将值存在实例的私有属性中,避免和描述符本身冲突 setattr(instance, f"_{self.attr_name}", value) # 你需要的工具函数 def create_get_method(default=None): return GetterDescriptor(default=default)
调用效果和你的需求完全匹配:
class SomeClass: example = create_get_method(default="初始默认值") obj = SomeClass() print(obj.get_example()) # 输出:初始默认值 obj.example = "自定义值" print(obj.get_example()) # 输出:自定义值 print(obj.example) # 输出:自定义值
方案2:基于元类(兼容Python 3.6以下版本)
如果需要兼容旧版本Python,可以用元类在类创建阶段扫描属性生成方法:
class GetterMeta(type): def __new__(cls, name, bases, attrs): for attr_name, attr_val in attrs.items(): # 识别标记了需要生成getter的属性 if getattr(attr_val, "_is_getter_target", False): getter_name = f"get_{attr_name}" # 生成对应的getter方法 def getter(self, attr=attr_name): # 替换为你的业务逻辑 return getattr(self, attr) attrs[getter_name] = getter return super().__new__(cls, name, bases, attrs) def create_get_method(): # 生成带标记的占位对象,供元类识别 placeholder = object() setattr(placeholder, "_is_getter_target", True) return placeholder
调用示例:
class SomeClass(metaclass=GetterMeta): example = create_get_method() def __init__(self): self.example = "测试值" obj = SomeClass() print(obj.get_example()) # 输出:测试值
原理解释
SQLAlchemy的relationship()本质上也是基于描述符协议实现:返回一个描述符实例,在类创建阶段拿到属性名和所属类信息,之后动态生成关联查询的访问逻辑,和上述实现的核心思路完全一致。
内容的提问来源于stack exchange,提问作者Cyril N.
相关产品推荐
相关产品推荐

