当我们在使用Python进行一些操作时,如果我们希望我们封装的模块里的方法能够在给使用者调用的同时可以获取到调用者当前正在执行的Python脚本名,以做一些后续操作。这听起来好像跟反射有异曲同工之处。反射可以实现吗?
来看一个需求:
现有一个用于封装工具API的模块:module.py
,如下:
def do():
...
# 处理中间过程
...
return 'result'
该模块中有一个方法do()
供调用者调用。现在有一个需求:该方法中的处理中间过程
需要获取到调用者当前执行的Python脚本名,并做一些处理操作,最后返回给调用者结果
这种情况需要如何实现呢?可以尝试以下办法:
2.1、方式1:使用反射?
在Java中,我们知道反射是指在类加载时动态获取类的成员(包括类、属性、方法等),以对类和对象进行修改。在Python中,这类似。反射可以动态获取对象的属性和方法,但对于Python脚本,好像和它没啥关系吧?更何况Python脚本名呢
2.2、方式2:使用os模块?
Python内置的os模块提供一些文件和路径处理的方法,兴许这里面有可以帮我们完成这个需求的方法呢
os模块中的__file__
属性可用于返回当前脚本的文件路径,包括文件名,结合os.path.basename()
是否可以获取调用者的脚本名呢?马上进行下面尝试:
工具API模块:module.py
:
import os
def do():
print(os.path.basename(__file__))
调用者模块:client.py
:
from module import do
do()
'''
module.py
'''
什么情况?我要获取的是调用者脚本名,我需要你打印出来的是client.py
,而不是module.py
从结果可见,这种方式不太行啊
2.3、使用sys模块?
Python内置的sys模块主要用于Python解释器和系统之间的交互,例如获取系统环境变量。sys可以实现吗?
sys提供了一个函数sys._getframe()
用于查看函数被什么函数调用及被第几行调用以及被调用函数所在的文件
sys._getframe(depth)
从调用堆栈返回一个框架对象。 如果给定了可选整数depth,则返回在堆栈顶部以下调用多次的框架对象。depth的默认值为0,返回调用堆栈顶部的帧。如果参数比调用堆栈更深,则会引发ValueError异常
sys._getframe()
的更多使用见文末总结
来试一下吧
工具API模块:module.py
:
import os
import sys
def do():
print(os.path.basename(sys._getframe().f_back.f_code.co_filename))
调用者模块:client.py
:
from module import do
do()
'''
client.py
'''
嗯嗯,sys模块的sys._getframe()
可以帮我们完成上述需求,但较为复杂,不好理解
2.4、sys获取argv
sys提供的sys.argv[]
可用于访问命令行参数。然而,sys.argv[0]
可用于获取正在执行的Python脚本本身
这可能是最好的一种方式。示例如下:
工具API模块:module.py
:
import os
import sys
def do():
print(os.path.basename(sys.argv[0]))
调用者模块:client.py
:
from module import do
do()
'''
client.py
'''
这不是更完美吗?
sys._getframe()
的常用使用总结如下:
'''
sys._getframe(0) # 被调用模块层
sys._getframe(1) # 调用者模块层
sys._getframe().f_back # 调用者模块层,和sys._getframe(1)相同
sys._getframe(0).f_code.co_filename # 被调用模块当前文件名,也可以通过__file__获得
sys._getframe(1).f_code.co_filename # 调用者模块当前文件名,和sys._getframe().f_back.f_code.co_filename相同
sys._getframe(0).f_code.co_name # 被调用模块层当前函数名
sys._getframe(1).f_code.co_name # 调用者模块层调用所在函数名,如果没有,则返回
sys._getframe(0).f_lineno # 被调用模块层被调用函数的行号
sys._getframe(1).f_lineno # 调用者模块层调用该函数所在的行号
'''