Python 使函数在第一次调用和后续调用之间表现不同

Python 使函数在第一次调用和后续调用之间表现不同,python,Python,正如您所看到的,我有一个硬编码的升级值设置为true。在这个脚本中,它总是进行tftp下载 如何在第一次迭代时将脚本更改为执行tftp下载,在下一次迭代中调用函数download时,它执行http下载?如果我正确理解您的意思,您只需要执行文本第一次调用即可执行thtp,您可以将upgrade设置为全局变量。然后,当调用下载函数时,您将其全局设置为False,并在后续调用中保持这种方式 注意,这个答案是基于对OP问题的某种解读。我假设调用此函数的脚本不知道该函数被调用了多少次 def downlo

正如您所看到的,我有一个硬编码的升级值设置为true。在这个脚本中,它总是进行tftp下载


如何在第一次迭代时将脚本更改为执行tftp下载,在下一次迭代中调用函数download时,它执行http下载?

如果我正确理解您的意思,您只需要执行文本第一次调用即可执行thtp,您可以将upgrade设置为全局变量。然后,当调用下载函数时,您将其全局设置为False,并在后续调用中保持这种方式

注意,这个答案是基于对OP问题的某种解读。我假设调用此函数的脚本不知道该函数被调用了多少次

def download():
    upgrade = True
    if upgrade:
        # do a download using tftp
    else:
        # do a download via HTTP

如果我对您的理解正确的话,您只需要第一次调用文字来执行thtp,您就可以将upgrade设置为全局变量。然后,当调用下载函数时,您将其全局设置为False,并在后续调用中保持这种方式

注意,这个答案是基于对OP问题的某种解读。我假设调用此函数的脚本不知道该函数被调用了多少次

def download():
    upgrade = True
    if upgrade:
        # do a download using tftp
    else:
        # do a download via HTTP

按如下方式重新构造代码:

upgrade = True

def download():
  global upgrade
  if upgrade:
     print('do a download using tftp')
     upgrade = False
  else:
     print('do a download using http')

download() # do a download using tftp
download() # do a download using http
在第二次迭代中,调用download时,使用upgrade=False作为参数:

def download(upgrade=True):

  if upgrade:
     do a download using tftp
  else:
     do a download via HTTP

按如下方式重新构造代码:

upgrade = True

def download():
  global upgrade
  if upgrade:
     print('do a download using tftp')
     upgrade = False
  else:
     print('do a download using http')

download() # do a download using tftp
download() # do a download using http
在第二次迭代中,调用download时,使用upgrade=False作为参数:

def download(upgrade=True):

  if upgrade:
     do a download using tftp
  else:
     do a download via HTTP
您可以使用闭包,也就是说,有一个返回的内部函数,这将保留一个外部状态。这假设python 3:

download(False)  # download via HTTP
给出:

def init_download():
    upgrade = True

    def inner():
        nonlocal upgrade
        if upgrade:
            print('do a download using tftp')
            upgrade = False
        else:
            print('do a download via HTTP')

    return inner

download = init_download()

download()
download()
download()
您可以使用闭包,也就是说,有一个返回的内部函数,这将保留一个外部状态。这假设python 3:

download(False)  # download via HTTP
给出:

def init_download():
    upgrade = True

    def inner():
        nonlocal upgrade
        if upgrade:
            print('do a download using tftp')
            upgrade = False
        else:
            print('do a download via HTTP')

    return inner

download = init_download()

download()
download()
download()

可能您可以也应该将此逻辑置于函数之外,但如果您希望每次都传递相同的参数,但在第一次调用后仍更改行为,则可以使用:


使用itertools.count而不是说一个列表的优点是,您不会在每次调用时积累内存。

可能您可以而且应该在函数之外使用此逻辑,但是如果您希望每次都传递相同的参数,但在第一次调用后仍然更改行为,则可以使用:


使用itertools.count而不是说一个列表的优点是,您不会在每次调用中积累内存。

您可以用以下方式构造代码:

from itertools import count 

def download(c=count()):
    if next(c) == 0:
        print('tftp')
    else:
        print('http')

download()
# tftp
download()
# http
download()
# http
download()
# http

这将通过它们一次,然后退出程序。print语句只是一个指南,用于向您展示正在发生的事情以及何时结束。

您可以用以下方式构造代码:

from itertools import count 

def download(c=count()):
    if next(c) == 0:
        print('tftp')
    else:
        print('http')

download()
# tftp
download()
# http
download()
# http
download()
# http

这将通过它们一次,然后退出程序。打印语句只是一个指南,用于向您展示发生了什么以及何时结束。

为了完整起见,这里是课堂解决方案:

def download(upgrade = False):

    while upgrade == False:
            print("HTTP")
            break
    print("TFTP")

download()

这允许您以非黑客的方式跨调用存储状态。

为完整起见,以下是类解决方案:

def download(upgrade = False):

    while upgrade == False:
            print("HTTP")
            break
    print("TFTP")

download()

这允许您以非黑客的方式跨调用存储状态。

因为@Alex的答案很好,但构建实例需要额外的步骤。您还需要全局存储实例

所以我做了一些改变。然后可以导入它并将其用作函数

util.py

class Download(object):
    def __init__(self):
        self.executed = False

    def __call__(self):
        print('http' if self.executed else 'tftp')
        self.executed = True

download = Download()

download()  # tftp
download()  # http
download()  # http
main.py使用它

class Downloader(object):
  _upgrade = False

  @classmethod
  def call(cls):
    if cls._upgrade:
      print('upgrade via http')
    else:
      print('download via tft')
      cls._upgrade = True

def download():
  Downloader.call()

这可能不是线程安全的。如果您在不同的进程中运行它,例如webserver后端,那么最好使用远程存储作为标记,例如redis/oss

因为@Alex的答案很好,但构建实例需要额外的步骤。您还需要全局存储实例

所以我做了一些改变。然后可以导入它并将其用作函数

util.py

class Download(object):
    def __init__(self):
        self.executed = False

    def __call__(self):
        print('http' if self.executed else 'tftp')
        self.executed = True

download = Download()

download()  # tftp
download()  # http
download()  # http
main.py使用它

class Downloader(object):
  _upgrade = False

  @classmethod
  def call(cls):
    if cls._upgrade:
      print('upgrade via http')
    else:
      print('download via tft')
      cls._upgrade = True

def download():
  Downloader.call()

这可能不是线程安全的。如果您在不同的进程中运行它,例如webserver后端,那么最好使用远程存储作为标记,例如redis/oss

首先。。。只调用一个实例?还是希望将状态实际持久化到存储器,以便更改跨越程序的多个运行?如果是后者,则需要实现从磁盘/数据库/任何东西读写的代码。。。只调用一个实例?还是希望将状态实际持久化到存储器,以便更改跨越程序的多个运行?如果是后者,您将需要实现从磁盘/数据库/任何地方读写的代码?这符合OP的要求。这不是一个好的实践,但这是在OP上。如果你认为某件事不是好的实践,你可以也应该投反对票,不管OP问什么。那么一个不起作用并且有语法错误的代码会被投上4次票?怎么做?@wim如果有人犯了一个简单的语法错误,请发表评论,以便他们能够修复它。不值得投反对票。@Barmar不告诉其他用户如何使用他们的反对票。顺便说一句,我会-1这个想法,即使它是正确地写在第一位。我


