术语“Pythonic”不适用于这个主题——在任何编程语言和范式中使用这样的全局变量都是不好的做法,并且不是 Python 特有的。
global 关键字是 Python 提供的工具,可让您选择退出 encapsulation 并打破变量的自然 scope。封装意味着您的每个组件都是一个逻辑的、自包含的单元,应该作为一个黑盒和performs one thing 工作(注意:这一件事是概念性的,可能包含许多,可能不是琐碎的子步骤)而不改变全局状态或产生side effects。原因是模块化:如果程序中出现问题(并且会出现问题),强大的封装可以很容易地确定发生故障的组件在哪里。
封装使代码更易于重构、维护和扩展。如果您需要一个组件以不同的方式运行,那么应该很容易将其移除或调整,而不会导致这些修改导致系统中其他组件发生变化的多米诺骨牌效应。
强制封装的基本工具包括类、函数、参数和return 关键字。语言通常会提供类似效果的模块、命名空间和闭包,但最终目标始终是限制范围并允许程序员创建松散耦合的抽象。
函数通过参数接收输入并通过返回值产生输出。您可以将返回值分配给调用范围内的变量。您可以将参数视为调整函数行为的“旋钮”。在函数内部,变量只是函数使用的临时存储空间,需要生成一个返回值然后消失。
理想情况下,函数写成pure 和idempotent;也就是说,它们不会修改全局状态并在多次调用时产生相同的结果。与其他语言相比,Python 在这方面的严格程度要低一些,使用某些in-place 函数(如sort 和random.shuffle)是很自然的。这些是证明规则的例外(如果您对 sorting 和 shuffling 有所了解,由于所使用的算法和对效率的需求,它们在这些上下文中是有意义的)。
就地算法是不纯且非幂等的,但如果它修改的状态仅限于其参数及其文档和返回值(通常为None)支持这一点,则行为是可预测的并且可以理解。
那么这一切在代码中是什么样子的?不幸的是,您的示例似乎是做作的,并且其目的/目标不清楚,因此没有直接的方法可以对其进行转换,从而使封装的优势显而易见。
这里列出了这些函数中除了修改全局状态之外的一些问题:
- 使用
"yes" 和"no" 字符串字面量代替True/False 布尔值。
-
hardcoding 函数中的值,使它们完全单一用途(它们也可以内联)。
-
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"))
我们可以看到不需要所有的功能——只要在需要比较的地方写alpha > 12,需要打印的时候调用print。函数的一个缺点是它们可以隐藏重要的逻辑,因此如果它们的名称和“合同”(由名称、docstring 和参数/返回值定义)不清楚,它们只会混淆函数的客户端(通常是您自己)。
为了便于说明,假设您经常调用此格式化程序。然后,有理由抽象;调用代码会变得很麻烦repetitive。您可以将格式化代码移动到辅助函数并传递任何动态数据以注入模板:
def fmt_higher(name, n, cutoff=12):
verb = "is" if n > cutoff else "isn't"
return f"{name} {verb} higher than {cutoff}"
if __name__ == "__main__":
print(fmt_higher("alpha", 42))
print(fmt_higher("beta", 6))
print(fmt_higher("epsilon", 0))
print(fmt_higher(name="delta", n=2, cutoff=-5))
我们可以更进一步,假设n > cutoff 是一个复杂得多的测试,其中包含许多小步骤,如果留在fmt_higher 中会违反单一职责。也许复杂的测试在代码的其他地方使用,并且可以推广以支持这两种用例。
在这种情况下,您仍然可以使用参数和返回值而不是 global,并对谓词执行与格式化程序相同的抽象:
def complex_predicate(n, cutoff):
# pretend this function is much more
# complex and/or used in many places...
return n > cutoff
def fmt_higher(name, n, cutoff=12):
verb = "is" if complex_predicate(n, cutoff) else "isn't"
return f"{name} {verb} higher than {cutoff}"
if __name__ == "__main__":
print(fmt_higher("alpha", 42))
print(fmt_higher("beta", 6))
print(fmt_higher("epsilon", 0))
print(fmt_higher(name="delta", n=2, cutoff=-5))
只有在有足够的理由进行抽象时才进行抽象(调用代码被阻塞或当您多次重复类似的代码块是经典的经验法则)。当你做抽象时,做它properly。