【问题标题】:Preferred way of resetting a class in Python在 Python 中重置类的首选方式
【发布时间】:2018-01-29 15:13:13
【问题描述】:

基于this post on CodeReview

我有课 Python (3) 中的Foo,其中当然包括__init__() 方法。这个类触发几个提示并做它的事情。假设我希望能够重置Foo,这样我就可以重新开始该过程。

首选的实现是什么?

再次调用__init__()方法

def reset(self):
    self.__init__()

还是创建一个新实例?

def reset(self):
    Foo()

如果reset 被多次调用,我不确定创建Foo 的新实例是否会留下任何可能影响性能的东西。另一方面,如果不是所有属性都在__init__() 中(重新)定义,__init__() 可能会产生副作用。

有没有首选的方法来做到这一点?

【问题讨论】:

  • “你做错了”。没有“重置类”模式是有原因的:当您处理完一个类实例中的数据后,您将处理该实例并创建另一个实例。
  • @jsbueno 如果我没记错的话,这是第二个选项的作用。
  • 不是真的 - 如果出于某种原因你希望你的班级只有一个实例 - 这些是要做的事情 - 这是很少需要的(如果需要,应该小心在__new__ 方法中)。只需创建一个新实例 - 如果没有对前一个实例的其他引用,它将自然丢弃。换句话说:普通代码不会“手动”调用__init__ - 只需创建一个新实例并允许语​​言这样做。
  • 我想指出,“创建和处理新实例”方法有时会导致内存使用量激增,尤其是在像 python 这样的语言中,您无法使用函数自行清理像 .Dispose() 或 .Free() 并且必须信任垃圾收集器。想象一下,您需要创建数千个、数百万个该类,或者只创建一个并重用。不仅内存使用量会有天壤之别,而且速度也会更快,因为每次都没有新的内存分配。所以...请不要使用“一种方法涵盖所有​​”的课程。

标签: python python-3.x class


【解决方案1】:

您可以将使用的实例保存在类属性中。 每当您要重置类时,请为该属性重新分配一个新实例。 以下是我将如何实现这种方法:

class Foo:
    instance = None    # The single instance

    def __init__(self, ...):
        # Initialize the instance if Foo.instance does not exist, else fail
        if type(self).instance is None:
            # Initialization
            type(self).instance = self
        else:
            raise RuntimeError("Only one instance of 'Foo' can exist at a time")

    @classmethod
    def reset(cls):
        cls.instance = None        # First clear Foo.instance so that __init__ does not fail
        cls.instance = Foo(...)    # Now the initialization can be called

然后,您只需引用Foo.instance即可访问该实例。

我选择将reset 作为类方法,因此由@classmethod 修饰。 使用这个装饰器,可以通过调用Foo.reset()来重置实例,并且cls参数会自动传递给方法。

与您建议的方法相比,我更喜欢这种方法(这或多或少是一种单例模式),因为在您的情况下,拥有Foo 的单个实例似乎是合乎逻辑的,因为您想重置时间>它。 因此,我发现“强制”使用单个实例相当直观。

另一方面,您可以在类之外有一个实例,并使用定义的reset 实例方法:

def reset(self):
    self.__init__()

但这可能效果不佳。 假设您想在 __init__ 方法之外设置属性。 调用__init__ 不会重置这些属性。 因此,您的实例不会按预期重置。 现在,如果您持有一个实例并将其重新分配给一个全新的实例,您可以确定它绝对是干净的。

关于你所说的“创建一个新实例”,这或多或少是我选择的,但问题是在哪里存储它。 我认为在课堂上保持温暖是有意义的。

顺便说一句,这个解决方案不应该有任何性能问题(如“内存泄漏”),因为一次只引用一个 Foo 实例,创建一个新实例将取消引用上一个。

【讨论】:

  • 我没有这样想过。您能否评论一下为什么/如果这比我在问题中提到的方法更可取?
  • 当然。谢谢!
  • 代码审查问题中的OP,这很有意义,所以谢谢你的解释!
【解决方案2】:

两者都是正确的,但语义的实现方式不同。

为了能够重置实例,我会这样写(我更喜欢从__init__ 调用自定义方法而不是相反,因为__init__ 是一种特殊方法,但这主要是个人喜好问题):

class Foo:
    def __init__(self):
        self.reset()
    def reset(self):
        # set all members to their initial value

你这样使用它:

Foo foo      # create an instance
...
foo.reset()  # reset it

从头开始创建一个新实例实际上更简单,因为该类不必实现任何特殊方法:

Foo foo      # create an instance
...
foo = Foo()  # make foo be a brand new Foo

如果旧实例未在其他任何地方使用,则会被垃圾回收

两种方式都可以用于所有初始化都在__init__ 中完成的普通 类,但第二种方式需要在创建时使用__new__ 自定义的特殊类,例如不可变的类。


但请注意,这段代码:

def reset(self):
    Foo()

不会做你想做的事:它只会创建一个新实例,然后立即删除它,因为它会在方法结束时超出范围。即使self = Foo() 只会在方法结束时设置同样会超出范围(并且新实例被销毁)的本地引用。

【讨论】:

  • 这种方法是首先想到的自然解决方案,但是......即使它在 python 中确实有效并且确实有效,但许多 IDE 在代码完成方面会遇到问题,因为它们会寻找类成员仅在 init 中。只是一个注释。
【解决方案3】:

正如其他人所说,您不需要重置课程。但如果你真的想要,你可以使用工厂函数,每次调用都会返回一个新类。

def make_Foo():
    class Foo():
        VALUE = 1
    return Foo

Foo = make_Foo()
Foo.VALUE = 2
print(Foo.VALUE)  # prints 2

Foo = make_Foo()  # reset
print(Foo.VALUE)  # prints 1

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-03-08
    • 1970-01-01
    • 1970-01-01
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多