python变量的层级
global
当使用global 定义全局变量时,经常会提示:Global variable '变量名' is undefined at the module level
1 | def test001(self) |
下边是查询后得到得结果
1 | a = '我是模块中的变量a' |
在整个py文件中,非def和class之外得变量,一般在最上放得变量,才是模块变量
如果是在模块中为全局变量赋值
1 | # 定义一个全局变量user_id,但是仍然需要在模块层先定义一个全局变量 |
变量定义及变量生命周期
变量作用域范围
现象:python中,在if else重定义的变量,没有声明全局,在外部也可以使用,这里涉及到一个python变量生命周期的问题。
python能够改变变量作用域的代码段是def、class、lamda。
if/elif/else、try/except/finally、for/while 并不能涉及变量作用域的更改,也就是说他们的代码块中的变量,在外部也是可以访问的
变量搜索路径是:本地变量->全局变量
函数内部访问函数外全局变量
下面代码编译报错:local variable 'COUNT' referenced before assignment
1 | COUNT = 1 |
将注释删除执行成功
1 | COUNT = 1 |
装饰器
装饰器是 Python 中的高级功能,它可以用来修改或扩展函数或类的功能,有些类似java中的aop
装饰器的应用非常广泛,例如可以用来记录函数运行时间、缓存函数结果、验证函数参数等等
样例代码
1 | # 定义一个简单的装饰器 |
1 | # 如果decorator本身需要传入参数,那就需要编写一个返回decorator的高阶函数,写出来会更复杂。比如,要自定义log的文本: |
样例代码解释
先定义一个名为 my_decorator 的装饰器函数,它接受一个函数作为参数,然后返回一个新的函数 wrapper。wrapper 函数在调用被装饰的函数之前和之后打印一些文本。接着使用 @my_decorator 语法将 say_hello 函数传递给 my_decorator 装饰器,在调用 say_hello 函数时,实际上是调用了 wrapper 函数,从而在调用 say_hello 函数之前和之后打印了一些文本。
相对/绝对路径导包
1 | ValueError: attempted relative import beyond top-level package |
绝对导入
既然要介绍相对导入,那必然绕不开绝对导入。绝对导入的格式为 import A.B 或 from A import B
下面是绝对导入的一些 :
1 | import fibo # 隐式相对导入 |
相对导入
相对导入格式为 from .A import B 或 from ..X import Y, . 代表当前包, .. 代表上层包, ... 代表上上层包,依次类推。
相对导入的一些案例如下所示:
1 | from . import echo # 表示从当前文件所在package导入echo这个module |
相对导入基于当前模块的名称。由于主模块的名称始终为"__main__",因此用作 Python 应用程序主模块的模块必须始终使用绝对导入。主模块所在文件夹不会被视作package,因此除了主模块外,与主模块处在同个文件夹的模块(也就是同级的模块)也必须使用绝对导入。
还有个更专业的说法: 相对导入使用模块的名称属性来决定模块在包层次结构中的位置,如果模块的名称不包含任何包信息(例如:模块名称为'main'),那么相对导入则被解析为最顶层的位置,不管这个时候这个模块实际上位于文件系统中的什么位置。
包package
文件夹被python解释器视作package需要满足两个条件:
1、文件夹中必须有__init__.py文件,该文件可以为空,但必须存在该文件。
2、不能作为顶层模块来执行该文件夹中的py文件(即不能作为主函数的入口)。顶层模块即是我们通常说的"main"方法所在的模块及其同级模块,其中"main"也常被称为主模块,即主模块所在文件夹不会被视作package。主模块的同级package被python解释器视为顶级包(也就是top-level package)。
如果想要导入顶层包更上层的包或者模块,需要将包路径添加到sys.path中
第一点很容易理解,下面详细介绍一下第二点。
脚本 & 模块?(script vs module)
python有两种加载文件的方法:一种是作为顶层的脚本,另一种是当做模块。如果你直接执行这个程序,那么这个文件就被当做是顶层脚本来执行了,在命令行里面输入 python myfile.py 就是这个情况。如果你输入python -m myfile.py或者在其他的文件当中使用import来导入这个文件的时候,它就被当做模块来导入。在同一时间里,只有一个主模块,主模块常被称为顶层脚本,顶层脚本可以这样解释:它是一个能够让你的程序从这里开始的python文件。
将模块作为脚本执行
test_script.py
1 | # test_script.py |
脚本中的__main__="name"下的代码仅在模块作为“主”文件执行时才运行:
1 | $ python test_script.py |
如果module是作为导入的模块,则不会执行该模块的__main__代码:
1 | >>>import fibo |
这通常用于为模块提供方便的用户界面,或用于测试目的(将模块作为脚本执行测试套件运行)。
模块的名称
当一个文件被加载进来,它就有一个名称(这个名称存储在__name__属性当中)。如果这个文件被当做一个主模块来执行,那么它的名字就是__main__。如果它被当做一个模块加载,那么它的名称就是文件名称,加上它所在的包名,以及所有的顶层的包名,这些名称中间是用点号隔开的。
比如下面的例子
1 | package/ |
比如你导入moduleX(from package.subpackag1 import moduleX),它的名称就package.subpackage1.mouleX。如果你导入moduleA的时候(from package import moduleA),它的名称就是package.moudleA。
(注:这里是使用包导入,即把package以及里面的所有文件看做一个包,导入的时候使用from xxx import yyy的形式来进行,我们调用第三方包的时候就是这种情况),
但是,当你直接从命令行里面运行moduleX的时候,他的名称则被替换为__main__。如果你直接从命令行运行moduleA,它的名称也是__main__。当一个模块被当做一个顶层脚本来执行的时候,它原来的名称则会被__main__取代。
结论
当一个模块的名称中没有包,也就是只有文件名的时候,说明这个模块是一个顶层模块。顶层模块中不能使用相对导入。相对导入使用模块的名称属性来决定模块在包层次结构中的位置,相对导入能向上相对多少级,完全取决于模块名称中有多少层。
当你运行交互式的解释器的时候,交互式进程的名称永远是__main__,因此你不能在交互式进程当中使用相对导入。相对导入只能在模块文件当中使用。
