Установка значений по умолчанию в классе - PullRequest
2 голосов
/ 04 июля 2019

Я создаю класс в Python, и я не уверен, как правильно установить значения по умолчанию. Моя цель - установить значения по умолчанию для всех экземпляров класса, которые также могут быть изменены методом класса. Однако я хотел бы восстановить начальные значения по умолчанию после вызова метода.

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

class plots:
    def __init__(self, **kwargs):
        self.default_attr = {'a': 1, 'b': 2, 'c': 3}
        self.default_attr.update(kwargs)
        self.__dict__.update((k, v) for k, v in self.default_attr.items())

    def method1(self, **kwargs):
        self.__dict__.update((k, v) for k, v in kwargs.items())

        #### Code for this method goes here

        # Then restore initial default values
        self.__dict__.update((k, v) for k, v in self.default_attr.items())

Когда я использую этот класс, я буду делать что-то вроде my_instance = plots() и my_instance.method1(), my_instance.method1(b = 5) и my_instance.method1(). При вызове method1 в третий раз, b будет 5, если я не сбрасываю значения по умолчанию в конце определения метода, но мне бы хотелось, чтобы оно снова было 2.

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

Любое предложение о том, как правильно решить эту проблему?

Ответы [ 3 ]

1 голос
/ 04 июля 2019

Вы можете использовать переменные класса и свойство, чтобы достичь своей цели - установить значения по умолчанию для всех экземпляров класса.Значения экземпляров могут быть изменены напрямую, а начальные значения по умолчанию восстановлены после вызова метода.

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

Вот краткий пример первого подхода с использованием одной переменной класса:

class Plots:

    _a = 1

    def __init__(self):
        self._a = None
        self.reset_default_values()

    def reset_default_values(self):
        self._a = Plots._a

    @property
    def a(self):
        return self._a

    @a.setter
    def a(self, value):
        self._a = value


plot = Plots()
print(plot.a)

plot.a = 42
print(plot.a)

plot.reset_default_values()
print(plot.a)

выход:

1
42
1
0 голосов
/ 04 июля 2019

Существует множество способов решения этой проблемы, но если у вас установлен Python 3.7 (или у вас установлен 3.6 и установлен backport ), классы данных могут подойти. для хорошего решения.

Прежде всего, он позволяет вам определять значения по умолчанию в удобочитаемой и компактной форме, а также позволяет выполнять все необходимые операции мутации:

>>> from dataclasses import dataclass
>>> @dataclass
... class Plots:
...     a: int = 1
...     b: int = 2
...     c: int = 3
...     
>>> p = Plots()        # create a Plot with only default values
>>> p
Plots(a=1, b=2, c=3)
>>> p.a = -1           # update something in this Plot instance
>>> p
Plots(a=-1, b=2, c=3)

Вы также можете бесплатно определить фабрики по умолчанию вместо значений по умолчанию с помощью определения поля класса данных . Возможно, это еще не проблема, но он позволяет избежать изменяемого по умолчанию значения gotcha , с которым рано или поздно сталкивается каждый программист на python.

И последнее, но не менее важное: написать функцию reset довольно легко, учитывая существующий класс данных, поскольку она отслеживает все значения по умолчанию, уже содержащиеся в ее атрибуте __dataclass_fields__:

>>> from dataclasses import dataclass, MISSING
>>> @dataclass
... class Plots:
...     a: int = 1
...     b: int = 2
...     c: int = 3
... 
...     def reset(self):
...         for name, field in self.__dataclass_fields__.items():
...             if field.default != MISSING:
...                 setattr(self, name, field.default)
...             else:
...                 setattr(self, name, field.default_factory())
...
>>> p = Plots(a=-1)     # create a Plot with some non-default values  
>>> p
Plots(a=-1, b=2, c=3)
>>> p.reset()           # calling reset on it restores the pre-defined defaults
>>> p
Plots(a=1, b=2, c=3)

Итак, теперь вы можете написать некоторую функцию do_stuff(...), которая обновляет поля в экземпляре Plot, и пока вы выполняете reset(), изменения не будут сохраняться.

0 голосов
/ 04 июля 2019

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

Вместо того, чтобы иметь self.default_attr, я бы просто вернулся к предыдущемуstate.

Используя декоратор, вы можете получить:

def with_kwargs(fn):
    def inner(self, **kwargs):
        prev = self.__dict__.copy()
        try:
            self.__dict__.update(kwargs)
            ret = fn(self)
        finally:
            self.__dict__ = prev
        return ret
    return inner


class plots:
    a = 1
    b = 2
    c = 3

    def __init__(self, **kwargs):
        self.__dict__.update(kwargs)

    @with_kwargs
    def method1(self):
        # Code goes here

ИМХО, это плохая идея, и, по крайней мере, она предлагает не мутировать plots.Вы можете сделать это, создав новый объект и передав его method1 как self.

class Transparent:
    pass


def with_kwargs(fn):
    def inner(self, **kwargs):
        new_self = Transparent()
        new_self.__dict__ = {**self.__dict__, **kwargs}
        return fn(new_self)
    return inner
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...