如何避免使用全局变量?

Car*_*arl 4 python if-statement function global-variables python-3.x

我使用全局变量,但我读过它们不是一个好的做法或 pythonic。我经常使用的函数会给出许多我需要在主函数中使用的 yes/no 变量。例如,如何在不使用全局变量的情况下编写以下代码?

def secondary_function():
    global alfa_is_higher_than_12
    global beta_is_higher_than_12

    alfa = 12
    beta = 5

    if alfa > 10:
        alfa_is_higher_than_12 = "yes"
    else:
        alfa_is_higher_than_12 = "no"

    if beta > 10:
        beta_is_higher_than_12 = "yes"
    else:
        beta_is_higher_than_12 = "no"

def main_function():
    global alfa_is_higher_than_12
    global beta_is_higher_than_12

    secondary_function()

    if alfa_is_higher_than_12=="yes":
        print("alfa is higher than 12")
    else:
        print("alfa isn't higher than 12")

    if beta_is_higher_than_12=="yes":
        print("beta is higher than 12")
    else:
        print("beta isn't higher thant 12")

main_function()
Run Code Online (Sandbox Code Playgroud)

ggo*_*len 10

术语“ Pythonic ”不适用于这个主题——使用这样的全局变量在任何编程语言和范式中都是糟糕的做法,并且不是 Python 特有的东西。

global关键字的工具,它的Python为您提供了选择的出封装并打破自然范围的变量。封装意味着你的每个组件都是一个逻辑的、独立的单元,应该作为一个黑匣子工作并执行一件事(注意:这件事是概念性的,可能由许多可能不平凡的子步骤组成),而没有改变全局状态或产生副作用。原因是模块化:如果程序出现问题(并且会出现问题),强大的封装可以很容易地确定发生故障的组件在哪里。

封装使代码更容易重构、维护和扩展。如果您需要一个组件以不同的方式运行,那么移除或调整它应该很容易,而这些修改不会导致系统中其他组件发生变化的多米诺骨牌效应。

强制封装的基本工具包括类、函数、参数和return关键字。语言通常提供模块、命名空间和闭包以达到类似的效果,但最终目标始终是限制范围并允许程序员创建抽象。

函数通过参数接收输入并通过返回值产生输出。您可以将返回值分配给调用范围内的变量。您可以将参数视为调整函数行为的“旋钮”。在函数内部,变量只是函数使用的临时存储空间,需要生成一个返回值然后消失。

理想的情况下,函数被写入到是幂等; 也就是说,它们不会修改全局状态并在多次调用时产生相同的结果。与其他语言相比,Python 在这方面没有那么严格,并且很自然地使用某些就地函数,例如sortrandom.shuffle。这些是证明规则的例外情况(如果您对排序改组有所了解,由于使用的算法和效率的需要,它们在这些上下文中是有意义的)。

就地算法是不纯的和非幂等的,但如果它修改的状态仅限于其参数及其文档和返回值(通常None)支持这一点,则行为是可预测和可理解的。

那么这一切在代码中是什么样子的呢?不幸的是,您的示例在其目的/目标方面似乎人为设计且不清楚,因此没有直接的方法可以对其进行转换,从而使封装的优势显而易见。

以下是这些函数中除修改全局状态之外的一些问题的列表:

  • 使用"yes""no"字符串文字代替True/False布尔值。
  • 在函数中硬编码值,使它们完全单一用途(它们也可以内联)。
  • printing in 函数(请参阅上面的副作用备注——如果他们愿意,则更喜欢返回值并让调用范围打印)。
  • 通用变量名称,例如secondary_function(我假设这相当于示例中的foo/ bar,但它仍然不能证明它们存在的理由,因此很难作为教学示例进行修改)。

但无论如何,这是我的镜头:

if __name__ == "__main__":
    alpha = 42
    beta = 6
    print("alpha %s higher than 12" % ("is" if alpha > 12 else "isn't"))
    print("beta %s higher than 12" % ("is" if beta > 12 else "isn't"))
Run Code Online (Sandbox Code Playgroud)

我们可以看到不需要所有的函数——只需alpha > 12在需要进行比较的任何地方编写并print在需要打印时调用。函数的一个缺点是它们可以用来隐藏重要的逻辑,所以如果它们的名称和“契约”(由名称、文档字符串和参数/返回值定义)不清楚,它们只会混淆客户端函数(通常是你自己)。

仅在有充分理由进行抽象时才进行抽象(调用代码被阻塞或多次重复类似的代码块是经典的经验法则)。当你做抽象时,要正确地做。


sjc*_*sjc 9

有人可能会问有什么原因可能需要像这样构造代码,但假设您有自己的原因,您可以只从辅助函数中返回值:

def secondary_function():

  alfa = 12
  beta = 5

  if alfa > 10:
      alfa_is_higher_than_12 = "yes"
  else:
      alfa_is_higher_than_12 = "no"

  if beta > 10:
      beta_is_higher_than_12 = "yes"
  else:
      beta_is_higher_than_12 = "no"

  return alfa_is_higher_than_12, beta_is_higher_than_12


def main_function():

  alfa_is_higher_than_12, beta_is_higher_than_12 = secondary_function()

  if alfa_is_higher_than_12=="yes":
      print("alfa is higher than 12")
  else:
      print("alfa isn't higher than 12")

  if beta_is_higher_than_12=="yes":
      print("beta is higher than 12")
  else:
      print("beta isn't higher thant 12")
Run Code Online (Sandbox Code Playgroud)

  • 除此之外,我经常将返回值包装在一个泛型类中,并将需要返回的任何内容塞入其中。Python 足够灵活,可以动态添加字段,因此它对我来说成为链接这样的东西的一种非常方便的方式:) (2认同)