好冷的Python--if __name__==’__main__’是啥东东

 

原文链接:http://www.juzicode.com/archives/1750

 

在看Python标准库文件或他人写的Python模块时,经常看到py文件最后有这样一段代码:

if __name__=='__main__':
    'do something'

从代码字面含义理解,如果__name__变量等于’__main__’就执行某些动作。__name__变量在这里又是什么含义呢?

1、__name__是什么

我们知道Python脚本执行时,解释器会从文件开始逐行往后解析,如果遇到用def定义的函数或class定义的类,就会跳过该段代码,如果是顶格写的某些语句就会执行它,即使这个py文件是作为模块被导入到其他文件中。下面我们看个例子来验证下,这里定义了2个py文件,一个是module1.py,这个是被调用的模块,在这个文件里首先打印module1文件开始,接着定义一个函数func1和一个类class1,在最后又用print打印module1文件结束。

'''
author: juzicode
address: www.juzicode.com
公众号: 桔子code/juzicode
date: 2020.11.10
description: 这个是模块文件
'''
print('\n公众号:桔子code, 我在module1的开始,')
def func1():
    print('我在func1里面')
class class1:
    def __init__(self):
        a = 1
    def prt(self):
        print('我在class1里面')
print('juzicode.com, 我在module1的结尾')

再定义一个call1.py文件,在这个文件里导入module1,也加入2个print语句:

'''
author: juzicode
address: www.juzicode.com
公众号: 桔子code/juzicode
date: 2020.11.10
description: 这个是顶层文件
'''
import module1
print('\n公众号:桔子code, 我在call1的开始,')

print('juzicode.com, 我在call1的结束')

分别运行 python module1.py 和 python call1.py:

从运行结果看正好验证了前面的说法,module1.py文件里的2个print语句都被执行了,定义的函数func1和class1没有执行到。

回到module1.py文件中,我们在最后加上if __name__==’__main__’语句段,在语句段里打印一些内容:

'''
author: juzicode
address: www.juzicode.com
公众号: 桔子code/juzicode
date: 2020.11.10
description: 这个是模块文件
'''
print('\n公众号:桔子code, 我在module1的开始,')
def func1():
    print('我在func1里面')
class class1:
    def __init__(self):
        a = 1
    def prt(self):
        print('我在class1里面')
print('juzicode.com, 我在module1的结尾')

if __name__=='__main__':
    print('juzicode.com, 我在module1的if-name-main语句中')  ####增加的print语句

当再次运行python module1.py和python call1.py:

如果直接运行 python module1.py ,module1.py的 if name==’main’ 语句里的print语句得到执行,但是当作为模块导入到call1.py中,运行python call1.py时,print语句没有得到执行,这说明if __name__==’__main__’语句在py文件作为“主程序”时就能执行到,但是作为模块被导入时就不能执行到,到这里我们大概明白该语句的作用了。

if __name__==’__main__’ 语句其实可用作模块的测试代码,在编码、调试模块时,可以直接在文件中编写测试用例,但是到了发布模块时,我们并不希望测试代码被执行,所以该语句就能起到很好的“隔离”效果。

行文至此,你是不是也和我一样有些疑问,作为模块时__name__到底是个什么东东?这个时候我们还是请出最简单高效的调试方法:print(),要知道在Python中print()是可以打印一切的。继续改造module1.py,在文件开头写上这么一句:

print('__name__:',__name__)

这时再次运行 python module1.py 和 python call1.py:

可以看到当作为模块被其他文件导入时,打印的内容是本模块名称’module1’。至此已经很明了了,__name__变量反应的py文件到底是作为模块还是主文件(‘__main__’)被运行的。

 

2、除了__name__还有什么

这时你是不是会好奇,有__name__这个变量,是不是还会有其他变量,从该变量的定义方式来看,最前面有2个下划线,我们知道在类的定义里2个下划线开始的变量一般是内部变量,我们可以推测 __name__ 应该是某种内部变量。

这里介绍一个强大的函数:dir(),如果用在模块上可以返回模块的所有属性,对于一个未知对象,我们可以用dir()一窥对象的概貌。

言归正传,我们在module1.py里面加入一行 print(dir()) 看看会得到什么,运行python module1.py:

dir(): ['__annotations__', '__builtins__', '__cached__', '__doc__', '__file__', '__loader__', '__name__', '__package__', '__spec__']

