什么是 mixin 以及它有什么用处?
- 2024-11-28 08:38:00
- admin 原创
- 199
问题描述:
在《Python 编程》中,Mark Lutz 提到了mixin这个术语。我的背景是 C/C++/C#,之前从未听说过这个术语。什么是 mixin?
通过阅读这个示例(因为很长,所以我提供了链接)的字里行间,我推测这是一个使用多重继承来扩展类而不是正确子类化的情况。是这样吗?
为什么我要这样做,而不是将新功能放入子类中? 就此而言,为什么混合/多重继承方法比使用组合更好?
混合继承和多重继承有什么区别?这仅仅是语义问题吗?
解决方案 1:
mixin 是一种特殊的多重继承。mixin 主要用在两种情况:
您想为一个班级提供许多可选的功能。
您想在许多不同的类中使用一个特定的功能。
对于第一个例子,请考虑werkzeug 的请求和响应系统。我可以通过以下方式创建一个简单的请求对象:
from werkzeug import BaseRequest
class Request(BaseRequest):
pass
如果我想添加接受标头支持,我会这样做
from werkzeug import BaseRequest, AcceptMixin
class Request(AcceptMixin, BaseRequest):
pass
如果我想创建一个支持接受标头、etags、身份验证和用户代理支持的请求对象,我可以这样做:
from werkzeug import BaseRequest, AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin
class Request(AcceptMixin, ETagRequestMixin, UserAgentMixin, AuthenticationMixin, BaseRequest):
pass
区别很微妙,但在上面的例子中,mixin 类并不是独立存在的。在更传统的多重继承中,AuthenticationMixin
(例如)可能更像Authenticator
。也就是说,类可能被设计为独立存在。
解决方案 2:
首先,你应该注意到 mixin 只存在于多继承语言中。你不能在 Java 或 C# 中使用 mixin。
基本上,mixin 是一种独立的基类型,它为子类提供有限的功能和多态共振。如果您使用 C# 思考,请想象一个您不必实际实现的接口,因为它已经实现;您只需从它继承并受益于其功能。
Mixins 的范围通常较窄,并且不是为了扩展。
[编辑——原因如下:]
既然你问了,我想我应该解释一下原因。最大的好处是你不必一遍又一遍地自己做。在 C# 中,mixin 最大的好处可能就是Disposal 模式。每当你实现 IDisposable 时,你几乎总是想遵循相同的模式,但最终你还是会编写并重写相同的基本代码,只进行微小的改动。如果有一个可扩展的 Disposal mixin,你就可以省去很多额外的输入工作。
[编辑2——回答您的其他问题]
混合继承和多重继承有什么区别?这仅仅是语义问题吗?
是的。混合宏和标准多重继承之间的区别只是语义问题;具有多重继承的类可能会利用混合宏作为多重继承的一部分。
混合的目的是创建一种类型,该类型可以通过继承“混合”到任何其他类型,而不会影响继承类型,同时仍为该类型提供一些有益的功能。
再次思考一个已经实现的接口。
我个人不使用 mixin,因为我主要用不支持 mixin 的语言进行开发,所以我很难想出一个像样的例子来让你“啊哈!”。但我会再试一次。我将使用一个设计出来的例子——大多数语言已经以某种方式提供了该功能——但希望它能解释如何创建和使用 mixin。如下所示:
假设您有一个类型,希望能够将其序列化为 XML 或从 XML 序列化。您希望该类型提供一个“ToXML”方法,该方法返回一个包含 XML 片段的字符串,该片段具有该类型的数据值,以及一个“FromXML”,允许该类型从字符串中的 XML 片段重建其数据值。同样,这是一个人为的示例,因此也许您可以使用文件流,或者语言运行时库中的 XML Writer 类……随便什么都可以。关键是您希望将对象序列化为 XML 并从 XML 中获取一个新对象。
本例中另一个重点是,您希望以通用方式执行此操作。您不希望为要序列化的每种类型都实现“ToXML”和“FromXML”方法,您需要一些通用方法来确保您的类型能够执行此操作并且正常工作。您希望代码重用。
如果您的语言支持它,您可以创建 XmlSerializable mixin 来为您完成工作。此类型将实现 ToXML 和 FromXML 方法。它将使用一些对示例并不重要的机制,能够从与其混合的任何类型中收集所有必要的数据,以构建 ToXML 返回的 XML 片段,并且它同样能够在调用 FromXML 时恢复该数据。
就是这样。要使用它,您需要让任何需要序列化为 XML 的类型继承自 XmlSerializable。每当您需要序列化或反序列化该类型时,只需调用 ToXML 或 FromXML 即可。事实上,由于 XmlSerializable 是一种完全成熟的类型并且具有多态性,因此您可以构建一个对您的原始类型一无所知的文档序列化器,仅接受 XmlSerializable 类型的数组。
现在想象一下将此场景用于其他事物,例如创建一个 mixin 以确保每个混合它的类都会记录每个方法调用,或者创建一个 mixin 为混合它的类型提供事务性。这个列表可以一直列下去。
如果您只是将 mixin 视为一种小型基本类型,旨在为某种类型添加少量功能而不会对该类型产生影响,那么您就成功了。
但愿如此。:)
解决方案 3:
这个答案旨在通过以下示例解释 mixin :
自包含:简短,无需了解任何库即可理解示例。
在 Python 中,而不是在其他语言中。
可以理解,有来自其他语言(例如 Ruby)的示例,因为该术语在这些语言中更为常见,但这是一个Python线程。
它还应审议有争议的问题:
是否需要多重继承来表征 mixin?
定义
我还没有看到来自“权威”来源的引用清楚地说明 Python 中的 mixin 是什么。
我已经看到了 mixin 的 2 种可能定义(如果它们被认为与其他类似概念(例如抽象基类)不同),人们并不完全同意哪一种是正确的。
不同语言之间的共识可能有所不同。
定义1:无多重继承
mixin 是一个类,其中该类的某些方法使用了该类中未定义的方法。
因此,该类不应被实例化,而应作为基类。否则,实例中的方法将无法在不引发异常的情况下调用。
有些资料添加的限制是类不能包含数据,只能包含方法,但我不明白为什么这是必要的。然而在实践中,许多有用的 mixin 没有任何数据,没有数据的基类使用起来更简单。
<=
一个典型的例子是仅从and实现所有比较运算符==
:
class ComparableMixin(object):
"""This class has methods which use `<=` and `==`,
but this class does NOT implement those methods."""
def __ne__(self, other):
return not (self == other)
def __lt__(self, other):
return self <= other and (self != other)
def __gt__(self, other):
return not self <= other
def __ge__(self, other):
return self == other or self > other
class Integer(ComparableMixin):
def __init__(self, i):
self.i = i
def __le__(self, other):
return self.i <= other.i
def __eq__(self, other):
return self.i == other.i
assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)
# It is possible to instantiate a mixin:
o = ComparableMixin()
# but one of its methods raise an exception:
#o != o
这个特殊的例子可以通过装饰器来实现functools.total_ordering()
,但这里的游戏是重新发明轮子:
import functools
@functools.total_ordering
class Integer(object):
def __init__(self, i):
self.i = i
def __le__(self, other):
return self.i <= other.i
def __eq__(self, other):
return self.i == other.i
assert Integer(0) < Integer(1)
assert Integer(0) != Integer(1)
assert Integer(1) > Integer(0)
assert Integer(1) >= Integer(1)
定义2:多重继承
mixin 是一种设计模式,其中基类的某些方法使用其未定义的方法,并且该方法应该由另一个基类实现,而不是像定义 1 中那样由派生类实现。
术语mixin 类是指旨在在该设计模式中使用的基类(TODO 那些使用该方法的类,还是那些实现该方法的类?)
很难判断一个给定的类是否是混合类:该方法可以在派生类上实现,在这种情况下我们回到定义 1。你必须考虑作者的意图。
这种模式很有趣,因为可以通过选择不同的基类来重新组合功能:
class HasMethod1(object):
def method(self):
return 1
class HasMethod2(object):
def method(self):
return 2
class UsesMethod10(object):
def usesMethod(self):
return self.method() + 10
class UsesMethod20(object):
def usesMethod(self):
return self.method() + 20
class C1_10(HasMethod1, UsesMethod10): pass
class C1_20(HasMethod1, UsesMethod20): pass
class C2_10(HasMethod2, UsesMethod10): pass
class C2_20(HasMethod2, UsesMethod20): pass
assert C1_10().usesMethod() == 11
assert C1_20().usesMethod() == 21
assert C2_10().usesMethod() == 12
assert C2_20().usesMethod() == 22
# Nothing prevents implementing the method
# on the base class like in Definition 1:
class C3_10(UsesMethod10):
def method(self):
return 3
assert C3_10().usesMethod() == 13
权威的 Python 事件
在collections.abc 的官方文档中,文档明确使用了术语Mixin Methods。
它指出如果一个类:
实现
__next__
继承自单个类
Iterator
那么该类将免费获得一个__iter__
mixin 方法。
因此至少在文档的这一点上,mixin 不需要多重继承,并且与定义 1 一致。
当然,文档在不同点可能会相互矛盾,并且其他重要的 Python 库可能在其文档中使用其他定义。
本页还使用了术语Set mixin
,这清楚地表明像Set
和这样的类Iterator
可以称为 Mixin 类。
其他语言
Ruby:显然不需要 mixin 进行多重继承,正如《Ruby 编程》和《Ruby 编程语言》等主要参考书中所提到的那样
C++:
virtual
设置的方法=0
是纯虚方法。
定义 1 与抽象类(具有纯虚方法的类)的定义一致。该类无法实例化。
定义 2 可以通过虚拟继承实现:从两个派生类进行多重继承
解决方案 4:
我认为它们是使用多重继承的一种规范的方式 - 因为最终 mixin 只是另一个 python 类,它(可能)遵循有关称为 mixin 的类的约定。
我对于 Mixin 的管理惯例的理解是:
添加方法但不添加实例变量(类常量可以)
仅继承自
object
(在 Python 中)
这样,它就限制了多重继承的潜在复杂性,并且通过限制你必须查看的位置(与完全多重继承相比),可以相当容易地跟踪程序流程。它们类似于ruby 模块。
如果我想添加实例变量(具有比单一继承允许的更大的灵活性),那么我倾向于进行组合。
话虽如此,我已经看到名为 XYZMixin 的类确实有实例变量。
解决方案 5:
我认为先前的回复很好地定义了MixIns。但是,为了更好地理解它们,从代码/实现的角度将MixIns与抽象类和接口进行比较可能会有所帮助:
1.抽象类
需要包含一个或多个抽象方法的类
抽象类 可以包含状态(实例变量)和非抽象方法
2. 界面
接口仅包含抽象方法(没有非抽象方法,也没有内部状态)
3. MixIns
MixIns(类似接口)不包含内部状态(实例变量)
MixIns包含一个或多个非抽象方法(与接口不同,它们可以包含非抽象方法)
例如在 Python 中,这些只是惯例,因为以上所有都是定义为es。但是,抽象类、接口和MixInsclass
的共同特点是它们不应独立存在,即不应被实例化。
解决方案 6:
混合继承和多重继承有什么区别?这仅仅是语义问题吗?
mixin 是多重继承的一种有限形式。在某些语言中,向类添加 mixin 的机制与继承略有不同(就语法而言)。
尤其是在 Python 环境中,mixin 是一个父类,它为子类提供功能,但不打算自行实例化。
您可能会说“这只是多重继承,而不是真正的混合”,因为可能与混合混淆的类实际上可以被实例化和使用 - 所以这确实是一种语义上的,而且非常真实的区别。
多重继承的示例
来自文档的这个例子是一个OrderedCounter:
class OrderedCounter(Counter, OrderedDict): 'Counter that remembers the order elements are first encountered' def __repr__(self): return '%s(%r)' % (self.__class__.__name__, OrderedDict(self)) def __reduce__(self): return self.__class__, (OrderedDict(self),)
它将模块中的Counter
和都子类化。OrderedDict
`collections`
Counter
和都OrderedDict
旨在被实例化并单独使用。但是,通过对它们进行子类化,我们可以拥有一个有序的计数器,并在每个对象中重用代码。
这是一种强大的代码重用方法,但也可能存在问题。如果发现某个对象中存在错误,不小心修复它可能会导致子类中出现错误。
Mixin 示例
Mixins 通常被推崇为一种实现代码重用的方法,避免了协作式多重继承(如 OrderedCounter)可能存在的潜在耦合问题。使用 Mixins 时,您使用的功能与数据之间的耦合度较低。
与上面的例子不同,mixin 并非单独使用。它提供新的或不同的功能。
例如,标准库中socketserver
有几个混合类。
可以使用这些混合类创建每种类型的服务器的分叉和线程版本。例如,ThreadingUDPServer 的创建方式如下:
class ThreadingUDPServer(ThreadingMixIn, UDPServer): pass
首先是混合类,因为它覆盖了 UDPServer 中定义的方法。设置各种属性也会改变底层服务器机制的行为。
在这种情况下,mixin 方法会覆盖对象定义中的方法,UDPServer
以允许并发。
重写的方法似乎是process_request
,它还提供了另一种方法process_request_thread
。以下是来自源代码的:
class ThreadingMixIn: """Mix-in class to handle each request in a new thread.""" # Decides how threads will act upon termination of the # main process daemon_threads = False def process_request_thread(self, request, client_address): """Same as in BaseServer but as a thread. In addition, exception handling is done here. """ try: self.finish_request(request, client_address) except Exception: self.handle_error(request, client_address) finally: self.shutdown_request(request) def process_request(self, request, client_address): """Start a new thread to process the request.""" t = threading.Thread(target = self.process_request_thread, args = (request, client_address)) t.daemon = self.daemon_threads t.start()
一个虚构的例子
这是一个混合类,主要用于演示目的 - 大多数对象将超越这个 repr 的用处:
class SimpleInitReprMixin(object):
"""mixin, don't instantiate - useful for classes instantiable
by keyword arguments to their __init__ method.
"""
__slots__ = () # allow subclasses to use __slots__ to prevent __dict__
def __repr__(self):
kwarg_strings = []
d = getattr(self, '__dict__', None)
if d is not None:
for k, v in d.items():
kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
slots = getattr(self, '__slots__', None)
if slots is not None:
for k in slots:
v = getattr(self, k, None)
kwarg_strings.append('{k}={v}'.format(k=k, v=repr(v)))
return '{name}({kwargs})'.format(
name=type(self).__name__,
kwargs=', '.join(kwarg_strings)
)
用法如下:
class Foo(SimpleInitReprMixin): # add other mixins and/or extend another class here
__slots__ = 'foo',
def __init__(self, foo=None):
self.foo = foo
super(Foo, self).__init__()
用法和样例:
>>> f1 = Foo('bar')
>>> f2 = Foo()
>>> f1
Foo(foo='bar')
>>> f2
Foo(foo=None)
解决方案 7:
Mixins 是编程中的一个概念,其中类提供功能但不用于实例化。Mixins 的主要目的是提供独立的功能,并且最好是 mixins 本身不与其他 mixins 继承,并且避免状态。在 Ruby 等语言中,有一些直接的语言支持,但对于 Python,没有。但是,您可以使用多类继承来执行 Python 中提供的功能。
我观看了此视频http://www.youtube.com/watch?v=v_uKI2NOLEM以了解 mixin 的基础知识。对于初学者来说,了解 mixin 的基础知识、其工作原理以及在实施过程中可能遇到的问题非常有用。
维基百科仍然是最好的: http: //en.wikipedia.org/wiki/Mixin
解决方案 8:
这一概念源自史蒂夫冰淇淋店 (Steve's Ice Cream ),这是史蒂夫·赫雷尔 (Steve Herrell) 于 1973 年在马萨诸塞州萨默维尔创立的一家冰淇淋店,店里会将混合料(糖果、蛋糕等) 混合到基本的冰淇淋口味(香草、巧克力等) 中。受到史蒂夫冰淇淋店的启发,Flavors编程语言的设计者于 1979 年首次将这一概念纳入编程语言,其中混合料是用于增强其他类的小型辅助类,而口味则是大型独立类。
因此,该概念的要点是可重用扩展(“可重用”与“一次性”相对;“扩展”与“基础”相对)。混合类可以是抽象类或具体类,因为混合类具有不完整的接口,而抽象类具有不完整的实现,具体类具有完整的实现。混合类可以以单继承的方式继承,通常用于扩展子类的接口,也可以以多继承的方式继承,通常用于扩展超类的实现。
混合类名通常以“-MixIn”、“-able”或“-ible”作为后缀来强调其性质,例如 Python 标准库中的模块的ThreadingMixIn
和类,以及模块的、、、、和类。ForkingMixIn
`socketserverHashable
IterableCallable
AwaitableAsyncIterable
Reversible`collections.abc
Sized
下面是模块的抽象混合内置类的示例,collections.abc
用于单继承扩展子类的接口Queue
并Stack
使用__len__
特殊方法:
import abc
import collections.abc
class Queue(collections.abc.Sized, metaclass=abc.ABCMeta):
@abc.abstractmethod
def enqueue(self, item):
raise NotImplementedError
@abc.abstractmethod
def dequeue(self):
raise NotImplementedError
class Stack(collections.abc.Sized, metaclass=abc.ABCMeta):
@abc.abstractmethod
def push(self, item):
raise NotImplementedError
@abc.abstractmethod
def pop(self):
raise NotImplementedError
下面是一个具体的混合类的例子,LoggingMixIn
用于多重继承,扩展内置超类的实现list
并dict
具有日志记录功能:
import logging
class LoggingMixIn:
def __setitem__(self, key, value):
logging.info('Setting %r to %r', key, value)
super().__setitem__(key, value)
def __delitem__(self, key):
logging.info('Deleting %r', key)
super().__delitem__(key)
class LoggingList(LoggingMixIn, list):
pass
class LoggingDict(LoggingMixIn, dict):
pass
>>> logging.basicConfig(level=logging.INFO)
>>> l = LoggingList([False])
>>> d = LoggingDict({'a': False})
>>> l[0] = True
INFO:root:Setting 0 to True
>>> d['a'] = True
INFO:root:Setting 'a' to True
>>> del l[0]
INFO:root:Deleting 0
>>> del d['a']
INFO:root:Deleting 'a'
解决方案 9:
我认为这里有一些很好的解释,但我想提供另一种观点。
在 Scala 中,您可以按照此处所述进行 mixin,但非常有趣的是,mixin 实际上是“融合”在一起以创建一种可继承的新类。本质上,您不是从多个类/mixin 继承,而是生成一种具有要继承的 mixin 所有属性的新类。这是有道理的,因为 Scala 基于 JVM,目前不支持多重继承(从 Java 8 开始)。顺便说一下,这种 mixin 类类型是 Scala 中称为 Trait 的特殊类型。
它暗示了类的定义方式:class NewClass extends FirstMixin with SecondMixin with ThirdMixin ...
我不确定 CPython 解释器是否也这么做(混合类组合),但我不会感到惊讶。此外,由于我有 C++ 背景,我不会将 ABC 或“接口”称为与混合等同的东西——这是一个类似的概念,但在使用和实现上有所不同。
解决方案 10:
也许几个例子会有所帮助。
如果您正在构建一个类,并且希望它像字典一样工作,那么您可以定义所有__ __
必要的方法。但这有点麻烦。作为替代方案,您可以只定义几个,然后从UserDict.DictMixin
(在 py3k 中移至)继承(除了任何其他继承之外collections.DictMixin
)。这将自动定义所有其余的字典 api。
第二个示例:GUI 工具包 wxPython 允许您创建具有多列的列表控件(例如,Windows 资源管理器中的文件显示)。默认情况下,这些列表相当基础。您可以添加其他功能,例如通过单击列标题按特定列对列表进行排序,方法是从 ListCtrl 继承并添加适当的混合。
解决方案 11:
这不是一个 Python 示例,但在D 编程语言中,该术语mixin
用于指代以相同方式使用的构造;将一堆东西添加到类中。
在 D 中(顺便说一句,它不执行 MI),这是通过将模板(想想语法感知和安全的宏,你就接近了)插入范围来实现的。这允许类、结构、函数、模块或任何其他东西中的一行代码扩展到任意数量的声明。
解决方案 12:
OP 提到他/她从未听说过 C++ 中的 mixin,可能是因为它们在 C++ 中被称为奇异循环模板模式 (CRTP)。此外,@Ciro Santilli 提到 mixin 是通过 C++ 中的抽象基类实现的。虽然抽象基类可用于实现 mixin,但这是一种过度的做法,因为运行时虚拟函数的功能可以在编译时使用模板实现,而无需在运行时查找虚拟表。
CRTP 模式在此处有详细描述
我已使用以下模板类将@Ciro Santilli 的答案中的 python 示例转换为 C++:
#include <iostream>
#include <assert.h>
template <class T>
class ComparableMixin {
public:
bool operator !=(ComparableMixin &other) {
return ~(*static_cast<T*>(this) == static_cast<T&>(other));
}
bool operator <(ComparableMixin &other) {
return ((*(this) != other) && (*static_cast<T*>(this) <= static_cast<T&>(other)));
}
bool operator >(ComparableMixin &other) {
return ~(*static_cast<T*>(this) <= static_cast<T&>(other));
}
bool operator >=(ComparableMixin &other) {
return ((*static_cast<T*>(this) == static_cast<T&>(other)) || (*(this) > other));
}
protected:
ComparableMixin() {}
};
class Integer: public ComparableMixin<Integer> {
public:
Integer(int i) {
this->i = i;
}
int i;
bool operator <=(Integer &other) {
return (this->i <= other.i);
}
bool operator ==(Integer &other) {
return (this->i == other.i);
}
};
int main() {
Integer i(0) ;
Integer j(1) ;
//ComparableMixin<Integer> c; // this will cause compilation error because constructor is protected.
assert (i < j );
assert (i != j);
assert (j > i);
assert (j >= i);
return 0;
}
编辑:在 ComparableMixin 中添加了受保护的构造函数,以便它只能被继承而不能实例化。更新了示例,以说明在创建 ComparableMixin 对象时受保护的构造函数将如何导致编译错误。
解决方案 13:
如果您可以找到任何其他解决方法(例如,使用组合而不是继承,或者只是将 monkey-patching 方法放入您自己的类中),并且不需要花费太多精力,我建议不要在新的 Python 代码中进行混合。
在旧式类中,您可以使用 mix-in 来从另一个类中获取一些方法。但在新式世界中,所有内容(甚至是 mix-in)都继承自object
。这意味着任何多重继承的使用都会自然而然地引发MRO 问题。
有多种方法可以使多重继承 MRO 在 Python 中工作,最显著的是 super() 函数,但这意味着您必须使用 super() 来完成整个类层次结构,而且理解控制流会困难得多。
解决方案 14:
mixin 提供了一种在类中添加功能的方法,即,您可以通过将模块包含在所需类中来与模块中定义的方法进行交互。虽然 ruby 不支持多重继承,但提供了 mixin 作为实现该目标的替代方案。
这里有一个例子解释如何使用 mixin 实现多重继承。
module A # you create a module
def a1 # lets have a method 'a1' in it
end
def a2 # Another method 'a2'
end
end
module B # let's say we have another module
def b1 # A method 'b1'
end
def b2 #another method b2
end
end
class Sample # we create a class 'Sample'
include A # including module 'A' in the class 'Sample' (mixin)
include B # including module B as well
def S1 #class 'Sample' contains a method 's1'
end
end
samp = Sample.new # creating an instance object 'samp'
# we can access methods from module A and B in our class(power of mixin)
samp.a1 # accessing method 'a1' from module A
samp.a2 # accessing method 'a2' from module A
samp.b1 # accessing method 'b1' from module B
samp.b2 # accessing method 'a2' from module B
samp.s1 # accessing method 's1' inside the class Sample
解决方案 15:
我刚刚使用 Python Mixin 为 Python 邮件过滤器实现了单元测试。通常,邮件过滤器会与 MTA 通信,这使得单元测试变得困难。测试 Mixin 会覆盖与 MTA 通信的方法,并创建一个由测试用例驱动的模拟环境。
因此,您采用未修改的 milter 应用程序(如 spfmilter)和 mixin TestBase,如下所示:
class TestMilter(TestBase,spfmilter.spfMilter):
def __init__(self):
TestBase.__init__(self)
spfmilter.config = spfmilter.Config()
spfmilter.config.access_file = 'test/access.db'
spfmilter.spfMilter.__init__(self)
然后,在 milter 应用程序的测试用例中使用 TestMilter:
def testPass(self):
milter = TestMilter()
rc = milter.connect('mail.example.com',ip='192.0.2.1')
self.assertEqual(rc,Milter.CONTINUE)
rc = milter.feedMsg('test1',sender='good@example.com')
self.assertEqual(rc,Milter.CONTINUE)
milter.close()
http://pymilter.cvs.sourceforge.net/viewvc/pymilter/pymilter/Milter/test.py?revision=1.6&view=markup
解决方案 16:
也许 ruby 中的一个例子可以帮助:
您可以包含mixinComparable
并定义一个函数"<=>(other)"
,mixin提供所有这些功能:
<(other)
>(other)
==(other)
<=(other)
>=(other)
between?(other)
它通过调用<=>(other)
并返回正确的结果来实现这一点。
"instance <=> other"
如果两个对象相等则返回 0,如果instance
大于则返回小于 0,如果大于other
则返回大于 0 。other
解决方案 17:
粗略总结一下上面所有好的答案:
状态 / 方法 | 具体方法 | 抽象方法 |
---|---|---|
具体状态 | 班级 | 抽象类 |
抽象状态 | Mixin | 界面 |
- 2024年20款好用的项目管理软件推荐,项目管理提效的20个工具和技巧
- 2024年开源项目管理软件有哪些?推荐5款好用的项目管理工具
- 2024年常用的项目管理软件有哪些?推荐这10款国内外好用的项目管理工具
- 项目管理软件有哪些?推荐7款超好用的项目管理工具
- 项目管理软件有哪些最好用?推荐6款好用的项目管理工具
- 项目管理软件哪个最好用?盘点推荐5款好用的项目管理工具
- 项目管理软件排行榜:2024年项目经理必备5款开源项目管理软件汇总
- 项目管理软件有哪些,盘点推荐国内外超好用的7款项目管理工具
- 项目管理必备:盘点2024年13款好用的项目管理软件
- 2024项目管理软件排行榜(10类常用的项目管理工具全推荐)