【问题标题】:Object not "resetting" in Jupyter Notebook unless I reset the kernel除非我重置内核,否则 Jupyter Notebook 中的对象不会“重置”
【发布时间】:2018-07-13 13:34:41
【问题描述】:

这是一个奇怪的问题,我不完全确定如何恰当地提出这个问题,但我会尽力而为。

我有一个自定义类,它基本上是一个 API 的包装器,它在每次调用时使用新数据更新 SQLite 数据库(我无法将它添加到问题中,因为它是庞大且私有的)。

奇怪的是,似乎某些信息正在被缓存(我认为这是不可能的,但这是唯一让我想起的事情,例如当您在 web dev 中进行编辑并且它们没有更新时),因为它第一次工作,但是当我尝试重新初始化对象并再次运行它时,它不会向数据库添加任何新数据(当我知道要添加新数据时)。

我知道代码有效,因为如果我重新启动内核并再次运行它,它会更新没有问题。

我已尝试删除对象 (del InitializedClass)、重新初始化并使用不同的值进行初始化,但似乎没有任何效果。除非重新启动内核,否则它不会更新数据库。

有人遇到过这样的问题吗?如果这还不够,我很乐意提供更多信息,但我不知道如何描述它。

谢谢!!


编辑

下面的伪代码基本上就是发生了什么

from something import SomeClass    

while True:

    obj = SomeClass() #      <---------  How can I "reset" this on each loop?

    obj.get_new_data_from_api()
    obj.update_raw_db()
    obj.process_raw_data()
    obj.update_processed_db()

    # i tried different combinations of deleting the object
    del obj
    del SomeClass
    from something import SomeClass

编辑 2:

所以正如大家提到的,这是类本身的问题,但我仍然不明白为什么会发生错误。基本上,当我将 datetime.now() 函数调用作为默认 kwarg 时(即使在删除类并创建了一个新实例,这没有更新)。问题如下图所示:

class SomeBrokenClass():

    def __init__(self):    
        pass

    def get_endpoint(self, start, end):
        return 'https://some.api.com?start_date=%s&end_date=%s' % (start, end)

    # THE PROBLEM WAS WITH THIS METHOD ( .get_data() ):
    # When re-initializing the class, the `end` argument
    # was not being updated for some reason. Even if I completely
    # delete the instance of the class, the end time would not update.

    def get_data(self, start, end = int(datetime.now().timestamp() * 1000)):
        return pd.read_json(self.get_endpoint(start, end))

    def get_new_data_from_api(self):
        start_date = self.get_start_date()
        df = self.get_data(start_date)
        return df


class SomeWorkingClass():

    def __init__(self):    
        pass

    def get_endpoint(self, start, end):
        return 'https://some.api.com?start_date=%s&end_date=%s' % (start, end)

    def get_data(self, start, end):
        return pd.read_json(self.get_endpoint(start, end))

    def get_new_data_from_api(self):
        start_date = self.get_start_date()
        end_date = int(datetime.now().timestamp() * 1000) # BUT THIS WORKS FINE
        df = self.get_data(start_date, end_date)
        return df

【问题讨论】:

  • 很可能是您的代码,我们看不到,所以不确定您希望如何获得帮助?
  • 但是为什么每次内核重启都能正常运行的代码呢?
  • "但是为什么每次内核重启都能正常工作的代码呢?" - 我不知道答案,但这可能是您代码中的错误。我建议您将代码简化为显示问题的工作示例。
  • @MitchWheat 好的,我尝试更新基本情况
  • @MitchWheat 我想我主要要问的是,不应该删除对象然后重新初始化它与重新启动内核做同样的事情吗?是否可以将信息缓存在笔记本中?

标签: python python-3.x class ipython jupyter-notebook


【解决方案1】:

您的问题与您的一种方法中参数的默认值有关:

def get_data(self, start, end = int(datetime.now().timestamp() * 1000)):
    ...

每次调用函数时都不会重新计算默认值。相反,作为默认值给出的表达式仅在定义方法时计算一次,并且该值被存储以用作所有后续调用的默认值。这在这里不起作用,因为它仅在加载模块时评估 datetime.now,而不是每次调用函数时。

