Каков наилучший способ разрешить только один из пары аргументов в конструкторе класса Python? - PullRequest
2 голосов
/ 12 марта 2020

У меня есть класс, который по существу функционирует как пример ниже. Конструктор принимает два аргумента, которые по умолчанию равны None, если значение не указано. Цель этого класса заключается в том, чтобы при заданном значении foo пользователь мог вызвать функцию calculateBar() для вычисления значения bar. И наоборот, пользователь должен иметь возможность предоставить значение для bar, а затем вызвать функцию calculateFoo(), чтобы вычислить значение для foo. Примечательно, что эти функции, по сути, являются обратными друг другу.

class ExampleClass:
    def __init__(self, foo=None, bar=None):
        self.foo = foo
        self.bar = bar

        # Essentially an XNOR. Raises an exception if:
        # 1. Both foo and bar are 'None' (no value given for either)
        # 2. Neither foo, nor bar are 'None' (value given for both)
        if bool(foo == None) == bool(bar == None):
            raise ValueError("Please give a value for Foo or Bar (but not both)")

    def calculateFoo(self):
        return self.bar / 2.0

    def calculateBar(self):
        return self.foo * 2.0

Проблема, с которой я столкнулся, заключается в том, что я хочу ограничить класс только тем, чтобы пользователь мог указать значение для foo ИЛИ значение для bar, но не оба, т. Е. Ровно один из них должен содержать значение, а другой должен быть None. Мой текущий метод заключается в том, чтобы просто вызвать исключение в случае, когда либо foo=None and bar=None, либо foo!=None and bar!=None. Это, однако, кажется неэффективным решением. Есть ли лучший способ ограничить пользователя вводом только одного из двух аргументов, но при этом разрешить правильное использование функций calculateFoo() и calculateBar()? Есть ли лучший способ организовать класс, чтобы позволить то, что мне нужно?

Ответы [ 2 ]

3 голосов
/ 12 марта 2020

Как предлагается в комментариях, вы можете разделить ваш класс ExampleClass на два отдельных класса. В данный момент ваш класс пытается сделать foo вещи и bar вещи, но если он не определен, вы оставляете возможность возникновения исключения, потому что один член не определен при вызове calculateFoo() или calculateBar().

То, что вы могли бы сделать, - это иметь абстрактный базовый класс с функцией calculate() и извлечь из него два отдельных класса Foo и Bar. Ваши производные классы будут вынуждены переопределить функцию. Абстрактный базовый класс является просто шаблоном - вы не сможете создать его экземпляр, но он действует как шаблон для производных классов.

Пример:

from abc import ABC, abstractmethod

class BaseClass(ABC):
    @abstractmethod
    def calculate(self):
        pass

class Foo(BaseClass):
    def __init__(self, foo):
        self.foo = foo

    def calculate(self):
        return self.foo / 2.0

class Bar(BaseClass):
    def __init__(self, bar):
        self.bar = bar

    def calculate(self):
        return self.bar * 2.0


foo = Foo(10)
print(foo.calculate())

bar = Bar(10)
print(bar.calculate())

Вывод:

5.0
20.0
1 голос
/ 12 марта 2020

Я думаю, что ваше __init__ в основном нормально, но проверьте оба аргумента first .

Как только вы определили, что было передано только одно значение, установите другое на его основе.

class ExampleClass:
    def __init__(self, foo=None, bar=None):
        if foo is not None and bar is not None:
            raise ValueError("Please give a value for Foo or Bar (but not both)")

        if foo is None:
            self.bar = bar
            self.foo = bar / 2.0
        else:
            self.foo = foo
            self.bar = foo * 2.0

Улучшение будет состоять в том, чтобы обеспечить определенные c методы класса для создания экземпляра для одно значение и не рекомендуется использовать ExampleClass.__new__.

class ExampleClass:

    def __init__(self, foo, bar):
        self.foo = foo
        self.bar = bar

    @classmethod
    def from_foo(cls, value):
        return cls(foo=value, bar=value/2)

    @classmethod
    def from_bar(cls, value):
        return cls(bar=value, foo=value*2)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...