Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Python Abstract Base Classes: Why doesn't abc prevent instantiation?

As far as I understood, the Python module abc should prevent instantiation of classes which have not all @abstractmethod marked methods of the base class implemented (provided that the base class has __metaclass__ = ABCMeta set)

However, this seems not to work with the following code:

Abstract base class:

""" Contains payment processors for executing payments """

from abc import ABCMeta, abstractmethod

class AbstractPaymentProcessor:
    """ Abstract class for executing faucet Payments
    Implement this at your own. Possible implementations include
    online wallets and RPC calls to running dogecoin wallets """

    __metaclass__ = ABCMeta

    @abstractmethod
    def execute_payment(self, destination_address, amount):
        """ Execute a payment to one receiving single address

        return the transaction id or None """
        pass

    @abstractmethod
    def execute_multi_payment(self, destination_addresses, amounts):
        """ Execute a payment to multiple receiving addresses

        return the transaction id or None """
        pass

    @abstractmethod
    def get_transaction_status(self):
        """ Get the status of the transaction

        Indicate if transaction is already confirmed. Return
         - True if confirmed
         - False if unconfirmed
         - None if transaction doesn't exist (or raise exception?)"""
        pass

    @abstractmethod
    def get_available_balance(self):
        """ Get the available balance
        i.e. how much "cash" is in the faucet """
        pass

subclass missing a method:

""" Contains a logging payment processor """

import logging
import random

from AbstractPaymentProcessor import AbstractPaymentProcessor

class DummyLoggingPaymentProcessor (AbstractPaymentProcessor):
    """ Payment processor that does nothing, just logs """

    def __new__(self):
        self._logger = logging.getLogger(__name__)
        self._logger.setLevel(logging.INFO)

    def execute_payment(self, destination_address, amount):
        """ Execute a payment to one receiving single address

        return the transaction id or None """
        raise NotImplementedError("Not implemented yet")

    def execute_multi_payment(self, destination_addresses, amounts):
        """ Execute a payment to multiple receiving addresses

        return the transaction id or None """
        raise NotImplementedError("Not implemented yet")

    def get_transaction_status(self):
        """ Get the status of the transaction

        Indicate if transaction is already confirmed. Return
         - True if confirmed
         - False if unconfirmed
         - None if transaction doesn't exist """
        raise NotImplementedError("Not implemented yet")


if __name__ == '__main__':
    # can instanciate, although get_available_balance is not defined. Why? abc should prevent this!?
    c = DummyLoggingPaymentProcessor()
    c.get_available_balance()

The subclass can be instantiated in the (quite crude) test code. Why is that so?

I'm using Python 2.7.

like image 854
Andreas Duering Avatar asked Jan 20 '15 19:01

Andreas Duering


People also ask

Can abstract functions prevent instantiation of that class?

An abstract method cannot be private. The access modifier of the abstract method should be the same in both the abstract class and its derived class. If you declare an abstract method as protected, it should be protected in its derived class. Otherwise, the compiler will raise an error.

Which class Cannot be instantiated Python?

Abstract base classes cannot be instantiated. Instead, they are inherited and extended by the concrete subclasses. Subclasses derived from a specific abstract base class must implement the methods and properties provided in that abstract base class. Otherwise, an error is raised during the object instantiation.

What is the point of ABC Python?

ABCs provide a formal way to define interfaces in Python, while staying true to the spirit of duck-typing. Besides, this works in a way that honours the Open-Closed Principle.

How do you instantiate an abstract class in Python?

An abstract class cannot be instantiated. It just provides an interface for subclasses to avoid code duplication. It makes no sense to instantiate an abstract class. A derived subclass must implement the abstract methods to create a concrete class that fits the interface defined by the abstract class.


1 Answers

You are overriding __new__; it is this method (on object.__new__) that prevents the instantiation.

You are not creating a immutable type here or otherwise are altering new object creation, so use __init__ instead:

def __init__(self):
    self._logger = logging.getLogger(__name__)
    self._logger.setLevel(logging.INFO)

You were using __new__ wrong in any case; the first argument passed in is the class, not an instance as no instance has been created at that point. By overriding __new__ and not calling the original, you a) are not creating an instance and b) not triggering the code that prevents the instance being created in the first place.

With __init__ instead of __new__ instantiation raises an exception as expected:

>>> class DummyLoggingPaymentProcessor (AbstractPaymentProcessor):
...     """ Payment processor that does nothing, just logs """
...     def __init__(self):
...         self._logger = logging.getLogger(__name__)
...         self._logger.setLevel(logging.INFO)
...     def execute_payment(self, destination_address, amount):
...         """ Execute a payment to one receiving single address
... 
...         return the transaction id or None """
...         raise NotImplementedError("Not implemented yet")
...     def execute_multi_payment(self, destination_addresses, amounts):
...         """ Execute a payment to multiple receiving addresses
... 
...         return the transaction id or None """
...         raise NotImplementedError("Not implemented yet")
...     def get_transaction_status(self):
...         """ Get the status of the transaction
... 
...         Indicate if transaction is already confirmed. Return
...          - True if confirmed
...          - False if unconfirmed
...          - None if transaction doesn't exist """
...         raise NotImplementedError("Not implemented yet")
... 
>>> c = DummyLoggingPaymentProcessor()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: Can't instantiate abstract class DummyLoggingPaymentProcessor with abstract methods get_available_balance
like image 59
Martijn Pieters Avatar answered Sep 22 '22 17:09

Martijn Pieters