特定案例的混合?
Case-specific mix-ins?
在 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()
从 MyClass
中引用它们,但根据传递给 [=13] 的 case
参数做不同的事情=].
我不确定这是否有意义,或者是否有更好的设计。在我的应用程序中,性能是关键,因此我想在调用 MixinA
和 MixinB
定义的特定于案例的方法时尽可能避免开销。我在某处读到,使用 mixins 对此有好处。但也许有更好的方法。
正如我在评论中所说,这种事情可以使用 metaclass 来完成。以下是如何使用其中之一:
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()
它也可以在没有元class 的情况下完成,至少有几种方法。一种是利用添加到 Python 3.6.
的 __init_subclass__()
特殊方法
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 中 classes 本身就是“first class objects”。
这是一个通过我命名为 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()
如您所见,所有这些技术都受到 class factory pattern 中常见的限制(这或多或少是我们在这里处理的问题)经常做的,即基础或metaclass 方法或指定函数必须知道 subclass 或 mix-in classed 的所有可能性才能使用。
虽然有一些解决方法可以缓解这种情况——至少在使用其他方法时,请参阅我对 的回答,它可以与“class 工厂”模式一起使用。
但是,在这种特定情况下,您可以通过简单地传递混入 class 来避免混入 classes 的硬编码作为参数而不是 case
选择器。
在 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()
从 MyClass
中引用它们,但根据传递给 [=13] 的 case
参数做不同的事情=].
我不确定这是否有意义,或者是否有更好的设计。在我的应用程序中,性能是关键,因此我想在调用 MixinA
和 MixinB
定义的特定于案例的方法时尽可能避免开销。我在某处读到,使用 mixins 对此有好处。但也许有更好的方法。
正如我在评论中所说,这种事情可以使用 metaclass 来完成。以下是如何使用其中之一:
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()
它也可以在没有元class 的情况下完成,至少有几种方法。一种是利用添加到 Python 3.6.
的__init_subclass__()
特殊方法
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 中 classes 本身就是“first class objects”。
这是一个通过我命名为 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()
如您所见,所有这些技术都受到 class factory pattern 中常见的限制(这或多或少是我们在这里处理的问题)经常做的,即基础或metaclass 方法或指定函数必须知道 subclass 或 mix-in classed 的所有可能性才能使用。
虽然有一些解决方法可以缓解这种情况——至少在使用其他方法时,请参阅我对
但是,在这种特定情况下,您可以通过简单地传递混入 class 来避免混入 classes 的硬编码作为参数而不是 case
选择器。