简体   繁体   English

在类中设置默认值

[英]Setting default values in a class

I am creating a class in Python, and I am unsure how to properly set default values.我正在用 Python 创建一个类,但不确定如何正确设置默认值。 My goal is to set default values for all class instances, which can also be modified by a class method.我的目标是为所有类实例设置默认值,也可以通过类方法进行修改。 However, I would like to have the initial default values restored after calling a method.但是,我希望在调用方法后恢复初始默认值。

I have been able to make it work with the code shown below.我已经能够使用下面显示的代码使其工作。 It isn't very "pretty", so I suspect that are better approaches to this problem.它不是很“漂亮”,所以我怀疑这是解决这个问题的更好方法。

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

When I use this class, I would do something like my_instance = plots() and my_instance.method1() , my_instance.method1(b = 5) , and my_instance.method1() .当我使用这个类时,我会做一些类似my_instance = plots()my_instance.method1()my_instance.method1(b = 5)my_instance.method1() When calling method1 the third time, b would be 5 if I don't reset the default values at the end of the method definition, but I would like it to be 2 again.第三次调用method1时,如果我不在方法定义的末尾重置默认值, b将为 5,但我希望它再次为 2。

Note: the code above is just an example.注意:上面的代码只是一个例子。 The real class has dozens of default values, and using all of them as input arguments would be considered an antipattern.真正的类有几十个默认值,将它们全部用作输入参数将被视为反模式。

Any suggestion on how to properly address this issue?有关如何正确解决此问题的任何建议?

You can use class variables , and property to achieve your goal to set default values for all class instances.您可以使用类变量和属性来实现为所有类实例设置默认值的目标。 The instances values can be modified directly, and the initial default values restored after calling a method.实例值可以直接修改,调用方法后恢复初始默认值。

In view of the context that "the real class has dozens of default values", another approach that you may consider, is to set up a configuration file containing the default values, and using this file to initialize, or reset the defaults.鉴于“真正的类有几十个默认值”的上下文,您可以考虑的另一种方法是设置一个包含默认值的配置文件,并使用该文件进行初始化或重置默认值。

Here is a short example of the first approach using one class variable:这是使用一个类变量的第一种方法的简短示例:

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)

output:输出:

1
42
1

You can use a context manager or a decorator to apply and reset the values without having to type the same code on each method.您可以使用上下文管理器或装饰器来应用和重置值,而无需在每个方法上键入相同的代码。

Rather than having self.default_attr , I'd just return to the previous state.而不是拥有self.default_attr ,我只是回到以前的状态。

Using a decorator you could get:使用装饰器,您可以获得:

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

IMHO this is a bad idea, and would at least suggest not mutating plots .恕我直言,这是一个坏主意,至少建议不要改变plots You can do this by making a new object and passing that to method1 as self .您可以通过创建一个新对象并将其作为self传递给method1来做到这一点。

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

There is a whole bunch of ways to solve this problem, but if you have python 3.7 installed (or have 3.6 and install the backport ), dataclasses might be a good fit for a nice solution.有很多方法可以解决这个问题,但是如果您安装了 python 3.7(或者安装3.6 并安装了 backport ),数据类可能非常适合一个不错的解决方案。

First of all, it lets you define the default values in a readable and compact manner, and also allows all the mutation operations you need:首先,它允许您以可读和紧凑的方式定义默认值,还允许您进行所有需要的变异操作:

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

You also get the option to define default factories instead of default values for free with the dataclass field definition .您还可以选择使用数据类字段定义免费定义默认工厂而不是默认值。 It might not be a problem yet, but it avoids the mutable default value gotcha , which every python programmer runs into sooner or later.这可能还不是问题,但它避免了每个 Python 程序员迟早会遇到的可变默认值 gotcha

Last but not least, writing a reset function is quite easy given an existing dataclass, because it keeps track of all the default values already in its __dataclass_fields__ attribute:最后但并非最不重要的一点是,鉴于现有数据类,编写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)

So now you can write some function do_stuff(...) that updates the fields in a Plot instance, and as long as you execute reset() the changes won't persist.所以现在您可以编写一些函数do_stuff(...)来更新 Plot 实例中的字段,并且只要您执行reset()更改就不会持续存在。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM