Java abstract/interface design in Python

2024/11/20 7:05:03

I have a number of classes which all share the same methods, only with different implementations. In Java, it would make sense to have each of these classes implement an interface or extend an abstract class. Does Python have anything similar to this, or should I be taking an alternative approach?

Answer

There's a bit of a story behind interfaces in Python. The original attitude, which held sway for many years, is that you don't need them: Python works on the EAFP (easier to ask forgiveness than permission) principle. That is, instead of specifying that you accept an, I don't know, ICloseable object, you simply try to close the object when you need to, and if it raises an exception then it raises an exception.

So in this mentality you would just write your classes separately, and use them as you will. If one of them doesn't conform to the requirements, your program will raise an exception; conversely, if you write another class with the right methods then it will just work, without your needing to specify that it implements your particular interface.

This works pretty well, but there are definite use cases for interfaces, especially with larger software projects. The final decision in Python was to provide the abc module, which allows you to write abstract base classes i.e. classes that you can't instantiate unless you override all their methods. It's your decision as to whether you think using them is worth it.

The PEP introducing ABCs explain much better than I can:

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.

https://en.xdnf.cn/q/26347.html

Related Q&A

PyCharm - no tests were found?

Ive been getting na error in PyCharm and I cant figure out why Im getting it:No tests were foundThis is what I have for my point_test.py: import unittest import sys import ossys.path.insert(0, os.path.…

Does Python PIL resize maintain the aspect ratio?

Does PIL resize to the exact dimensions I give it no matter what? Or will it try to keep the aspect ratio if I give it something like the Image.ANTIALIAS argument?

How to scale images to screen size in Pygame

I was wondering how I would go about scaling the size of images in pygame projects to the resolution of the screen. For example, envisage the following scenario assuming windowed display mode for the t…

GridSearch for an estimator inside a OneVsRestClassifier

I want to perform GridSearchCV in a SVC model, but that uses the one-vs-all strategy. For the latter part, I can just do this:model_to_set = OneVsRestClassifier(SVC(kernel="poly"))My problem …

The Pythonic way of organizing modules and packages

I come from a background where I normally create one file per class. I organize common classes under directories as well. This practice is intuitive to me and it has been proven to be effective in C++,…

Where do you need to use lit() in Pyspark SQL?

Im trying to make sense of where you need to use a lit value, which is defined as a literal column in the documentation.Take for example this udf, which returns the index of a SQL column array:def find…

Evaluate multiple scores on sklearn cross_val_score

Im trying to evaluate multiple machine learning algorithms with sklearn for a couple of metrics (accuracy, recall, precision and maybe more).For what I understood from the documentation here and from t…

Generate SQL statements from a Pandas Dataframe

I am loading data from various sources (csv, xls, json etc...) into Pandas dataframes and I would like to generate statements to create and fill a SQL database with this data. Does anyone know of a way…

How to translate a model label in Django Admin?

I could translate Django Admin except a model label because I dont know how to translate a model label in Django Admin. So, how can I translate a model label in Django Admin?

converty numpy array of arrays to 2d array

I have a pandas series features that has the following values (features.values)array([array([0, 0, 0, ..., 0, 0, 0]), array([0, 0, 0, ..., 0, 0, 0]),array([0, 0, 0, ..., 0, 0, 0]), ...,array([0, 0, 0, …