如果可以,我会投-2票。为什么投反对票?这符合OP的要求。这不是一个好的实践,但这是在OP上。如果你认为某件事不是好的实践,你可以也应该投反对票,不管OP问什么。那么一个不起作用并且有语法错误的代码会被投上4次票?怎么做?@wim如果有人犯了一个简单的语法错误,请发表评论,以便他们能够修复它。不值得投反对票。@Barmar不告诉其他用户如何使用他们的反对票。顺便说一句,我会-1这个想法,即使它是正确地写在第一位。如果可以的话,我会-2。OP说“迭代”,意思是函数被多次调用,但他从一开始就不知道调用的次数。这实际上并不能解决在第一次调用和后续调用之间更改升级开关的问题;它只是一条指令,让调用者在函数调用之间完成这项工作。作为最佳实践,这是正确的做法吗?是的,绝对是。它回答了这个问题吗。。。呃……重读这个问题,我倾向于同意。但我想确认一下,对我来说还是有点模糊。这是唯一的答案,是朝着正确的方向前进。但我会更进一步,只编写两个不同的函数,如download_tftp和download_http,并将使用的逻辑保留在函数本身之外。@wim,关于functools.partial如何分割这两个配置?OP说“迭代,'这意味着函数被多次调用,但他从一开始就不知道调用的次数。这实际上并不能解决在第一次调用和后续调用之间更改升级开关的问题;它只是一条指令,让调用者在函数调用之间完成这项工作。作为最佳实践,这是正确的做法吗?是的,绝对是。它回答了这个问题吗。。。呃……重读这个问题,我倾向于同意。但我想确认一下,对我来说还是有点模糊。这是唯一的答案,是朝着正确的方向前进。但我会更进一步,只编写两个不同的函数,如download_tftp和download_http,并将要使用的逻辑保留在函数本身之外。@wim,functools.partial如何拆分这两种配置?OP问的是一些可以用默认参数轻松解决的问题,你想向他介绍闭包吗?@ZWiki,嗯?默认参数只是将工作推到外部范围;它不能解决这个问题,这是在调用中保持状态,除非你把它变成一个可变的——这比这更为骇人听闻。我不认为这是一个特别困难的关闭,因为它们去了,我想这是一个很简单的介绍。哦,我误解了这个问题。我认为闭包是一种很好的方法,因为我们没有C静态等价物——1种总的存储状态的方法,因为您无法访问状态。如果你想要的话,最好使用一个可调用类。OP问的问题可以用默认参数轻松解决,你想把他介绍给闭包吗?@ZWiki,嗯?默认参数只是将工作推到外部范围;它不能解决这个问题,这是在调用中保持状态,除非你把它变成一个可变的——这比这更为骇人听闻。我不认为这是一个特别困难的关闭,因为它们去了,我想这是一个很简单的介绍。哦,我误解了这个问题。我认为闭包是一种很好的方法,因为我们没有C静态等价物——1种总的存储状态的方法,因为您无法访问状态。如果需要,最好使用可调用类。-1这是另一种存储全局state@Alex谢谢,对不起,你的更正是对的,我误读了这个问题,以为他们想要一个开关回到过去forth@wim你能给我解释一下为什么这么糟糕吗?我同意这不是最好的实践,但您实际发现默认可变参数的可能用例的频率是多少?无法抗拒!我认为@wim认为这是一个X-Y问题,他/她可能是对的,但如果你把逻辑放在函数之外,问题就变得相当微不足道了。我要加上一句感慨的话:@Chris_Rands Right。当你的问题变得微不足道时,这是一件好事。——1这只是另一种存储全局信息的坏方法state@Alex谢谢,对不起,你的更正是对的,我误读了这个问题,以为他们想要一个开关回到过去forth@wim你能给我解释一下为什么这么糟糕吗?我同意这不是最好的实践,但您实际发现默认可变参数的可能用例的频率是多少?无法抗拒!我认为@wim认为这是一个X-Y问题,他/她可能是对的,但如果你把逻辑放在函数之外,问题就变得相当微不足道了。我要加上一句感慨的话:@Chris_Rands Right。当你的问题
m变得微不足道这是件好事,目的是每次调用只执行一个操作。也许你可以用它来制作一个生成器,但这需要改变调用约定。阅读上面更新的代码。它在每次呼叫中执行一次操作,然后退出。不,它没有。如果您使用upgrade=False调用它,那么它会在while循环中打印HTTP,中断循环,然后从同一个调用中打印TFTP。目的是每次调用只执行一个操作。也许你可以用它来制作一个生成器,但这需要改变调用约定。阅读上面更新的代码。它在每次呼叫中执行一次操作,然后退出。不,它没有。如果您使用upgrade=False调用它,那么它将在while循环中打印HTTP,中断循环,然后从同一调用中打印TFTP。