具体示例:是否可以避免使用全局变量?

Specific example: Is it possible to aviod the use of a global variable?

我有以下程序,有问题的变量(字典)是 player_info,它存储玩家信息(姓名和目标)。为了解决当前导致的错误,我只需要将 player_info 设置为 全局变量 ,但我想知道 Whosebug 专家是否可以建议或讨论替代方法的可能性解决此问题的方法 不使用全局变量。

代码

#FOOTBALL COACH app

#The program allows a user to enter a number of players (their names and goals scored) and then search for a player, returning their average goals for the three matches


import sys 
def main():
 mainmenu()

def mainmenu():

  print("=====WELCOME to the MAIN MENU=============")
  print("""
  1..........Add New Players & Goals
  2..........Search by Players
  3..........Quit

  =========================================
  """)

  choice=int(input("Enter choice:"))
  if choice==1:
    addplayers()
  elif choice==2:
    searchplayer(player_info)
  elif choice==3:
    sys.exit()
  else:
    print("You must make a valid choice - 1, 2 or 3")


def addplayers():


  player_info= {} #create a dictionary that stores the player name: player goals

  num_players = int(input("Please enter number of players you wish to enter:"))
  print ("You are entering %s players" %num_players)
  player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']
  for i in range(0,num_players):
      player_name = input("Enter Player Name :")
      player_info[player_name] = {}
      for entry in player_data:
          player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.


  mainmenu()


def searchplayer():  
   print("===============SEARCH by player: Calculate average goals==================")
   name = input("Player name : ")
   if name in player_info.keys():
     #print student_info
      print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
   else:
      print("Please enter a valid player name:")

main()

如前所述,我知道在 addplayer() 子程序中重写这个可以解决问题:

global player_info
player_info = {} #create a dictionary that stores the player name: player goals

...我正在寻找不使用全局变量来解决问题的方法。

更新:

下面使用 return player_info 的一个答案是我想要的,但它还不太奏效。此外,每次添加播放器时我都需要 return 到主菜单,不太确定如何执行此操作,每次都没有调用主菜单。有什么建议么? https://repl.it/JRl5/1

您可以在函数内部使用 return 以避免使用全局变量。一个简单的例子如下所示:

def addplayers():

    player_info= {} 

    name = input("Enter Name: ")
    test = int(input("Enter a number: "))

    player_info[name] = test

    return player_info


player_info = addplayers()

如果你想在另一个函数中使用它,你只需将字典作为参数传递给该函数:

def searchplayers(player_info):

    print (player_info)

注意:关于“Why are global variables evil?”的有趣回答

编辑:

您的 addplayers() 正在呼叫 mainmenu(),它本身正在 mainmenu() 内呼叫。这是一个递归函数,最好避免使用这些函数,除非有充分的理由使用它。我会将 mainmenu 的内容放入 while 循环中,直到满足某些条件。完整的代码如下所示(我已经删除了 main 函数,因为它实际上没有做任何事情):

def mainmenu():

    stop = False

    while stop == False:

        print("=====WELCOME to the MAIN MENU=============")
        print("""
        1..........Add New Players & Goals
        2..........Search by Players
        3..........Quit

        =========================================
        """)

        choice=int(input("Enter choice:"))
        if choice==1:
            player_info = addplayers()
        elif choice==2:
            searchplayer(player_info)
        elif choice==3:
            print ("Exit the main menu")
            stop = True
        else:
            print("You must make a valid choice - 1, 2 or 3")


def addplayers():


    player_info= {} #create a dictionary that stores the player name: player goals

    num_players = int(input("Please enter number of players you wish to enter:"))
    print ("You are entering %s players" %num_players)
    player_data = ['Match 1 goals : ', 'Match 2 goals : ', 'Match 3 goals : ']

    for i in range(0,num_players):
        player_name = input("Enter Player Name :")
        player_info[player_name] = {}

        for entry in player_data:
            player_info[player_name][entry] = int(input(entry)) #storing the marks entered as integers to perform arithmetic operations later on.

    return player_info


def searchplayer(player_info):  
    print("===============SEARCH by player: Calculate average goals==================")
    name = input("Player name : ")
    if name in player_info.keys():
        #print student_info
        print ("Average player goals : ", str(sum(player_info[name].values())/3.0))
    else:
        print("Please enter a valid player name:")


mainmenu()

将与游戏相关的所有内容存储在数据结构中,例如字典,并将其传递到所有可以根据需要更新的函数中。编写一个函数 "newgame" 来创建这个结构并对其进行初始化。

在某种程度上,这是面向对象的编程,不使用 Python 的 class 和对象的语法。也许,您稍后会在 class / 教程中学习这些内容。

首先,总是可以避免使用全局变量。其次,全局变量在Python中可能用词不当; global 将变量存储在本地全局变量中,通常是本地模块。这避免了像 C 这样的语言与全局变量的大部分问题,因为它们会发生冲突; Python 每个模块有一个名称space。对于只有一个上下文的简单脚本,这可能没问题。

您可能使用的另一个名称space 是特定对象的名称,使用 class。这可能看起来像:

class Game:
    def mainmenu(self,...):
        self.addplayers()
    def addplayers(self):
        self.player_info = {}

使用这种代码,任何实例化 Game 的人都可以创建多个实例,每个实例在使用时都作为 self 传递。这在很大程度上是类似形式的可变状态传递的语法糖:

def mainmenu():
    state={}
    addplayers(state)
def addplayers(state):
    state['player_info'] = {}

对于某些形式的编程,不可变状态更可取(特别是共享数据的多线程,或者保留可以撤消步骤的日志)。这样做的方式类似,但每次调用都会创建一个新状态:

def mainmenu():
    state = {}
    state = addplayers(state)
def addplayers(oldstate):
    newstate = oldstate.copy()
    newstate['player_info'] = {}
    return newstate

Python 不是为此设计的,也没有真正的模式来防止您无意中修改可变类型。某些类型可以转换为类似的不可变类型,例如 frozensettuple

我们可以做的一个更奇怪的 hack 是调用一个 Python 函数,使用一组与通常不同的全局变量。这可能会被滥用来获取您现有的函数、global 语句和所有这些,并让它们使用不同的变量:

fakeglobals = mainmenu.__globals__.copy()
exec(addplayers.__code__, fakeglobals)

不过,您的原始代码在函数之间来回调用,并且每个函数都会根据 __globals__ 属性重置它们的全局变量。

您的代码还使用尾递归实现了一个循环。这在 Python 中没有优化,最终会 运行 出栈 space。在优化尾递归的语言中,您可以连续传递状态作为参数,而不需要 return 它。