2015-01-20 3 views
2

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

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

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

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

ответ

3

Ты главный __new__; именно этот метод (на object.__new__) предотвращает создание экземпляра.

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

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

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

С __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 
+0

Ах, спасибо очень много. Проблемы с кодированием C# на работе и Python у себя дома, я думаю ... У меня также возникло соблазн использовать новое ключевое слово несколько раз для создания экземпляра. –