期限 ”Pythonic https://stackoverflow.com/questions/25011078/what-does-pythonic-mean” 不适用于这个主题——在任何编程语言和范例中使用这样的全局变量都是不好的做法,并且不是 Python 特有的。
The global
关键字是Python提供给你选择退出的工具封装 https://en.wikipedia.org/wiki/Encapsulation_(computer_programming)并打破自然scope https://en.wikipedia.org/wiki/Scope_(computer_science)一个变量的。封装意味着每个组件都是一个逻辑的、独立的单元,应该像黑盒子一样工作执行一件事 https://en.wikipedia.org/wiki/Single_responsibility_principle(注:这个一件事是概念性的,可能由许多可能不平凡的子步骤组成),不会改变全局状态或产生副作用 https://en.wikipedia.org/wiki/Side_effect_(computer_science)。原因是模块化:如果程序中出现问题(并且it will https://en.wikipedia.org/wiki/Murphy%27s_law),具有强大的封装性可以很容易地确定故障组件的位置。
封装使代码更容易重构、维护和扩展。如果您需要一个组件有不同的行为,那么应该很容易删除它或调整它,而不会导致这些修改导致系统中其他组件的更改产生多米诺骨牌效应。
强制封装的基本工具包括类、函数、参数和return
关键词。语言通常提供模块、命名空间和闭包来达到类似的效果,但最终目标始终是限制范围并允许程序员创建松散耦合的抽象。
函数通过参数接收输入并通过返回值产生输出。您可以将返回值分配给调用范围内的变量。您可以将参数视为调整函数行为的“旋钮”。在函数内部,变量只是函数使用的临时存储,用于生成其一个返回值,然后消失。
理想情况下,函数可以写成pure https://en.wikipedia.org/wiki/Pure_function and 幂等的 https://en.wikipedia.org/wiki/Idempotence#Computer_science_examples;也就是说,它们不会修改全局状态,并且在多次调用时会产生相同的结果。 Python 在这方面比其他语言稍微宽松一些,并且很自然地使用某些in-place https://en.wikipedia.org/wiki/In-place_algorithm功能类似于sort https://docs.python.org/3/library/stdtypes.html#list.sort and random.shuffle https://docs.python.org/3/library/random.html#random.shuffle。这些是证明规则的例外情况(如果您了解一点sorting https://en.wikipedia.org/wiki/Timsort and 洗牌 https://en.wikipedia.org/wiki/Fisher%E2%80%93Yates_shuffle,由于所使用的算法和对效率的需要,它们在这些上下文中有意义)。
就地算法是不纯粹且非幂等的,但如果它修改的状态仅限于其参数及其文档和返回值(通常是None
)支持这一点,行为是可预测且可理解的。
那么这一切在代码中是什么样子的呢?不幸的是,您的示例似乎是人为的且不清楚其目的/目标,因此没有直接的方法来对其进行转换以使封装的优势变得显而易见。
以下列出了这些函数中除修改全局状态之外的一些问题:
- using
"yes"
and "no"
字符串文字而不是True
/False
布尔值。
-
硬编码 https://en.wikipedia.org/wiki/Hard_coding函数中的值,使它们完全具有单一用途(它们也可以内联)。
-
print
在函数中调用(请参阅上面的副作用注释 - 更喜欢返回值并让调用范围打印(如果他们愿意的话)。
- 通用变量名称如
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
当您需要打印时。函数的一个缺点是它们可以隐藏重要的逻辑,因此如果它们的名称和“契约”(由名称定义,文档字符串 https://www.python.org/dev/peps/pep-0257/和参数/返回值)不清楚,它们只会让函数的客户端(通常是你自己)感到困惑。
为了便于说明,假设您经常调用此格式化程序。然后,就有理由进行抽象;调用代码会变得很麻烦并且重复的 https://en.wikipedia.org/wiki/Don%27t_repeat_yourself。您可以将格式化代码移至辅助函数并传递任何动态数据以注入到模板中:
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 https://en.wikipedia.org/wiki/SOLID.