Python非排他重叠继承优化:如何按实例参数动态提供方法属性?
问题描述
想要实现:类实例仅在参数满足特定非互斥条件时拥有对应属性与方法,规则如下:
- 基类
polygon的属性和方法始终可用 - 第一个参数
vert等于3时,拥有_triangle子类的属性和方法 - 第二个参数
axis非None时,拥有_solid子类的属性和方法
目前已经有可行实现(包含ShadowRanger的建议),但想找更优雅的写法——最好能移除_triangle_solid这个组合类;同时搞不懂为啥有的方法要给axis设默认参数,有的不用。
现有实现代码:
class polygon(): def __new__(cls, vert, axis=None): triangle = vert == 3 solidrev = axis is not None if triangle and not solidrev: return super().__new__(_triangle) elif solidrev and not triangle: return super().__new__(_solid) elif solidrev and triangle: return super().__new__(_triangle_solid) else: return super().__new__(cls) def __init__(self, vert, axis=None): self.polygon_attribute = 1 def polygon_method(self): print('polygon') class _triangle(polygon): def __init__(self, vert, axis=None): super().__init__(vert, axis) self.triangle_attribute = 2 def triangle_method(self): print('triangle') class _solid(polygon): def __init__(self, vert, axis): super().__init__(vert, axis) self.solid_attribute = 3 def solid_method(self): print('solid of revolution') class _triangle_solid(_triangle, _solid): def __init__(self, vert, axis): super().__init__(vert, axis)
参数组合示例:
P = polygon(2) P = polygon(2,axis=0) P = polygon(3) P = polygon(3,axis=0)
一、更优雅的实现:移除_triangle_solid类
你可以用**动态混入(Dynamic Mixins)**的思路,不用手动定义所有组合子类,而是根据参数条件动态构建类。这样不管条件怎么组合,都能自动生成对应的实例类型。
实现方案一:动态生成类并手动初始化
class polygon(): def __init__(self, vert, axis=None): self.polygon_attribute = 1 def polygon_method(self): print('polygon') class _triangle: def __init__(self, vert, axis=None): self.triangle_attribute = 2 def triangle_method(self): print('triangle') class _solid: def __init__(self, vert, axis): self.solid_attribute = 3 def solid_method(self): print('solid of revolution') def polygon(vert, axis=None): # 收集需要混入的父类 bases = [polygon] if vert == 3: bases.append(_triangle) if axis is not None: bases.append(_solid) # 用参数组合生成唯一类名,避免重复创建相同类 class_name_parts = [] if vert == 3: class_name_parts.append('triangle') if axis is not None: class_name_parts.append('solid') class_name = f"polygon_{'_'.join(class_name_parts)}" if class_name_parts else 'polygon' # 动态创建类 DynamicPolygon = type(class_name, tuple(bases), {}) # 实例化并逐个调用父类的初始化方法 instance = DynamicPolygon.__new__(DynamicPolygon) for base in bases: if base == polygon: base.__init__(instance, vert, axis) elif base == _triangle: base.__init__(instance, vert, axis) elif base == _solid: base.__init__(instance, vert, axis) return instance
实现方案二:利用Mixin和MRO自动初始化
这种方式更简洁,靠Python的方法解析顺序(MRO)自动调用父类初始化:
class polygon(): def __init__(self, vert, axis=None): self.polygon_attribute = 1 def polygon_method(self): print('polygon') class TriangleMixin: def __init__(self, vert, axis=None): super().__init__(vert, axis) self.triangle_attribute = 2 def triangle_method(self): print('triangle') class SolidMixin: def __init__(self, vert, axis=None): # 统一加默认值,避免MRO调用报错 super().__init__(vert, axis) self.solid_attribute = 3 def solid_method(self): print('solid of revolution') def polygon(vert, axis=None): # 按顺序添加Mixin到基类列表头部 bases = [polygon] if axis is not None: bases.insert(0, SolidMixin) if vert == 3: bases.insert(0, TriangleMixin) # 动态生成类 cls = type(f"DynamicPolygon_{vert}_{axis}", tuple(bases), {}) return cls(vert, axis)
这两种方式都能自动处理所有参数组合,不用再手动写_triangle_solid这类组合类,后续加新条件也只需要新增Mixin类,改几行判断逻辑就行。
二、关于axis默认参数的疑问
为啥有的方法要给axis设默认值,有的不用?核心看这个方法的调用场景:
- 基类
polygon和_triangle的__init__需要处理axis为None的情况——比如当你只传vert=3,没传axis时,会调用这两个类的初始化方法,要是没默认参数,就会因为缺参数报错。 - 原来的
_solid类只有在axis非None时才会被实例化(现有代码里__new__只在axis is not None时返回_solid或_triangle_solid),所以它的__init__不用设默认参数——因为调用的时候肯定已经传了有效的axis值,不会缺参数。
不过在动态混入的实现里,建议给所有Mixin的__init__都加上axis=None的默认值,避免因MRO顺序问题导致参数传递错误,让代码更健壮。
内容的提问来源于stack exchange,提问作者Gerrit Nowald
相关产品推荐
相关产品推荐

