Базовые классы Python Abstract: почему abc не предотвращает создание экземпляров?

Насколько я понял, модуль Python abc должен предотвращать создание экземпляров классов, которые не все @abstractmethod реализованы помеченные методы базового класса (при условии, что базовый класс имеет __metaclass__ = ABCMeta задавать)

Тем не менее, это, кажется, не работает со следующим кодом:

Абстрактный базовый класс:

""" 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

В подклассе отсутствует метод:

""" 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()

Подкласс может быть создан в (довольно сыром) тестовом коде. Почему это так?

Я использую Python 2.7.

1 ответ

Решение

Вы переопределяете __new__; именно этот метод (на object.__new__) что мешает реализации.

Вы не создаете неизменный тип здесь или иным образом изменяете создание нового объекта, поэтому используйте __init__ вместо:

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

Вы использовали __new__ неправильно в любом случае; первый передаваемый аргумент - это класс, а не экземпляр, так как в этот момент не было создано ни одного экземпляра. Переопределением __new__ и не вызывая оригинал, вы а) не создаете экземпляр и б) не запускаете код, который предотвращает создание экземпляра в первую очередь.

С __init__ вместо __new__ экземпляр создает исключение, как и ожидалось:

>>> 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
Другие вопросы по тегам