【问题标题】:Pass class as function argument and reset class repeatedly within in function将类作为函数参数传递并在函数内重复重置类
【发布时间】:2018-10-04 14:03:01
【问题描述】:

在我的 python 脚本中,我定义了一个类似于以下(当然是假的)类的类:

import copy


class Example:

    def __init__(self, a, b):

        self.a = a
        self.b = b

        self.__default__ = copy.deepcopy(self.__dict__)

        self.t = 0
        self.d = False

    def do(self):

        self.a += self.b - self.t
        self.t += 1
        if self.t == self.b:
            self.d = True
        return self.a

    def reset(self):

        self.__init__(**self.__default__)

现在,我想将此类的一个实例传递给我的主函数,并反复将该实例重置为其默认状态。尽管看了herehereherehere,但我还是无法进行。下面的工作示例给出了所需的结果,但显式地重置了 main 函数中的实例。功能失调的示例是我使用reset 方法使其工作的众多尝试之一。

# working example:
def main(x):

    agg = []
    for i in range(x):
        klass = Example(1, 3)
        while not klass.d:
            a = klass.do()
            agg.append(a)
    return agg

# dysfunctional example:
def main2(klass, x):

    agg = []
    for i in range(x):
        klass.reset()
        while not klass.d:
            a = klass.do()
            agg.append(a)
    return agg

然后main(5)给了

res = main(5)
print(res)
>>> [4, 6, 7, 4, 6, 7, 4, 6, 7, 4, 6, 7, 4, 6, 7]

ex = Example(1, 3)  # default state
res = main2(ex, 5)
print(res)

抛出错误:TypeError: __init__() got an unexpected keyword argument '__default__'

由于我想避免由于不同的原因而不得不在主脚本中重新实例化该类,如果有人可以通过 reset 方法帮助我,我将不胜感激。

【问题讨论】:

  • 你想如何神奇地运行reset方法的功能而不调用它?
  • 您是否(最初)以“默认状态”将对象传递给主函数?
  • @MadPhysicist 我想我一开始并不清楚这个问题。现在我确实在main2 函数中调用了reset 方法。
  • @jedwards 对于这个例子,是的。 Example(1, 3) 是类的默认状态。
  • main2 中的返回 agg 缩进不好?

标签: python class


【解决方案1】:

这样的事情怎么样:

class Example:

    def __init__(self, *args, **kwargs):
        """This stores the default state then init the instance using this default state"""
        self.__default_args__ = args
        self.__default_kwargs__ = kwargs
        self.init(*args, **kwargs)

    def do(self):
        """Do whatever you want """
        self.a += self.b - self.t
        self.t += 1
        if self.t == self.b:
            self.d = True
        return self.a

    def init(self, a, b):
        """Inits the instance to a given state"""
        self.a = a
        self.b = b
        self.t = 0
        self.d = False
        return self

    def reset(self):
        """Resets the instance to the default (stored) state"""
        return self.init(*self.__default_args__, **self.__default_kwargs__)

【讨论】:

  • 我看不出在__init__ 中允许*args**kwargs 的意义,如果您之后将其全部传递给带有签名init(self, a, b) 的可调用对象。除了ab 之外的任何关键字参数都会引发TypeError
  • 只是为了简化。这样,如果您想处理更多参数,您只需更新init() 中的参数。如果您在 __init__(self, a, b) 中定义参数,然后使用意外的参数调用方法,TypeError 也会在那里。
  • 感谢您的回答。 +1,因为它在我给您的示例中完美运行。然而,当我尝试将方法转移到我的主要项目时,class.reset 返回一个NoneObject。这种行为的原因可能是什么?
  • 如果没有明确的 return 子句,.reset() 返回 None。您似乎希望 .reset() 返回重置实例。更新了我的答案
【解决方案2】:

这是一个使用上下文管理器的实现:

class Example:
    def __init__(self, a, b):
        self.a = a
        self.b = b
        self.t = 0
        self.d = False

    def do(self):
        self.a += self.b - self.t
        self.t += 1
        if self.t == self.b:
            self.d = True
        return self.a

    def __enter__(self):
        self._a = self.a
        self._b = self.b

    def __exit__(self, *args):
        self.t = 0
        self.d = False
        self.a = self._a
        self.b = self._b

def main2(klass, x):
    agg = []
    for i in range(x):
        with klass:
            while not klass.d:
                a = klass.do()
                agg.append(a)
    return agg


ex = Example(1, 3)
res = main2(ex, 5)
print(res)

【讨论】:

    【解决方案3】:

    实现此目的的一种可重用方法是实现要继承的 Resettable 类。

    可重置

    class Resettable:
        def __init__(self, *args, **kwargs):
            self.__args__ = args
            self.__kwargs__ = kwargs
    
        def reset(self):
            self.__init__(*self.__args__, **self.__kwargs__)
    

    用法

    使用property 定义一个完全依赖于其他属性的属性也会使重置过程更加顺畅。这种拥有单一事实来源的想法通常对于需要在时间上来回切换的状态很有帮助。

    class Example(Resettable):
        def __init__(self, a=0):
            self.a = a
            super().__init__(a)
    
        def do(self):
            self.a += 1
            return self.a
    
        @property
        def d(self):
            return self.a > 3 # or any other condition
    
    def main(instance, x):
        agg = []
        for _ in range(x):
            instance.reset()
            while not instance.d:
                a = instance.do()
                agg.append(a)
        return agg
    
    print(main(Example(), 3)) # [1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]
    

    Resettable 类的基本假设是传递给构造函数的参数包含重置所需的所有信息,使用属性使该假设更容易满足。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-08-13
      • 1970-01-01
      • 1970-01-01
      • 2019-05-08
      • 2019-07-23
      • 2011-04-07
      • 1970-01-01
      相关资源
      最近更新 更多