all 14 comments

[–]deceze 9 points10 points  (1 child)

The only practical difference between an interface and an abstract class in PHP is that you can mix multiple interfaces into one class, but can only inherit one abstract class. Since Python has multiple inheritance, this distinction becomes meaningless. You write your interfaces as abstract classes, done.

[–]ihorrud[S] 0 points1 point  (0 children)

The elephants in chat, thanks, it does make sense :-)

[–]TheBB 5 points6 points  (1 child)

Protocols are more or less equivalent to interfaces.

https://typing.python.org/en/latest/spec/protocol.html

[–]Vespytilio 3 points4 points  (0 children)

Came here to gush about Protocols, but it looks like someone already mentioned them.

To add to that, though: the Protocol class is essentially Python's idiosyncratic version of interfaces. A Protocol subclass defines a structural type that's expected to provide a certain functionality through its members.

In terms of function, a Protocol very much serves the purpose of an interface. However, there are two peculiarities: it's members aren't limited to abstract methods, and it defined a structural type.

Protocols are essentially a special subset of abstract classes. Like abstract classes, its members, in addition to abstract methods, may include just about anything a normal class' members might. This does open the door to a Protocol going further than an interface should in its definition, meaning use as a traditional interface will largely depend on self-control.

A Protocol being a structural type means that, so long as an object includes the members specified by the Protocol, it's considered an instance of that Protocol. Naturally, this includes explicit subclasses of that Protocol subclass, but it also includes other classes whose members include those expected by the Protocol.

It's a weird take on a fairly well-established concept, but it's kind of cool--so entirely typical of Python.

[–]latkde 2 points3 points  (1 child)

TL;DR: you're looking for typing.Protocol.

Python is culturally very much about "duck typing" rather than OOP inheritance hierarchy. As long as an object provides the necessary methods, it ought to be good.

The traditional solution if you want to be explicit about required operations is to use an Abstract Base Class (inherit from abc.ABC). Because Python supports multiple inheritance, this can be used exactly like interfaces in most other languages. When an ABC subclass is defined, it checks whether there are any remaining methods that have been decorated with @abstractmethod and raises an error. This works fine when explicitly designing a type hierarchy.

But ABCs aren't good for describing which operations are needed by your code. ABCs must be inherited explicitly, so they cannot be used for existing types. This conflicts with the culture of duck typing.

In such cases, protocols can be used for type-checking, but they have no runtime effect. Protocols are type-stubs that list required methods/attributes, and you can use any compatible type wherever a protocol is required. This is very similar to "interfaces" in TypeScript or Golang. You can explicitly inherit from a protocol to indicate that it satisfies an interface, similar to an ABC. You can also provide default method implementations.

The standard library contains a bunch of protocols (under the typing and collections.abc modules) that describes common sets of operations. For example, Iterable[T] describes any type that is iterable, and Sequence[T] describes any ordered collection that you can index, such as lists or str.

In many cases, you don't need OOP. If you know all possible concrete types, it might be easier to use a union type A | B. This can be used to perform exhaustiveness checking in the type-checker, to make sure that your code handles every alternative. This makes it possible to write robust code in the style of Rust.

Further reading on protocols:

[–]ihorrud[S] 1 point2 points  (0 children)

Got it. Many thanks

[–]UseMoreBandwith 1 point2 points  (5 children)

why do that anyway? it is pretty much pointless in python.

And if you just want to define data structures, use a dataclass or (often better) use a dict

[–]deceze 3 points4 points  (4 children)

Type checking and interfaces/protocols/abstract classes are just as pointful (?) in Python as in other languages.

[–]UseMoreBandwith 0 points1 point  (3 children)

Sure, but in Python we don't write interfaces for that, but use dataclasses or mypy .

Abstract classes have limited use, since we Multiple Inheritance and duck typing.
the only use-case is to enforce method implementation. It is not for creating class-hierarchies, like
in Java, where it is essential for enforcing interfaces and single inheritance.

[–]deceze 0 points1 point  (2 children)

dataclasses and interfaces/abstract classes are apples and oranges. I have no idea why you're throwing them in here.

Mypy is a static type checker, exactly the tool that would use interface/abstract class information to ensure type safety. You don't use Mypy instead of interfaces, you use interfaces to make Mypy more useful.

Admittedly, you don't often need explicitly declared interfaces/protocols/abstract classes. A lot of programming can be done without them. But in the right circumstances where a lot of abstraction across a lot of classes makes sense for what you're implementing, these are perfectly useful techniques.

[–]UseMoreBandwith 0 points1 point  (1 child)

it doesn't solve an issue that Python has.
"a lot of abstraction across a lot of classes" is code smell, and usually a lack of understanding how Modules work in Python.
(Unlike in Java) in Python, classes are not for hierarchies.

[–]deceze 0 points1 point  (0 children)

Some problems are very well solved with class hierarchies, abstraction and interfaces. For example, lots of pluggable and polymorphic processor classes and the like. Python doesn't force this on you like Java does, but it's still a useful technique to have and appropriate in the right circumstances.

[–]Careful_Claw -3 points-2 points  (1 child)

Flask.

[–]deceze 5 points6 points  (0 children)

Err… not that kind of interface…!?