解决这个问题的常用方法是设置一个类似None的标记值作为默认值,然后如果找到标记,则在函数内部计算适当的值:

def get_data(self, start, end=None):
    if end is None:
        end = int(datetime.now().timestamp() * 1000)
    ...

【讨论】:

  • 我正要自己写这个答案。荣誉。
  • 谢谢!对我来说,这似乎是一个相当奇怪的怪癖。您会认为在每次新函数调用时,都会重新评估所有代码。 特别是当创建类的新实例时。但我认为如果在模块加载时设置它是有意义的。非常有趣的东西!我确实花了大约 18 个小时试图找出问题所在哈哈,再次感谢您的帮助:)
  • 初学 Python 的程序员通常会在尝试使用可变默认值(如空列表)作为稍后修改的参数(例如,通过调用 @ 987654325@ 列表的方法)。像def foo(x=[]) 这样声明的函数始终使用与默认参数相同的列表,因此第一个之后的调用将看到所有先前调用中添加到列表中的值。您遇到的情况并不常见,但根源相同。
【解决方案2】:

您不是“删除对象然后重新初始化它”——您是从全局命名空间中删除模块,然后将其添加回来。这不会重新执行模块的代码:

# test.py
print("Hi!")

>>> import test
Hi!
>>> del test
>>> import test
<Nothing printed>

如果你想重新加载你的模块,你需要明确地这样做,如this question

>>> import importlib
>>> importlib.reload(test)
Hi!
<module 'test' from '/Users/rat/test.py'>

(编辑以添加以下内容)但是,您在这里尝试做的事情永远不是必需的。在创建它的新实例之前,您永远不需要删除一个类。如果像这样重新加载模块有帮助,我能想到的唯一原因是:

  1. 第二次创建 SomeClass 实例时未发生的行为实际上是由 something 模块顶层的代码引起的 - 也就是说,在任何函数或类定义之外,或者
  2. SomeClass 在其自己的类属性中记录某些内容,并在第二次实例化时选择不做某事。

在这两种情况下,我会采取的方法是找到只执行一次的代码并将其提取到一个函数中,以便您可以在需要时直接调用它。抱歉含糊不清,但没有您的代码,很难更精确。我的赌注是第一个,可能还有其他情况,但这至少可以给你一个开始。

【讨论】:

  • 谢谢!这行得通,但是在每次迭代时重新加载模块似乎有点草率(我只是尝试将其作为最后的努力,因为我不确定还能做什么)。我仍然不明白为什么运行obj = SomeClass() 不会重新初始化/重新启动/重新创建类/对象。我一直认为,每次您这样做时,它都会用该类的新实例覆盖该变量(尤其是,当您事先使用del 时)。这不正确吗?再次感谢您的帮助
  • 永远不需要在重新分配变量之前删除它,或者在创建它的新实例之前删除一个类。如果您的代码在您重新加载模块时有效,但在其他情况下无效,那么您的 something 代码中一定有一些有趣的东西。如果您提供,我们可以进一步确定。
  • 我刚刚在您的编辑中看到了您的“大量和私人”评论,所以我猜这个选项已经失效。我已经编辑了我的答案,以提供更多关于如何理解正在发生的事情的想法。并且要清楚 - 以这种方式工作不仅仅是“有点草率”,这完全是奇怪的。我已经使用 python 将近二十年了,我从来没有做过类似的事情。如果你能弄清楚你的代码中有什么奇怪的地方,我敢肯定这是没有必要的。
  • 感谢您的帮助,非常感谢!我实际上想通了并将其添加到我的第二次编辑中。这是班级的问题。出于某种原因,datetime.now() 时间戳在每次迭代时都没有更新(即使我完全删除了实例!)。这是一个怪癖吗?当函数调用作为 kwarg 的默认值包含在内时,哪些函数调用不会更新?
  • 取决于您所说的“怪癖”。一个函数参数只能有一个默认值!创建函数时设置默认值。另一个答案有更多细节。
猜你喜欢
  • 2021-07-19
  • 1970-01-01
  • 2023-01-13
  • 1970-01-01
  • 2017-07-26
  • 1970-01-01
  • 1970-01-01
  • 2021-04-01
  • 1970-01-01
相关资源
最近更新 更多