What to use in replacement of an interface/protocol in python
I wrote a chess game in Python (with tkinter), and the way I did it was to have a Piece class, Queen/Knight/etc. classes that inherit from the Piece class, a Player class, a Square class, and a main program class for tkinter's main loop. Each Piece has a color and location, and a method to help generate move sets for pieces that move in straight lines until blocked. Specific Piece subclasses each contain a method to determine their move set. A Square object contains a Piece and the square's location on the board.
The main program class has an __init__
that sets up the board, places the pieces, loads piece icons and sound effects, and initializes options. A draw_board
method redraws the board, resetting all pieces and rebinding the hotkeys. Then there are various other methods to load new icons, start a new game, set the volume, save, undo, castle, and so on.
I'm not done with version 10 yet, but you can get the source code and assets for version 9 here.
You can also check out the open source Shane's Chess Information Database. I've never used it, but it looks pretty nice.
New in Python 3.8:
Some of the benefits of interfaces and protocols are type hinting during the development process using tools built into IDEs and static type analysis for detection of errors before runtime. This way, a static analysis tool can tell you when you check your code if you're trying to access any members that are not defined on an object, instead of only finding out at runtime.
The typing.Protocol
class was added to Python 3.8 as a mechanism for "structural subtyping." The power behind this is that it can be used as an implicit base class. That is, any class that has members that match the Protocol
's defined members is considered to be a subclass of it for purposes of static type analysis.
The basic example given in PEP 544 shows how this can be used.
from typing import Protocol
class SupportsClose(Protocol):
def close(self) -> None:
# ...
class Resource:
# ...
def close(self) -> None:
self.file.close()
self.lock.release()
def close_all(things: Iterable[SupportsClose]) -> None:
for thing in things:
thing.close()
file = open('foo.txt')
resource = Resource()
close_all([file, resource]) # OK!
close_all([1]) # Error: 'int' has no 'close' method
Note: The typing-extensions
package backports typing.Protocol
for Python 3.5+.
I don't usually use interfaces in Python, but if you have to do it you can use zope.interface
. You can then verify whether classes or objects implement certain interfaces. Also, it can also raise errors if classes don't implement all methods or attributes. Twisted and other frameworks use this library.
In short, you probably don't need to worry about it at all. Since Python uses duck typing - see also the Wikipedia article for a broader definition - if an object has the right methods, it will simply work, otherwise exceptions will be raised.
You could possibly have a Piece
base class with some methods throwing NotImplementedError
to indicate they need to be re-implemented:
class Piece(object):
def move(<args>):
raise NotImplementedError(optional_error_message)
class Queen(Piece):
def move(<args>):
# Specific implementation for the Queen's movements
# Calling Queen().move(<args>) will work as intended but
class Knight(Piece):
pass
# Knight().move() will raise a NotImplementedError
Alternatively, you could explicitly validate an object you receive to make sure it has all the right methods, or that it is a subclass of Piece
by using isinstance or isubclass.
Note that checking the type may not be considered "Pythonic" by some and using the NotImplementedError
approach or the abc
module - as mentioned in this very good answer - could be preferable.
Your factory just has to produce instances of objects having the right methods on them.