Java abstract/interface design in Python
我有许多类,它们都共享相同的方法,只使用不同的实现。在Java中,让每一个类实现一个接口或扩展一个抽象类是有意义的。python有类似的东西吗,或者我应该采取另一种方法吗?
在Python中,界面背后有一些故事。最初的态度是,你不需要它们:python遵循EAFP(请求原谅比请求允许更容易)原则。也就是说,你不需要指定你接受一个,我不知道,一个失败的对象,你只需要在你需要的时候尝试删除这个对象,如果它引发了一个异常,那么它就会引发一个异常。
因此,在这种心态下,你只需单独编写你的类,并按照你的意愿使用它们。如果其中一个不符合要求,那么程序将引发异常;相反,如果用正确的方法编写另一个类,那么它将只起作用,而不需要指定它实现特定的接口。
这很好地工作,但是对于接口,特别是对于更大的软件项目,有明确的用例。python中的最后一个决定是提供
介绍ABC的政治公众人物比我能解释的好得多:
In the domain of object-oriented programming, the usage patterns for
interacting with an object can be divided into two basic categories,
which are 'invocation' and 'inspection'.Invocation means interacting with an object by invoking its methods.
Usually this is combined with polymorphism, so that invoking a given
method may run different code depending on the type of an object.Inspection means the ability for external code (outside of the
object's methods) to examine the type or properties of that object,
and make decisions on how to treat that object based on that
information.Both usage patterns serve the same general end, which is to be able to
support the processing of diverse and potentially novel objects in a
uniform way, but at the same time allowing processing decisions to be
customized for each different type of object.In classical OOP theory, invocation is the preferred usage pattern,
and inspection is actively discouraged, being considered a relic of an
earlier, procedural programming style. However, in practice this view
is simply too dogmatic and inflexible, and leads to a kind of design
rigidity that is very much at odds with the dynamic nature of a
language like Python.In particular, there is often a need to process objects in a way that
wasn't anticipated by the creator of the object class. It is not
always the best solution to build in to every object methods that
satisfy the needs of every possible user of that object. Moreover,
there are many powerful dispatch philosophies that are in direct
contrast to the classic OOP requirement of behavior being strictly
encapsulated within an object, examples being rule or pattern-match
driven logic.On the other hand, one of the criticisms of inspection by classic OOP
theorists is the lack of formalisms and the ad hoc nature of what is
being inspected. In a language such as Python, in which almost any
aspect of an object can be reflected and directly accessed by external
code, there are many different ways to test whether an object conforms
to a particular protocol or not. For example, if asking 'is this
object a mutable sequence container?', one can look for a base class
of 'list', or one can look for a method named 'getitem'. But note
that although these tests may seem obvious, neither of them are
correct, as one generates false negatives, and the other false
positives.The generally agreed-upon remedy is to standardize the tests, and
group them into a formal arrangement. This is most easily done by
associating with each class a set of standard testable properties,
either via the inheritance mechanism or some other means. Each test
carries with it a set of promises: it contains a promise about the
general behavior of the class, and a promise as to what other class
methods will be available.This PEP proposes a particular strategy for organizing these tests
known as Abstract Base Classes, or ABC. ABCs are simply Python classes
that are added into an object's inheritance tree to signal certain
features of that object to an external inspector. Tests are done using
isinstance(), and the presence of a particular ABC means that the test
has passed.In addition, the ABCs define a minimal set of methods that establish
the characteristic behavior of the type. Code that discriminates
objects based on their ABC type can trust that those methods will
always be present. Each of these methods are accompanied by an
generalized abstract semantic definition that is described in the
documentation for the ABC. These standard semantic definitions are not
enforced, but are strongly recommended.Like all other things in Python, these promises are in the nature of a
gentlemen's agreement, which in this case means that while the
language does enforce some of the promises made in the ABC, it is up
to the implementer of the concrete class to insure that the remaining
ones are kept.
我对Python不太熟悉,但我敢打赌它不熟悉。
Java中接口存在的原因是它们指定了一个合同。例如,实现
此外,开发人员和编译器都可以知道这样的方法存在,并且可以在有问题的对象上调用,即使他们不知道它的确切类。这是多态性的一种形式,静态类型需要允许不同的实现类,但仍然知道它们都是合法的。
这在Python中没有真正意义,因为它不是静态类型。您不需要声明对象的类,也不需要说服编译器您调用的方法确实存在。"在duck类型世界中,"接口"与调用方法和信任对象可以适当地处理该消息一样简单。
注意-欢迎更多知识渊博的Python师进行编辑。
也许你可以用这样的东西。这将作为抽象类。因此,每个子类都被强制实现func1()。
1 2 3 4 | class Abstract: def func1(self): raise NotImplementedError("The method not implemented") |
我用3.5+编写了一个库,允许用Python编写接口。
要点是在
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | import inspect def implements(interface_cls): def _decorator(cls): verify_methods(interface_cls, cls) verify_properties(interface_cls, cls) verify_attributes(interface_cls, cls) return cls return _decorator def verify_methods(interface_cls, cls): methods_predicate = lambda m: inspect.isfunction(m) or inspect.ismethod(m) for name, method in inspect.getmembers(interface_cls, methods_predicate): signature = inspect.signature(method) cls_method = getattr(cls, name, None) cls_signature = inspect.signature(cls_method) if cls_method else None if cls_signature != signature: raise NotImplementedError( "'{}' must implement method '{}({})' defined in interface '{}'" .format(cls.__name__, name, signature, interface_cls.__name__) ) def verify_properties(interface_cls, cls): prop_attrs = dict(fget='getter', fset='setter', fdel='deleter') for name, prop in inspect.getmembers(interface_cls, inspect.isdatadescriptor): cls_prop = getattr(cls, name, None) for attr in prop_attrs: # instanceof doesn't work for class function comparison if type(getattr(prop, attr, None)) != type(getattr(cls_prop, attr, None)): raise NotImplementedError( "'{}' must implement a {} for property '{}' defined in interface '{}'" # flake8: noqa .format(cls.__name__, prop_attrs[attr], name, interface_cls.__name__) ) def verify_attributes(interface_cls, cls): interface_attributes = get_attributes(interface_cls) cls_attributes = get_attributes(cls) for missing_attr in (interface_attributes - cls_attributes): raise NotImplementedError( "'{}' must have class attribute '{}' defined in interface '{}'" .format(cls.__name__, missing_attr, interface_cls.__name__) ) def get_attributes(cls): boring = dir(type('dummy', (object,), {})) return set(item[0] for item in inspect.getmembers(cls) if item[0] not in boring and not callable(item[1])) |
然后您可以编写这样的类:
1 2 3 4 5 6 7 8 9 | class Quackable: def quack(self) -> bool: pass @implements(Quackable) class MallardDuck: def quack(self) -> bool: pass |
但下面会给出一个错误:
1 2 3 4 5 6 | @implements(Quackable) class RubberDuck: def quack(self) -> str: pass NotImplementedError: 'RubberdDuck' must implement method 'quack((self) -> bool)' defined in interface 'Quackable' |