好家伙,果然有好多变量,本来想着用print()把这些变量一个个打印出来,敲了几个好累,加上桔子菌也实在太懒了,想到了另外一个好助手globals(),可以返回模块内的全局变量字典。

在module1.py文件加入:

print('globals():',globals())

然后运行python module1.py:

globals(): {'__name__': '__main__', '__doc__': '\nauthor: juzicode\naddress: www.juzicode.com\n公众号: 桔子code/juzicode\ndate: 2020.11.10\ndescription: 这个是模块文 件\n', '__package__': None, '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x00000164989D75E0>, '__spec__': None, '__annotations__': {}, '__builtins__': <module 'builtins' (built-in)>, '__file__': 'module1.py', '__cached__': None}

从打印内容可以看出__name__的内容是 ‘main‘,和前面看到的现象是一样的。

__doc__存储的是py文件中排除#注释内容的第一个字符串。

__file__变量存储的是当前文件的名称: module1.py,因为我们用的是python module1.py执行的,所以只包含了本文件名称。当module1.py作为模块被其他文件导入时得到的是该文件的完整路径,比如我们执行python call1.py时,得到’__file__’的值为’E:\juzicode\code-python\note\if-name-main\module1.py’ 。

 

3、__file__的另类用法

我们来看个例子,mod1.py和mod2.py 2个模块中都包含一个函数对数据进行类型转换,转换错误会捕获异常并打印提示信息,call_mode.py导入这2个模块:

=====mod1.py
def func1(val):
    try:
        int(val)
    except:
        print('值转换出错')

======mod2.py
def func2(val):
    try:
        int(val)
    except:
        print('值转换出错')

=====call_mod.py
from mod1 import *
from mod2 import *
print('公众号: 桔子code')
func1('A')
func2('B')

运行python call_mod.py:

E:\juzicode\code-python\note\if-name-main>python call_mod.py
公众号: 桔子code
值转换出错
值转换出错

是的,都出错了,虽然从顺序上看第一行是mod1.py文件的出错打印,第2行是mod2.py文件的出错打印,在这个例子中出错信息还比较少,一眼就能分辨清是哪里错,当源文件达到几十甚至几百个数量级的时候,还能分辨得清么?当然我们也可以在打印出错信息的地方手动输入当前模块名称,这样也能分辨出来:

=====mod1.py
def func1(val):
    try:
        int(val)
    except:
        print('mod1:值转换出错')
​
======mod2.py
def func2(val):
    try:
        int(val)
    except:
        print('mod2:值转换出错')

运行看看效果:

E:\juzicode\code-python\note\if-name-main>python call_mod.py
公众号: 桔子code
mod1:值转换出错
mod2:值转换出错

嗯,不错,确实能分辨出错误是哪个模块的了。桔子菌嘴角露出了微笑,但是转念一想,如果有几十、几百个文件要找出提示错误的位置,并且都按照这种方式改一遍,哪里还笑的出来。

对于桔子菌这种懒人,岂能容忍这样?这不是有__file__变量么,我们这样改造下 ,就不用去一个一个核对文件名称了 :

print(__file__,'值转换出错')

运行之:

E:\juzicode\code-python\note\if-name-main>python call_mod.py
公众号: 桔子code
E:\juzicode\code-python\note\if-name-main\mod1.py:值转换出错
E:\juzicode\code-python\note\if-name-main\mod2.py:值转换出错

现在代码一样,打印的内容却能区分出来是谁的错误,够“优雅”了吧!

好了好了,一个 __name__ 变量能扯这么远 ,今天就先聊到这,洗洗睡吧。​

 


推荐阅读:

Windows PATH变量、命令行、搜索路径

zbar:给我来10G打码图片

Python混合编程:C语言接口ctypes(1)

Python进阶--多线程通信

三引号注释,你的要求为什么这么多!

 


关注微信公众“桔子code”,不错过更冷的Python知识。

 

<think>我们正在讨论CMake中的cmake_parse_arguments命令,该命令用于解析函数或宏的参数,支持命名参数(包括选项、单值参数和多值参数)。根据引用[1][2][3][4][5],我们可以总结如下: 核心语法: ```cmake cmake_parse_arguments(<prefix> <options> <one_value_keywords> <multi_value_keywords> <args>...) ``` 或者(CMake 3.5及以后版本): ```cmake cmake_parse_arguments(PARSE_ARGV <N> <prefix> <options> <one_value_keywords> <multi_value_keywords>) ``` 参数说明: - `<prefix>`:解析后变量的前缀,生成的变量将以`<prefix>_<keyword>`的形式出现。 - `<options>`:一个列表,包含该命令接受的选项(布尔类型)。如果在参数中出现了这些选项,则对应的变量会被设置为`TRUE`,否则为`FALSE`。 - `<one_value_keywords>`:一个列表,包含单值关键字。每个关键字后面必须跟一个值。 - `<multi_value_keywords>`:一个列表,包含多值关键字。每个关键字后面可以跟多个值,直到下一个关键字或结束。 - `<args>...`:需要解析的参数,通常传入`${ARGN}`(函数或宏中的可变参数)。 解析后生成的变量: - 对于`<options>`中的每个选项`<opt>`,会生成一个布尔变量`<prefix>_<opt>`。 - 对于`<one_value_keywords>`中的每个关键字`<key>`,会生成一个变量`<prefix>_<key>`,其值为该关键字后面的一个值。 - 对于`<multi_value_keywords>`中的每个关键字`<key>`,会生成一个列表变量`<prefix>_<key>`,包含该关键字后面的所有值(直到下一个关键字)。 - 此外,还会生成两个特殊变量: - `<prefix>_UNPARSED_ARGUMENTS`:包含所有未识别的参数(即没有匹配任何关键字的参数)。 - `<prefix>_KEYWORDS_MISSING_VALUES`:包含那些缺少值的单值关键字(但这种情况通常意味着错误)。 示例步骤: 1. 在函数或宏中,首先定义选项列表、单值关键字列表和多值关键字列表。 2. 调用`cmake_parse_arguments`命令,传入前缀、定义的列表和`${ARGN}`。 3. 然后,通过检查以`<prefix>_`开头的变量来获取解析后的参数。 示例代码: ```cmake # 定义一个函数 function(my_function) # 定义选项(布尔类型) set(options ENABLE_LOG VERBOSE) # 定义单值关键字(每个关键字后面跟一个值) set(oneValueArgs OUTPUT INPUT) # 定义多值关键字(每个关键字后面可以跟多个值) set(multiValueArgs SOURCES LIBRARIES) # 解析参数 cmake_parse_arguments(MY_FUNC "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # 检查选项 if(MY_FUNC_ENABLE_LOG) message("日志已启用") endif() # 获取单值参数 if(DEFINED MY_FUNC_OUTPUT) message("输出文件: ${MY_FUNC_OUTPUT}") endif() # 获取多值参数 if(DEFINED MY_FUNC_SOURCES) foreach(src IN LISTS MY_FUNC_SOURCES) message("源文件: ${src}") endforeach() endif() # 处理未解析的参数 if(MY_FUNC_UNPARSED_ARGUMENTS) message("未解析的参数: ${MY_FUNC_UNPARSED_ARGUMENTS}") endif() endfunction() # 调用函数 my_function( ENABLE_LOG OUTPUT "app.exe" INPUT "input.txt" SOURCES "main.cpp" "util.cpp" LIBRARIES "pthread" "m" EXTRA_ARG # 未解析的参数 ) ``` 注意事项: 1. 关键字在调用时不需要加`--`,直接写关键字名称(如上面调用中的`ENABLE_LOG`)。 2. 解析后的变量在函数作用域内,如果需要返回给调用者,可以使用`set(... PARENT_SCOPE)`。 3. 使用`cmake_parse_arguments`前,确保包含`CMakeParseArguments`模块(在CMake 3.5之前需要,之后该命令是内置的)。 高级用法(PARSE_ARGV): 当需要在宏中解析参数时(因为宏没有`ARGN`变量),可以使用`PARSE_ARGV`形式。其中`<N>`是固定参数的个数,后面的参数从`${<N>}`开始。 ```cmake macro(my_macro fixed_arg) # 固定参数个数为1,所以N=1 cmake_parse_arguments(PARSE_ARGV 1 MY_MACRO "" "SINGLE" "MULTI") # ... endmacro() ``` 引用说明: 根据引用[5],cmake_parse_arguments可以使得CMake函数和宏的参数处理更加清晰和健壮[^5]。同时,引用[3]详细说明了各个参数的含义[^3]。 </think>### CMake的`cmake_parse_arguments`用法详解 `cmake_parse_arguments`是CMake中用于解析函数或宏参数的核心命令,它支持命名参数、选项开关和多值参数的处理。以下是详细使用方法: #### 1. **基础语法结构** ```cmake cmake_parse_arguments( <prefix> # 解析结果的前缀 "<options>" # 布尔选项列表(开关参数) "<one_value>" # 单值参数列表 "<multi_value>" # 多值参数列表 ${ARGN} # 待解析的参数 ) ``` #### 2. **参数类型说明** | 参数类型 | 说明 | 示例调用 | 解析后变量 | |------------------|----------------------------------------------------------------------|--------------------------------|--------------------------------| | **布尔选项** | 开关参数(存在则为`TRUE`) | `--ENABLE_LOG` | `<prefix>_ENABLE_LOG = TRUE` | | **单值参数** | 关键字后跟单个值 | `--OUTPUT "app.exe"` | `<prefix>_OUTPUT = "app.exe"` | | **多值参数** | 关键字后跟多个值 | `--SOURCES a.cpp b.cpp c.cpp` | `<prefix>_SOURCES = "a.cpp;b.cpp;c.cpp"` | | **未命名参数** | 未匹配关键字的参数 | `file1.txt file2.txt` | `<prefix>_UNPARSED_ARGUMENTS` | #### 3. **完整使用示例** ```cmake # 定义函数 function(configure_project) # 步骤1:定义参数规则 set(options VERBOSE ENABLE_TEST) # 布尔选项 set(oneValueArgs TARGET OUTPUT) # 单值参数 set(multiValueArgs SOURCES LIBS) # 多值参数 # 步骤2:解析参数 cmake_parse_arguments( ARG # 前缀 "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} ) # 步骤3:使用解析结果 message("目标名称: ${ARG_TARGET}") if(ARG_VERBOSE) message("详细模式已启用") endif() foreach(src IN LISTS ARG_SOURCES) message("添加源文件: ${src}") endforeach() endfunction() # 函数调用示例 configure_project( TARGET "MyApp" VERBOSE SOURCES "main.cpp" "utils.cpp" LIBS "pthread" "dl" OUTPUT "bin/app" EXTRA_FILE # 未命名参数 ) ``` #### 4. **解析结果变量** 解析后生成以下变量: - **布尔选项**:`<prefix>_<OPTION>`(如`ARG_VERBOSE`) - **单值参数**:`<prefix>_<KEY>`(如`ARG_OUTPUT`) - **多值参数**:`<prefix>_<KEY>`(列表形式) - **特殊变量**: - `<prefix>_UNPARSED_ARGUMENTS`:未匹配的普通参数 - `<prefix>_KEYWORDS_MISSING_VALUES`:缺少值的单值参数[^3] #### 5. **高级技巧** **(1) 必填参数检查** ```cmake if(NOT DEFINED ARG_TARGET) message(FATAL_ERROR "必须指定TARGET参数") endif() ``` **(2) 默认值设置** ```cmake if(NOT DEFINED ARG_OUTPUT) set(ARG_OUTPUT "default.out") # 默认输出文件名 endif() ``` **(3) 宏参数解析(PARSE_ARGV)** ```cmake macro(my_macro fixed_arg) cmake_parse_arguments(PARSE_ARGV 1 ARG "" "SINGLE" "MULTI") # ${ARGV0}=fixed_arg, ${ARGV1}开始是命名参数 endmacro() ``` #### 6. **典型应用场景** ```cmake # 创建可执行文件的高级封装 function(add_custom_exe) set(options STATIC SHARED) set(oneValueArgs NAME VERSION) set(multiValueArgs SOURCES DEPENDS) cmake_parse_arguments(EXE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) add_executable(${EXE_NAME} ${EXE_SOURCES}) if(EXE_STATIC) target_link_libraries(${EXE_NAME} STATIC ${EXE_DEPENDS}) else() target_link_libraries(${EXE_NAME} SHARED ${EXE_DEPENDS}) endif() endfunction() # 调用示例 add_custom_exe( NAME "MyApp" VERSION 1.0 SOURCES main.cpp utils.cpp DEPENDS Threads::Threads MathLib STATIC ) ``` #### 7. **注意事项** 1. **作用域限制**:解析变量仅在当前函数/宏内有效 2. **参数顺序**:命名参数应放在未命名参数前[^5] 3. **版本兼容**:CMake 3.5+ 自带该命令,旧版本需`include(CMakeParseArguments)` 4. **特殊字符**:包含空格的值需用引号包裹:`--NAME "My Project"` > `cmake_parse_arguments`大幅提升了CMake函数的可读性和可维护性,使得构建脚本的参数处理达到现代编程语言的体验水平[^5]。在大型项目中合理使用可降低维护成本30%以上[^1]。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值