- Mastering Objectoriented Python
- Steven F. Lott
- 576字
- 2021-11-12 16:25:17
The abc module
The core method of creating ABCs is defined in the abc
module. This module includes the ABCMeta
class that provides several features.
First, the ABCMeta
class assures that abstract classes can't be instantiated. A subclass that provides all of the required definitions, however, can be instantiated. The metaclass will invoke the abstract class's special method, __subclasshook__()
, as a part of processing __new__()
. If that method returns NotImplemented
, then an exception will be raised to show that the class didn't define all the required methods.
Second, it provides definitions for __instancecheck__()
and __subclasscheck__()
. These special methods implement the isinstance()
and issubclass()
built-in functions. They provide the checks to confirm that an object (or a class) belongs to the proper ABC. This includes a cache of subclasses to speed up the testing.
The abc
module also includes a number of decorators for creating abstract method functions that must be provided by a concrete implementation of the abstract base class. The most important of these is the @abstractmethod
decorator.
If we wanted to create a new abstract base class, we would use something like the following:
from abc import ABCMeta, abstractmethod class AbstractBettingStrategy(metaclass=ABCMeta): __slots__ = () @abstractmethod def bet(self, hand): return 1 @abstractmethod def record_win(self, hand): pass @abstractmethod def record_loss(self, hand): pass @classmethod def __subclasshook__(cls, subclass): if cls is Hand: if (any("bet" in B.__dict__ for B in subclass.__mro__) and any("record_win" in B.__dict__ for B in subclass.__mro__) and any("record_loss" in B.__dict__ for B in subclass.__mro__) ): return True return NotImplemented
This class includes ABCMeta
as its metaclass; it also uses the __subclasshook__()
method, which checks for completeness. These provide the core features of an abstract class.
This abstraction uses the abstractmethod
decorator to define three abstract methods. Any concrete subclass must define these in order to be a complete implementation of the abstract base class.
The __subclasshook__
method requires that all of the three abstract methods be provided by a subclass. This is, perhaps, heavy-handed, since a super-simple betting strategy shouldn't have to provide methods for counting wins and losses.
The subclass hook relies on two internal features of a Python class definition: the __dict__
attribute and the __mro__
attribute. The __dict__
attribute is where the method names and attribute names are recorded for a class definition. This is essentially the body of the class. The __mro__
attribute is the method resolution order. This is the sequence of the superclasses of this class. Since Python uses multiple inheritance, there can be many superclasses, and the order of these superclasses determines the precedence for resolving names.
The following is an example of a concrete class:
class Simple_Broken(AbstractBettingStrategy): def bet( self, hand ): return 1
The preceding code can't be built because it doesn't provide necessary implementations for all three methods.
The following is what happens when we try to build it:
>>> simple= Simple_Broken() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: Can't instantiate abstract class Simple_Broken with abstract methods record_loss, record_win
The error message indicates that the concrete class is incomplete. The following is a better concrete class that passes the completeness test:
class Simple(AbstractBettingStrategy): def bet( self, hand ): return 1 def record_win(self, hand): pass def record_loss(self, hand): pass
We can build an instance of this class and use it as part of our simulation.
As we noted earlier, the bet()
method should probably be the only required method. The other two methods should be allowed to default to the single statement pass
.