Подтвердить что ты не робот

Предотвращение класса от прямого экземпляра в Python

У меня есть суперкласс с методом, который вызывает другие методы, которые определены только в его подклассах. Поэтому, когда я создаю экземпляр моего суперкласса и вызываю его метод, он не может найти метод и вызывает ошибку.

Вот пример:

class SuperClass(object):

  def method_one(self):
    value = self.subclass_method()
    print value


class SubClassOne(SuperClass):

  def subclass_method(self):
    return 'subclass 1'


class SubClassTwo(SuperClass):

  def subclass_method(self):
    return 'nubclass 2'


s1 = SubClassOne()
s1.method_one()

s2 = SubClassTwo()
s2.method_one()

c = SuperClass()
c.method_one()

# Results:
# subclass 1
# nubclass 2
# Traceback (most recent call last):
#   File "abst.py", line 28, in <module>
#     c.method_one()
#   File "abst.py", line 4, in method_one
#     value = self.subclass_method()
# AttributeError: 'SuperClass' object has no attribute 'subclass_method'

Я думал об изменении init суперкласса и проверял тип объекта, когда создается новый экземпляр. Если объект принадлежит супер-классу, то повышаем ошибку. Тем не менее, я не слишком уверен, что это питонический способ сделать это.

Любые рекомендации?

4b9b3361

Ответ 1

Вы говорите об абстрактных базовых классах, а язык Python не поддерживает их изначально.

Однако в стандартной библиотеке есть модуль, который вы можете использовать, чтобы помочь вам. Ознакомьтесь с документацией abc.

Ответ 2

Я бы переопределял __new__() в базовом классе и просто не создавал экземпляр вообще, если это базовый класс.

class BaseClass(object):

    def __new__(cls, *args, **kwargs):
        if cls is BaseClass:
            raise TypeError("base class may not be instantiated")
        return object.__new__(cls, *args, **kwargs)

Это отделяет проблемы немного лучше, чем наличие в __init__(), и "не удается быстро".

Ответ 3

Ваш подход является типичным шаблоном структуры.

Использование __init__ для проверки того, что type(self) is not SuperClass является разумным способом убедиться, что SuperClass не был создан напрямую.

Другим общим подходом является предоставление методов заглушки, которые raise NotImplementedError при вызове. Это более надежно, поскольку он также проверяет, что подклассы переопределили ожидаемые методы.

Ответ 4

Это то, что я могу сделать:

class SuperClass(object):
    def __init__(self):
        if type(self) == SuperClass:
            raise Exception("<SuperClass> must be subclassed.")
        # assert(type(self) == SuperClass)

class SubClass(SuperClass):
    def __init__(self):
        SuperClass.__init__(self)

subC = SubClassOne()
supC = SuperClass() # This line should throw an exception

При запуске (исключение выбрано!):

[ 18:32 [email protected] ~/so/python ]$ ./preventing-direct-instantiation.py
Traceback (most recent call last):
  File "./preventing-direct-instantiation.py", line 15, in <module>
    supC = SuperClass()
  File "./preventing-direct-instantiation.py", line 7, in __init__
    raise Exception("<SuperClass> must be subclassed.")
Exception: <SuperClass> must be subclassed.

Изменить (из комментариев):

[ 20:13 [email protected] ~/SO/python ]$ cat preventing-direct-instantiation.py 
#!/usr/bin/python

class SuperClass(object):
    def __init__(self):
        if type(self) == SuperClass:
            raise Exception("<SuperClass> must be subclassed.")

class SubClassOne(SuperClass):
    def __init__(self):
        SuperClass.__init__(self)

class SubSubClass(SubClassOne):
    def __init__(self):
        SubClassOne.__init__(self)

class SubClassTwo(SubClassOne, SuperClass):
    def __init__(self):
        SubClassOne.__init__(self)
        SuperClass.__init__(self)

subC = SubClassOne()

try:
    supC = SuperClass()
except Exception, e:
    print "FAILED: supC = SuperClass() - %s" % e
else:
    print "SUCCESS: supC = SuperClass()"

try:
    subSubC = SubSubClass()
except Exception, e:
    print "FAILED: subSubC = SubSubClass() - %s" % e
else:
    print "SUCCESS: subSubC = SubSubClass()"

try:
    subC2 = SubClassTwo()
except Exception, e:
    print "FAILED: subC2 = SubClassTwo() - %s" % e
else:
    print "SUCCESS: subC2 = SubClassTwo()"

Печать

[ 20:12 [email protected] ~/SO/python ]$ ./preventing-direct-instantiation.py 
FAILED: supC = SuperClass() - <SuperClass> must be subclassed.
SUCCESS: subSubC = SubSubClass()
SUCCESS: subC2 = SubClassTwo()