python - 特定于案例的混合?
问题描述
python有没有办法根据其他参数继承不同的mixin类?例子:
a = MyClass(case='A') # spawns a class instance which inherits MixinA
b = MyClass(case='B') # spawns a class instance which inherits MixinB
MixinA
两者MixinB
都需要访问数据,MyClass
但本身不托管任何数据。它们都定义了 的不同版本some_method
,这些版本将由self.some_method()
内部引用,但根据传递给的 -argumentMyClass
执行不同的操作。case
MyClass
我不确定这是否有意义或是否有更好的设计。MixinA
在我的应用程序中,性能是关键,所以我想在调用and定义的特定于案例的方法时尽量避免开销MixinB
。我在某处读到使用 mixins 会对此有好处。但也许有更好的方法。
解决方案
正如我在评论中所说,这种事情可以使用元类来完成。以下是如何使用一个:
class MixinA:
def some_method(self):
print('In MixinA.some_method()')
class MixinB:
def some_method(self):
print('In MixinB.some_method()')
class MyMetaClass(type):
mixins = {'A': MixinA, 'B': MixinB} # Supported mix-ins.
def __new__(cls, name, bases, classdict, **kwargs):
case = kwargs.pop('case')
mixin = cls.mixins.get(case)
if not mixin:
raise TypeError(f'Unknown mix-in case selector {case!r} specified')
else:
bases += (mixin,)
return type.__new__(cls, name, bases, classdict, **kwargs)
class ClassA(metaclass=MyMetaClass, case='A'): ...
class ClassB(metaclass=MyMetaClass, case='B'): ...
a = ClassA()
b = ClassB()
a.some_method() # -> In MixinA.some_method()
b.some_method() # -> In MixinB.some_method()
它也可以在没有元类的情况下完成,至少有两种方法。一种是利用__init_subclass__()
添加到 Python 3.6 中的特殊方法。
class MixinA:
def some_method(self):
print('In MixinA.some_method()')
class MixinB:
def some_method(self):
print('In MixinB.some_method()')
class MyClass:
mixins = {'A': MixinA, 'B': MixinB} # Supported mix-ins.
def __init_subclass__(cls, /, case, **kwargs):
super().__init_subclass__(**kwargs)
mixin = cls.mixins.get(case)
if not mixin:
raise TypeError(f'Unknown mix-in case selector {case!r} specified')
else:
cls.__bases__ += (mixin,)
class ClassA(MyClass, case='A'): ...
class ClassB(MyClass, case='B'): ...
a = ClassA()
b = ClassB()
a.some_method() # -> In MixinA.some_method()
b.some_method() # -> In MixinB.some_method()
另一种方法是通过普通函数,这是可能的,因为在 Python 中,类本身就是“第一类对象”。
这是通过我命名的函数执行此操作的示例mixer()
:
class MixinA:
def some_method(self):
print('In MixinA.some_method()')
class MixinB:
def some_method(self):
print('In MixinB.some_method()')
class MyClass: ...
MIXINS = {'A': MixinA, 'B': MixinB} # Supported mix-in classes.
def mixer(cls, *, case):
mixin = MIXINS.get(case)
if not mixin:
raise TypeError(f'Unknown mix-in case selector {case!r} specified')
mixed_classname = f'{cls.__name__}Plus{mixin.__name__}'
return type(mixed_classname, (cls, mixin), {})
ClassA = mixer(MyClass, case='A')
ClassB = mixer(MyClass, case='B')
a = ClassA()
b = ClassB()
a.some_method() # -> In MixinA.some_method()
b.some_method() # -> In MixinB.some_method()
如您所见,所有这些技术都受到类工厂模式(或多或少我们在这里处理的)中常见的限制,即基类或元类方法或指定函数必须知道子类或混合类的所有可能性,以便它们可用。
不过,有一些解决方法可以缓解这种情况——至少在使用其他方法时,请参阅我对不正确使用__new__
生成类的回答?它可以与“类工厂”模式一起使用。
但是,在这种特定情况下,您可以通过简单地将混合类作为参数而不是case
选择器传递来避免混合类的硬编码。
推荐阅读
- java - 如何从给定范围加载日期数组?
- java - java.lang.ClassNotFoundException:用于 Spark 3.0.0 的 org.apache.spark.sql.sources.v2.DataSourceV2
- regex - 使用正则表达式进行 DART 条件查找和替换
- java-8 - 在 Netbeans 中运行项目时如何抑制错误的“Java 版本已过期消息”
- python - 如何在python中将base64字节转换为图像
- javascript - 如何让 async/await 真正等待
- r - 如果另一个变量的下一行满足条件,则定义新变量取 1
- python - Python 类如何在没有在类定义中声明的情况下拥有一个新属性?
- webpack - 为什么要使用 babel-loader,因为 webpack 已经使用了 terser 来缩小 es6+ 代码?
- multithreading - 如何识别/获取循环对象初始化导致Scala死锁的自动提示?