Принудительное использование специализированного подкласса - PullRequest
0 голосов
/ 24 декабря 2018

Я пытаюсь использовать более специализированный класс, когда суперкласс вызывается с определенными параметрами.Конкретно, у меня есть класс Monomial (чей __init__ принимает коэффициент coef и power) и класс Constant.Я хотел бы, чтобы всякий раз, когда Monomial вызывался с power=0, вместо него возвращался экземпляр Constant.

Цель этих классов - создать структуру для генерации "случайных" математических функций.

То, что у меня было изначально:

class Monomial(Function):
    def __init__(self, coef: int, power: int, inner=Identity()):
        super().__init__(inner)
        self.pow = power
        self.coef = coef


class Constant(Monomial):
    def __init__(self, c: int):
        super().__init__(c, 0)

Я попытался добавить следующий __new__ метод:

class Monomial(Function):
    def __new__(cls, coef: int, power: int, inner=Identity()):
        if power == 0:
            return Constant(coef)
        instance = object.__new__(Monomial)
        instance.__init__(coef, power, inner)
        return instance

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

Какой лучший подход для этого?

Ответы [ 2 ]

0 голосов
/ 24 декабря 2018

Способ возврата другого типа класса при вызове класса заключается в переопределении метода __new__, а не __init__.Значение, возвращаемое __new__, является значением, используемым в качестве экземпляра (в отличие от __init__, которому даже не разрешено возвращать значение).Вы начали правильно, но попытка внутри __new__ создать экземпляр подкласса, вызвав его, просто повторно введет Monomial.__new__ - вы, вероятно, получите там ошибку рекурсии.

Итак, хотя Pythonпозволяет изменить тип возвращаемого значения __new__, возможно, вам следует подумать о том, чтобы иметь фабричную функцию, независимую от любого класса, которая вместо этого будет возвращать экземпляр соответствующего класса.Иногда «чем проще, тем лучше».

В любом случае, код для фабричного подхода:

class Monomial(Function):

    def __init__(self, coef: int, power: int, inner=Identity()):
        super().__init__(inner)
        self.pow = power
        self.coef = coef

class Constant(Monomial):
    def __init__(self, c: int):
       super().__init__(self, coef=c, power=0)
       ...

def create_monomial(coef, power):
    if power == 0:
         return Constant(coef)
    return Monomial(coef, power)

(create_monomial также может быть статическим или классовым методом, как вам кажется лучше)

И, есливы действительно думаете, что было бы лучше, способ распутать метод __new__ таков:

class Monomial(Function):
    def __new__(cls, coef: int, power: int = 0, inner=Identity()):
        if power == 0:
            cls = Constant
        return super().__new__(cls)

class Constant(Monomial):
    def __init__(self, c: int, **kw):
        super().__init__(c, 0)

Механизм создания Python будет вызывать __init__, если возвращение __new__ является экземпляром self- так что Monomial и Constant __init__ будут называться правильно.Вам просто нужно исправить __init__ константы, чтобы не нарушать параметр ocasional power = 0, который он получит.

Исправление подписей потребовало бы значительно больше работы и, вероятно, потребовало бы использования метакласса для фактического проглатывания.в его __call__ методе неиспользованные power до константы __init__;

Также обратите внимание, что «реальное исправление» заключается в том, что вызов super().__new__ требует явной передачи класса - в отличие от других случаев использования super(), где «self» или «cls» предоставляются Python.Это связано с тем, что __new__ на самом деле является статическим методом - к которому Python добавляет cls при построении класса, но с помощью других механизмов, отличных от используемых "classmethods".

0 голосов
/ 24 декабря 2018

Как насчет использования factory method подхода?Это хорошая альтернатива, когда точный тип экземпляра должен быть определен динамически.выглядит так:

class Monomial(Function):
    @staticmethod
    def create(coef: int, power: int, inner=Identity()):
        if power == 0:
            return Constant(coef)
        else:
            return Monomial(coef, power)

x = Monomial.create(...)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...