名称空间和作用域
什么是名称空间?
import this
回显:(Python内置的小彩蛋)
The Zen of Python, by Tim Peters
Beautiful is better than ugly.
# 优美胜于丑陋(Python以编写优美的代码为目标)
Explicit is better than implicit.
# 明了胜于晦涩(优美的代码应当是明了的,命名规范,风格相似)
Simple is better than complex.
# 简洁胜于复杂(优美的代码应当是简洁的,不要有复杂的内部实现)
Complex is better than complicated.
# 复杂胜于凌乱(如果复杂不可避免,那代码间也不能有难懂的关系,要保持接口简洁)
Flat is better than nested.
# 扁平胜于嵌套(优美的代码应当是扁平的,不能有太多的嵌套)
Sparse is better than dense.
# 间隔胜于紧凑(优美的代码有适当的间隔,不要奢望一行代码解决问题)
Readability counts.
# 可读性很重要(优美的代码是可读的)
Special cases aren’t special enough to break the rules.Although practicality beats purity.
# 即便假借特例的实用性之名,也不可违背这些规则(这些规则至高无上)
Errors should never pass silently.Unless explicitly silenced.
# 不要包容所有错误,除非你确定需要这样做(精准地捕获异常,不写except:pass风格的代码)
In the face of ambiguity, refuse the temptation to guess.
# 当存在多种可能,不要尝试去猜测
There should be one– and preferably only one –obvious way to do it.
# 而是尽量找一种,最好是唯一一种明显的解决方案(如果不确定,就用穷举法)
Although that way may not be obvious at first unless you’re Dutch.
# 虽然这并不容易,因为你不是 Python 之父(这里的Dutch是指Guido)
Now is better than never.Although never is often better than *right* now.
# 做也许好过不做,但不假思索就动手还不如不做(动手之前要细思量)
If the implementation is hard to explain, it’s a bad idea.If the implementation is easy to explain, it may be a good idea.
# 如果你无法向人描述你的方案,那肯定不是一个好方案;反之亦然(方案测评标准)
Namespaces are one honking great idea — let’s do more of those!
# 命名空间是一种绝妙的理念,我们应当多加利用(倡导与号召)
在python当中所有有关名字的定义都会放到名称空间,比如定义变量名,函数名。根据存放的内容不同有三种名称空间,内置的名称空间,全局的名称空间,局部的名称空间。
内置名称空间
解释器一启动就有的,python内置的,一启动就会加载到内存中的。我们可以直接调用的。
>>> import builtins
>>> dir(builtins)
['ArithmeticError', 'AssertionError', 'AttributeError', 'BaseException', 'BlockingIOError', 'BrokenPipeError', 'BufferError', 'BytesWarning', 'ChildProcessError', 'ConnectionAbortedError', 'ConnectionError', 'ConnectionRefusedError', 'ConnectionResetError', 'DeprecationWarning', 'EOFError', 'Ellipsis', 'EnvironmentError', 'Exception', 'False', 'FileExistsError', 'FileNotFoundError', 'FloatingPointError', 'FutureWarning', 'GeneratorExit', 'IOError', 'ImportError', 'ImportWarning', 'IndentationError', 'IndexError', 'InterruptedError', 'IsADirectoryError', 'KeyError', 'KeyboardInterrupt', 'LookupError', 'MemoryError', 'ModuleNotFoundError', 'NameError', 'None', 'NotADirectoryError', 'NotImplemented', 'NotImplementedError', 'OSError', 'OverflowError', 'PendingDeprecationWarning', 'PermissionError', 'ProcessLookupError', 'RecursionError', 'ReferenceError', 'ResourceWarning', 'RuntimeError', 'RuntimeWarning', 'StopAsyncIteration', 'StopIteration', 'SyntaxError', 'SyntaxWarning', 'SystemError', 'SystemExit', 'TabError', 'TimeoutError', 'True', 'TypeError', 'UnboundLocalError', 'UnicodeDecodeError', 'UnicodeEncodeError', 'UnicodeError', 'UnicodeTranslateError', 'UnicodeWarning', 'UserWarning', 'ValueError', 'Warning', 'ZeroDivisionError', '__build_class__', '__debug__', '__doc__', '__import__', '__loader__', '__name__', '__package__', '__spec__', 'abs', 'all', 'any', 'ascii', 'bin', 'bool', 'bytearray', 'bytes', 'callable', 'chr', 'classmethod', 'compile', 'complex', 'copyright', 'credits', 'delattr', 'dict', 'dir', 'divmod', 'enumerate', 'eval', 'exec', 'exit', 'filter', 'float', 'format', 'frozenset', 'getattr', 'globals', 'hasattr', 'hash', 'help', 'hex', 'id', 'input', 'int', 'isinstance', 'issubclass', 'iter', 'len', 'license', 'list', 'locals', 'map', 'max', 'memoryview', 'min', 'next', 'object', 'oct', 'open', 'ord', 'pow', 'print', 'property', 'quit', 'range', 'repr', 'reversed', 'round', 'set', 'setattr', 'slice', 'sorted', 'staticmethod', 'str', 'sum', 'super', 'tuple', 'type', 'vars', 'zip']
全局名称空间
在python文件中顶头写的,没有任何缩进级别的,都是全局的。全局定义的名字,在哪都能调用。
局部名称空间
比如在函数内部定义的名称,可以把函数看做一个暗箱,里面有什么代码和内容其实并不会展现出来,因此里面有一个什么内容你也不知道,对外部(全局)是不可见的。不能够直接调用,这就是局部的,只在函数这个暗箱内才是有效的。
因此内置名称空间和全局名称空间是全局作用域,局部名称空间是局部作用域。
名称也要先定义在使用,函数也是,函数名也是名称空间的一种。
def test1():
print("test1")
test2() <------虽然走到这一行test2()没有被定义但是并不会报错,因为语法没错。
test1()
def test2():
print("test2")
比如上面这一段代码,虽然代码中定义了test1和test2方法,但是test2定义的要晚于调用,因此我们再执行的时候就执行到test1()这行调用代码的时候就会报错说test2这个name没有被定义。虽然明显的test1这个函数调用test2函数是有问题的,但是定义和调用是两个阶段,在定义阶段没有语法上的问题其实就是没问题的。这里就说到了python是如何读取程序文件的,python的读取时边读边解释。
再说找变量的问题:
x = 1
def test1():
print(x)
test1()
结果:1
这里调用的就是test1,print(x),它首先会在函数内找x的定义(局部找),然后去找全局,发现全局有一个x=1,那么就print出来,假如说全局也没有的话那么就会去找内置的。为了验证先从局部去找我们在函数内部加一个x来看看结果:
x = 1
def test1():
x = 2333
print(x)
test1()
print(x)
结果:
2333
1
可以发现打印结果完全就是不同的,函数内部的首先找到了局部的x = 2333然后把它打印了出来,函数外部的print直接调用的全局的x。
查看全局名称空间和局部名称空间
x = 1
def test1():
x = 2333
print(globals())
print(locals())
test1()
结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000037B8C1A1D0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/坚果云同步/Python/Day5(函数+)/初始函数.py', '__cached__': None, 'x': 1, 'test1': <function test1 at 0x00000037B8B77048>}
{'x': 2333}
我们可以发现在局部函数内打印的全局和局部的内容,全局的包括x = 1和test1函数,局部的就是x = 2333,都是以字典的形式呈现的,不过如果在全局的情况下的话:
x = 1
def test1():
x = 2333
print(globals())
print(locals())
结果:
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000037B8C1A1D0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/坚果云同步/Python/Day5(函数+)/初始函数.py', '__cached__': None, 'x': 1, 'test1': <function test1 at 0x00000037B8B77048>}
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000037B8C1A1D0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/坚果云同步/Python/Day5(函数+)/初始函数.py', '__cached__': None, 'x': 1, 'test1': <function test1 at 0x00000037B8B77048>}
在全局的局部内容还是全局的,这些都是顶头写的。