【问题标题】:Python - using a shared variable in a recursive functionPython - 在递归函数中使用共享变量
【发布时间】:2014-07-08 20:44:10
【问题描述】:

我正在使用递归函数对 Python 中的列表进行排序,并且我想在函数继续时跟踪排序/合并的次数。但是,当我在函数内部声明/初始化变量时,它会在函数的每次连续调用中成为局部变量。如果我在函数外部声明变量,则函数认为它不存在(即无法访问它)。如何在函数的不同调用之间共享此值?

我尝试在函数内外使用“全局”变量标记,如下所示:

global invcount  ## I tried here, with and without the global tag

def inv_sort (listIn):
    global invcount   ## and here, with and without the global tag

    if (invcount == undefined):  ## can't figure this part out
        invcount = 0

    #do stuff

但我不知道如何检查全局变量的未定义状态并在第一次递归调用时给它一个值(因为在所有连续的递归中它应该有一个值并被定义)。

我的第一个想法是从函数的每次调用中返回变量,但我不知道如何从函数中传递两个对象,而且我已经必须将列表传递出去以进行递归排序工作。我第二次尝试解决此问题涉及我将变量invcount 添加到我传递的列表中,作为带有标识符的最后一个元素,例如"i27"。然后我可以检查最后一个元素中是否存在标识符(本例中的字母i),如果存在pop(),则在函数调用开始时将其关闭,并在递归期间重新添加它。在实践中,这变得非常复杂,虽然它最终可能会起作用,但我想知道是否有更实用或更简单的解决方案。

有没有办法在不直接传递/返回的情况下共享变量?

【问题讨论】:

  • 你的函数当前返回什么?它会改变listIn 还是创建并返回一个新列表?
  • 好吧,我猜它会返回一个新列表,因为我创建了一个要返回的新列表对象,但它使用输入列表中的相同元素并将它们按排序顺序添加到新列表中。您输入一个列表,该函数按排序顺序输出列表。我现在让它按顺序正确输出列表,但我只是在让它计算排序过程中交换或倒置的数量时遇到了问题,这是我的算法类中的实际分配。

标签: python recursion global-variables return-value


【解决方案1】:

另一种方法可能是使用默认参数,例如:

def inv_sort(listIn, invcount=0):
    ...
    invcount += 1
    ...
    listIn, invcount = inv_sort(listIn, invcount)        
    ...
    return listIn, invcount

这样做的缺点是你的调用会稍微不那么整洁:

l, _ = inv_sort(l) # i.e. ignore the second returned parameter

但这确实意味着 invcount 会在每次使用单个参数调用函数时自动重置(如果需要测试,还提供了注入 invcount 值的机会:assert result, 6 == inv_sort(test, 5))。

【讨论】:

  • 这是一个有趣的想法。我没有考虑将invcount 变量传递给in,只是将它传递出去。我将不得不玩弄它,看看它是如何工作的。
  • 另外,赋值运算符的左侧有一个逗号 - 这是否意味着我理论上可以返回一个元组并将该元组的每个值自动分配给左侧的相应变量?
  • @AliumBritt 是的,这正是正在发生的事情 - invcount 返回一个 2 元组,它被解压缩为两个单独的名称,例如l_.
  • 好的,我将不得不尝试使用该功能。谢谢!
【解决方案2】:

假设您不需要知道函数内部的计数,我会使用装饰器函数来解决这个问题:

import functools

def count_calls(f):
    @functools.wraps(f)
    def func(*args):
        func.count += 1
        return f(*args)
    func.count = 0
    return func

你现在可以装饰你的递归函数了:

@count_calls
def inv_sort(...):
    ...

并在调用它之前或之后检查或重置count

inv_sort.count = 0
l = inv_sort(l)
print(inv_sort.count)

【讨论】:

  • 那么在这种情况下,我的 inv_sort 函数会是 count 函数中的子函数吗?我不确定我是否理解您示例中的所有内容。我从来没有使用过任何使用@ 符号的函数调用或声明。使用@ 到底是什么意思/做什么?
  • @count_callsinv_sort = count_calls(inv_sort) 的语法糖 - “装饰器”函数 (count_calls) 必须将函数作为参数 (f) 并返回函数 (func) . count 不是一个函数——它是func 的一个属性,用于跟踪func 被调用的次数(因此,当func 调用f 时,有多少次f被调用)。
  • @AliumBritt 请注意,我刚刚纠正了一个错字 - count_calls 实际上 没有 return func!
  • 那么,这仅仅是计算函数被调用的次数吗?我实际上需要知道函数内部各个步骤的结果,所以我调用函数的次数不会与这个值直接相关。事实上,我可以多次调用该函数,而无需增加我的内部结果变量invcount
  • @AliumBritt 在这种情况下,这种方法可能对你没有多大用处!
【解决方案3】:

您可以做几件事。以您的示例为例,您应该像这样修改它:

invcount = 0

def inv_sort (listIn):
    global invcount

    invcount += 1

    # do stuff

但是这种方法意味着您应该在每次调用 inv_sort 之前将 invcount 归零。 所以实际上最好将invcount 作为结果的一部分返回。例如使用这样的元组:

def inv_sort(listIn):

    #somewhere in your code recursive call
    recursive_result, recursive_invcount = inv_sort(argument)

    # this_call_invcount includes recursive_invcount
    return this_call_result, this_call_invcount   

【讨论】:

  • 我可以将列表作为元组的一部分返回吗?例如return this_list, this_integer?
  • 是的,当然。任何对象都可以是元组的一部分
  • 好的。另外,为什么这意味着我必须在每个函数调用中将变量归零?我想要运行总数,并使用局部变量将值强制重置为默认值。您是说每次函数调用都会将全局变量重置为 0,还是建议我将其作为最佳实践?
  • 如果要对两个列表进行排序,则必须将 invcount 分配为零,对第一个列表进行排序,将 invcount 值存储在其他变量中。然后将 invcount 设置为零,对第二个列表进行排序并存储其值。否则,在对两个列表进行排序后,您将获得两种函数调用的总和。
  • 不,我只想一次排序一个列表。它实际上是合并排序算法的一个实现,所以真正的代码是这样的:left = whole[:middle] right = whole [middle:] left = inv_sort(left) right = inv_sort(right)。我希望共享inv_count
【解决方案4】:

Python 中不存在“未定义”变量,您也不需要。

在函数外部,将变量设置为 0。在循环内部,使用 global 关键字,然后递增。

invcount = 0
def inv_sort (listIn):
    global invcount

    ... do stuff ...

    invcount += 1

【讨论】:

  • 那么在这种情况下,内部global invcount只保留我的变量名在函数的内部命名空间中?当我改变它时,它会改变同名的外部变量的值吗?
  • 我不确定你在问什么。 global 所做的是告诉解释器“在当前范围内,名称“invcount”指的是全局变量”——因此对该名称的任何赋值都会影响全局变量。
  • 好的,这就是我想知道的。谢谢!
  • 全局变量可以是列表吗?
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多