目录
模块(Module)
模块可以很好地,通过产生多个.py文件管理代码(模块可以相互import)。可以提高可读性与代码的重复使用。
# basicmath.py
def add(a: int, b: int) -> int:
result = int(a) + int(b)
return result
def subtract(a: int, b: int) -> int:
result = int(a) - int(b)
return result
此时并不会有任何输出,但我们可以使用这些函数
>>> add(2, 5)
7
>>> subtract(9, 3)
6
引入其他.py
# mathrunner.py
import basicmath
a = input("Enter your first value: ")
b = input("Enter your second value: ")
result = basicmath.add(a,b)
print(result)
result = basicmath.subtract(a,b)
print(result)
Scope
Important
Scope refers to the availability of a particular object in a program. That availability is interpreted locally first and then globally.
作用域指的是程序中特定对象的可用性。这种可用性首先在本地解释,然后在全局解释。
上面程序中,一个重要点是add函数与subtract函数如何访问的。你会看到变量传递给了basicmath模块。Scope指的是类、函数与变量在程序中给定执行点的作用域。在上面的例子中,注意result变量如何在basicmath模块的add&subtract函数中被使用,以及如何在mathrunner程序中被使用。根据Python的Scope法则,我们可以以这种方式重新使用变量名(比如这里result既在basicmath中也在mathrunner中)。result变量locally作用于add&subtract函数,意味着它只存在于这些函数被调用的时候。如果我们像在add&subtract函数外使用result变量,我们可以像修改basicmath程序改成下面这样:
# basicmath.py
result = 0
def add(a: int, b: int) -> int:
result = int(a) + int(b)
return result
def subtract(a: int, b: int) -> int:
result = int(a) - int(b)
return result
def get_last_result() -> int:
return result
# mathrunner.py
import basicmath
a = input("Enter your first value: ")
b = input("Enter your second value: ")
result = basicmath.add(a,b)
print(result)
result = basicmath.subtract(a,b)
print(result)
print(basicmath.get_last_result())
输出像这样
Enter your first value: 5
Enter your second value: 2
7
3
0
为什么result最后仍为0。尽管result变量作用于函数外(globally),Python仍然会给予locally中result优先权。在没有任何locally的result实例时,才会服从result的globally实例。当add&subtract函数在函数内创造了result的locally实例,get_last_result函数却没有在函数内创造result的locally实例,所以result会取result=0的函数外(globally实例)实例。如果想使用globally作用范围的变量,我们需要告诉Python我们的意图。
# basicmath.py
result = 0
def add(a: int, b: int) -> int:
global result
result = int(a) + int(b)
return result
def subtract(a: int, b: int) -> int:
global result
result = int(a) - int(b)
return result
def get_last_result() -> int:
return result
输出像这样
Enter your first value: 5
Enter your second value: 2
7
3
3
此时get_last_result函数由于函数内没有实现任何locally的值,所以会从该函数外部寻找globally的值 。此时有三个globally的值,result=0以及2个函数声明了global的result,选择最近的global,即result=3。若 只有add函数写了global,那么result=7。
总结:Scope包含local&global。首先local其次无local时才global。
Definition Access(定义访问)
当编写包含执行许多不同类型操作的函数的模块时,你会发现需要其中一些函数在模块内执行操作,但不一定希望在模块外调用这些函数。在编程术语中,我们将这两种类型的函数描述为私有函数(private)和公共函数(public)。虽然有些编程语言在编译时提供了修饰符来声明此意图,但Python没有。相反,Python中的所有函数在默认情况下都是公共的。没有办法阻止另一个程序调用模块中不应该被调用的函数!
为了绕过这个特性,并且在Python中没有这样的修饰符被认为是一个特性,程序员采用了一些正式的约定来交流意图。当编写不打算在模块外部使用的函数时,它们应该带有一个下划线字符。此约定用于Python中的函数、常量和类。
# Specifying private intent
def _myprivatefunction():
_myprivateconstant = "525600" # minutes in a year
class _myprivateclass():
Namespaces
随着你的程序的规模和复杂性的增长,理解范围的重要性将变得越来越重要。如果没有我们创建的对象的作用域的能力,每个Python程序员都必须创建唯一的对象名称!想象一下,如果对象命名行为像互联网域名,每个都必须在中央注册中心记录,以避免重复。通过作用域和名为名称空间的约定,大多数编程语言可以避免这种不必要的复杂性。名称空间是一个类似于字典的符号名称集合,这些符号名称被绑定到当前定义它们的对象上。
名称空间并不是一个完美的解决方案,但它们可以很好地帮助程序员识别和区分他们编写的模块和程序。重要的是要避免使用已知的名称空间,特别是那些Python内置对象使用的名称空间。例如,您应该避免创建名为int或tuple的对象,因为它们已经存在于Python程序的每个实例中。想象一下,如果我们将basicmath模块命名为math,它是标准库的一部分!更具体地说,如果我们在mathrunner.py程序中也有一个add函数会怎么样?命名空间允许我们区分相似的命名对。
# mathrunner.py
import basicmath as m
def add(a, b):
print(a + b)
a = input("Enter your first value: ")
b = input("Enter your second value: ")
print(m.add(a,b))
print(add(a,b))
打印的两个add函数将产生截然不同的结果,名称空间允许我们在编写的代码中区分它们。Python还为我们导入的模块提供了命名约定。请注意,在这个修订版本的mathrunner.py中,import语句和随后使用的名称空间有何不同。当您导入的模块使用较长的名称空间(您可能不希望每次使用时都键入该名称空间)时,这可能很有用。
Python makes use of four types of namespaces, listed in order of precedence:
Local
Inside a class method or function
Enclosed
Inside an enclosing function in instances when one function is nested(嵌套) within another
Global
Outside all functions or class methods
Built-In
The built-in namespace consists of all of the Python objects that are available to your program at runtime. Try running
dir(__builtins__)
on the IDLE shell to see the full list of built-ins. Many of the names should look familiar to you.
说到内置组件,让我们快速地看一下你可能已经开始在课程中讨论的一些代码中观察到的内置组件,__name__。
# my_module.py
def my_func_1():
pass
def my_func_2():
pass
def my_func_3():
pass
def start():
my_func_1()
my_func_2()
my_func_3()
此时不会有任何输出,可以添加一个
# my_module.py
def my_func_1():
pass
def my_func_2():
pass
def my_func_3():
pass
def start():
my_func_1()
my_func_2()
my_func_3()
start()
但是有一个问题,如果在其他程序,你只想调用my_func_1怎么办?
# my_other_module.py
import my_module
my_module.my_func_1()
很明显,这样是会运行start()的
改成这样即可,只有在运行本文件时__name__才等于‘__main__’,其他文件调用这个模块__name__不等于‘__main__’。
# my_module.py
def my_func_1():
pass
def my_func_2():
pass
def my_func_3():
pass
def start():
my_func_1()
my_func_2()
my_func_3()
if __name__ == '__main__':
start()