在类中设置默认值

时间:2019-07-03 22:30:03

标签: python class default

我正在用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 个答案:

答案 0 :(得分:1)

您可以使用类变量和属性来实现为所有类实例设置默认值的目标。实例值可以直接修改,调用方法后可以恢复初始默认值。

鉴于“真实类具有数十个默认值”,您可能会考虑的另一种方法是设置包含默认值的配置文件,并使用该文件初始化或重置默认值。

以下是使用一个类变量的第一种方法的简短示例:

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

答案 1 :(得分:0)

您可以使用上下文管理器或装饰器来应用和重置值,而不必在每种方法上键入相同的代码。

我没有回到self.default_attr,而是回到了先前的状态。

使用装饰器,您可以获得:

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

答案 2 :(得分:0)

有很多方法可以解决此问题,但是如果您安装了python 3.7(或安装了the backport并安装了python 3.7),dataclasses可能是一个不错的解决方案的好选择。

首先,它允许您以可读和紧凑的方式定义默认值,还允许您需要的所有变异操作:

>>> 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)

您还可以选择使用dataclass field definition免费定义默认工厂而不是默认值。也许这还不是问题,但是它避免了mutable default value 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(),更改就不会持久。