一、命名空间
https://blog.youkuaiyun.com/yuelai_217/article/details/123110259
https://www.php.cn/faq/556852.html
1、概念
Python在代码执行时会维护一个常量、变量、函数等命名的环境。这个环境就是命名空间(Namespace),也可以称作名称空间。命名空间是一个存储名称与对象对应关系的字典(dictionary),是从名称到对象的映射,其中名称就是指标识符(identifier),对象就是指一切对象,Python中的一切都是对象,包括整数、浮点数、字符串、函数、类等。。大部分的命名空间都是通过字典来实现的,它的键就是变量名,它的值就是那些变量的值。
命名空间提供了在项目中避免名字冲突的一种方法。各个命名空间是独立的,没有任何关系的,所以一个命名空间中不能有重名,但不同的命名空间是可以重名而没有任何影响。
2、命名空间分类
Python中的命名空间可以分为三种类型:
(1)内置命名空间(Built-in Namespace)
内置命名空间是Python 语言内置的名称,Python解释器启动时就加载了的命名空间。内置命名空间中默认包含了一些函数和变量,比如函数名 abs、char 、print()、type()、str()和BaseException、Exception 等,开发人员可以直接使用。内置命名空间也可以通过 builtins 模块来访问。
(2)全局命名空间(Global Namespace)
全局命名空间是指在模块中定义的名称,而不是在函数或类中定义的,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。在一个模块中定义的全局变量在其他模块中也可以被访问。一个模块只有一个全局命名空间,模块中的所有函数都可以访问这个命名空间。
(3)局部命名空间(Local Namespace)
局部命名空间是函数中定义的名称,在函数调用时创建的命名空间,记录了函数的变量,包括函数的参数和局部定义的变量。(类中定义的也是)。函数结束调用时,局部命名空间也会被销毁。
3、通过globals()和locals()函数获取命名空间
Python中可以使用globals()和locals()两个函数来获取对应的全局命名空间和局部命名空间。globals()函数返回全局命名空间的字典也可以通过当前模块中的任一模块的函数的__globals__访问,locals()函数返回局部命名空间的字典,内置命名空间可以通过全局命名空间中的键__bulitins__来访问。
4、命名空间查找规则
当使用一个变量时,Python会先在局部命名空间中查找对应的变量,如果查到,Python 将使用它,然后停止搜索。如果没有找到,就会到全局命名空间中查找,最后再到内置命名空间中查找。换句话说,Python查找变量时是按照局部、全局、内置的顺序进行的,这种查找变量的顺序被称为LEGB规则。如果这些名字空间找不到 x,它将放弃查找并引发一个 NameError 异常,如,NameError: name ‘aa’ is not defined。
对于闭包来说,这里有一点区别,如果在local namespace中找不到变量的话,还会去父函数的local namespace中找变量。
5、命名空间的作用域和生存周期
在Python中,每个函数、模块、类都有自己的命名空间。当一个名称被绑定到一个对象时,它只在该命名空间中有意义。如果名称被绑定到一个局部命名空间中,它就只在该函数中有意义;如果名称被绑定到全局命名空间中,它在该模块中就有意义;如果名称被绑定到内置命名空间中,它就在整个Python解释器中都有意义。
(1)
b = 3
def func():
b = 2
print(b)
func()
print(b)
2
3
(2)
b = 3
def func():
global b
b = 2
print(b)
func()
print(b)
2
2
(3)
def func():
b = 2
print(b)
func()
print(b)
2
Traceback (most recent call last):
File "D:/DL/yolov5/test/z.py", line 5, in <module>
print(b)
NameError: name 'b' is not defined
(4)
def func():
global b
b = 2
print(b)
func()
print(b)
2
2
(5)当前的函数用global之后,b变量就被加到了globals里面
def func():
global b
b = 2
print(b)
print(func.__globals__)
print(globals())
func()
print(b)
print(globals())
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CC9627FCC8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/DL/yolov5/test/z.py', '__cached__': None, 'func': <function func at 0x000001CC962D8168>}
2
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CC9627FCC8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/DL/yolov5/test/z.py', '__cached__': None, 'func': <function func at 0x000001CC962D8168>, 'b': 2}
2
{'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x000001CC9627FCC8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'D:/DL/yolov5/test/z.py', '__cached__': None, 'func': <function func at 0x000001CC962D8168>, 'b': 2}
(6)
def out():
## global关键字作用
global en
en = 1
other()
print(out.__globals__['en'])
## 调用other可以打印en
def other():
print(en)
def b():
## globals里面没有en那个全局变量
out()
print(b.__globals__['en'])
b()
1
1
1
(7)
# a.py
def out():
## global关键字作用
global en
en = 1
other()
print(out.__globals__['en'])
## 调用other可以打印en
def other():
print(en)
#############上面是a文件,下面是b文件#############
# b.py
from a import out
def b():
## globals里面没有en那个全局变量
out()
print(b.__globals__['en'])
b()
KeyError: 'en'
1
1
(8)
# a.py
def out():
## global关键字作用
global en
en = 1
# other()
# print(out.__globals__['en'])
## 调用other可以打印en
# def other():
# print(en)
# b.py
from a import *
## b 文件没执行过a文件的out函数,也就是global还没定义
# print('这个是out的:',out.__globals__['en']) # KeyError: 'en'
def b():
## 执行out,global被定义
out()
## 打印out函数的globals
print('这个是out的:',out.__globals__['en'])
## 打印b函数的globals
# print('这个是b的:',b.__globals__['en']) # KeyError: 'en'
b()
print('这里是分割线'.center(150,'-'))
## out函数执行过后,global已经被定义
print('这个是out的:',out.__globals__['en'])
# print(b.__globals__['en']) # KeyError: 'en'
这个是out的: 1
------------------------------------------------------------------------这里是分割线------------------------------------------------------------------------
这个是out的: 1
不同的命名空间在不同的时刻创建,有不同的生存期。
1、内置命名空间在 Python 解释器启动时创建,会一直保留,不被删除。
2、模块的全局命名空间在模块定义被读入时创建,通常模块命名空间也会一直保存到解释器退出。
3、当函数被调用时创建一个局部命名空间,当函数返回结果或抛出异常时被删除。每一个递归调用的函数都拥有自己的命名空间。
Python 的一个特别之处在于其赋值操作总是在最里层的作用域。赋值不会复制数据,只是将命名绑定到对象。删除也是如此:“del y” 只是从局部作用域的命名空间中删除命名 y 。事实上,所有引入新命名的操作都作用于局部作用域。
i=1
def func2():
i=i+1
func2();
#错误:UnboundLocalError: local variable 'i' referenced before assignment
由于创建命名空间时,python会检查代码并填充局部命名空间。在python运行那行代码之前,就发现了对i的赋值,并把它添加到局部命名空间中。当函数执行时,python解释器认为i在局部命名空间中但没有值,所以会产生错误。
def func3():
y=123
del y
print(y)
func3()
#错误:UnboundLocalError: local variable 'y' referenced before assignment
#去掉"del y"语句后,运行正常
6、from module import 和 import module
(1)使用 import module时,module 本身被引入,但是保存它原有的命名空间,所以我们需要使用 module.name 这种方式访问它的函数和变量。from module import这种方式,是将其它模块的函数或者变量引到当前的命名空间中,所以就不需要使用 module.name 这种方式访问其它的模块的方法了。但是注意使用global命名的全局变量在导入其他模块时不会被导入当前模块的命名空间。
# c文件
f = 1
def out():
## global关键字作用
global en
en = 1
other()
print(out.__globals__['en'])
def other():
print(en)
if __name__ == '__main__':
out()
#d文件
import c
def b():
c.out()
aa = c.out.__globals__
bb = b.__globals__
print(aa)
print(bb)
b()
# c文件
f = 1
def out():
## global关键字作用
global en
en = 1
other()
print(out.__globals__['en'])
def other():
print(en)
if __name__ == '__main__':
out()
# d文件
from c import out
#import c
def b():
out()
aa = out.__globals__
bb = b.__globals__
print(aa)
print(bb)
b()
# d文件
from c import *
#import c
def b():
out()
aa = out.__globals__
bb = b.__globals__
print(aa)
print(bb)
b()
(2)python中的module也是对象,所有的modules都有一个内置的属性__name__,模块的__name__属性的值取决于如何使用这个模块,如果import module,那么__name__属性的值是模块的名字。如果直接执行这个模块的话,那么__name__属性的值就是默认值__main__。
7、命名空间的注意点
(1)命名空间中的名称必须是合法的标识符。
(2)函数和类就是创建一个新的命名空间。
(3)变量名可以在命名空间中重新绑定到其他对象上。
(4)使用global语句可以让函数内部的变量引用全局变量。
(5)使用nonlocal语句可以让函数内部的变量引用外层函数的局部变量。
(6)Python中名称空间的查找是按照LEGB规则进行查找的。
二、all
当使用from module import *时,会将非下划线开头的成员都导入当前命名空间中,这样可能弄脏当前命名空间。如果显式声明了 all,import * 就只会导入 all 列出的成员。如果 all 定义有误,列出的成员不存在,还会抛出异常。当使用import module或者from module import 时与__all__变量无关。
三、init.py
导入模块就是在导入模块的位置将模块(.py)文件拷贝过去
python中的__init__.py文件用来区分一文件夹是包还是普通文件夹,所以导入一个包时,这个包内一定要有__init__.py文件(必须是其目录的子文件)。(新版本的python不管有没有_init__.py文件都是包)
当包有__init__.py文件时,不管用哪种导入模块的方式都会先执行__init__.py文件,我们可以借助__init__.py文件导入我们所需的其他模块(当其他模块也是其包的子文件时可以使用from .module import
实现自动子模块导入)。
# a文件夹下有a_a文件
def a_A():
print('this is a_A')
print('this is a_a')
# a文件夹下有a_b文件
def a_B():
print('this is a_B')
print('this is a_b')
# __init__文件
print('this is __init__')
# b文件
from a import a_a
执行b文件,结果:
this is __init__
this is a_a
# 可以看出先执行了__init__文件,然后再执行了a_a文件。
当没有__init__文件时,执行b文件,结果:
this is a_a
# 可以看出依然识别出a是一个模块
# b文件
import a
#当b文件使用import 导入时,有__init__文件
执行b文件,结果:
this is __init__
# 可以看出使用import导入模块时,只会执行__init__.py文件
# b文件
from a import *
执行b文件,结果:
this is __init__
# 可以看出这种导入方式依旧只会执行__init__文件
# __init__.py文件
print('this is __init__')
from .a_a import a_A
from .a_b import a_B
无论b.py文件以哪种方式导入包a,结果:
this is __init__
this is a_a
this is a_b
# 可以看出通过__init__文件实现了子模块的自动导入
# __init__.py文件
print('this is __init__')
from .a_a import a_A
from .a_b import a_B
__all__ = ['a_A']
# b文件
import a
a.a_A()
a.a_B()
执行b文件,结果:
this is __init__
this is a_a
this is a_b
this is a_A
this is a_B
# b文件
from a import *
a_A()
a_B()
执行b文件,结果:
NameError: name 'a_B' is not defined
this is __init__
this is a_a
this is a_b
this is a_A
当__init__文件没有__all__时:
# __init__.py文件
print('this is __init__')
from .a_a import a_A
from .a_b import a_B
# b文件
from a import *
a_A()
a_B()
执行b文件,结果:
this is __init__
this is a_a
this is a_b
this is a_A
this is a_B
得到结论,init.py的作用不再是标识文件夹是包,而是可以实现子模块的自动导入,变量__all__作用于from package import *,不管是py模块还是包都可以用all来实现模糊导入,注意如果__all__的变量里没有某个函数时通过 from package import *是导入不了这个函数的,但还是可以通过from package import 这个函数来实现精确导入,或者删除__all__变量。
可以被import导入的对象有:
模块文件(.py文件)
C或C++扩展(已编译为共享库或DLL文件)
包(包含多个模块)
内建模块(使用C编写并已链接到Python解释器中)
可以作为模块被导入的文件形式有:.py、.pyo、.pyc、.pyd、.so、.dll
参考:https://blog.youkuaiyun.com/m0_37607365/article/details/79889901?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-1.pc_relevant_paycolumn&spm=1001.2101.3001.4242.2
https://blog.youkuaiyun.com/yucicheung/article/details/79445350
init.py文件:
from .builder import DATASETS, PIPELINES, build_dataloader, build_dataset
from .cityscapes import CityscapesDataset
from .coco import CocoDataset
from .custom import CustomDataset
from .dataset_wrappers import (ClassBalancedDataset, ConcatDataset,
RepeatDataset)
from .deepfashion import DeepFashionDataset
from .lvis import LVISDataset, LVISV1Dataset, LVISV05Dataset
from .nightowls import NightOwlsDataset
from .samplers import DistributedGroupSampler, DistributedSampler, GroupSampler
from .utils import replace_ImageToTensor
from .voc import VOCDataset
from .waymo_open import WaymoOpenDataset
from .wider_face import WIDERFaceDataset
from .xml_style import XMLDataset
from .underwater_optics import UnderwaterOpticsDataset
__all__ = [
'CustomDataset', 'XMLDataset', 'CocoDataset', 'DeepFashionDataset',
'VOCDataset', 'CityscapesDataset', 'LVISDataset', 'LVISV05Dataset',
'LVISV1Dataset', 'GroupSampler', 'DistributedGroupSampler',
'DistributedSampler', 'build_dataloader', 'ConcatDataset', 'RepeatDataset',
'ClassBalancedDataset', 'WIDERFaceDataset', 'DATASETS', 'PIPELINES',
'build_dataset', 'replace_ImageToTensor', 'UnderwaterOpticsDataset'
]
__all__ += ['WaymoOpenDataset', 'NightOwlsDataset']