这篇文章 的内容时基于B站up主小甲鱼的视频写的,大家也可以去看看小甲鱼的视频,小甲鱼视频。
在Python中,if __ name __ == “__ main __”: 是一个常见的用法,其作用是判断当前模块是否作为主程序运行,还是被其他模块导入并调用。
具体作用如下
其中,tc模块是一个实现摄氏度转化为华氏度的模块,内容如下:
""" 摄氏度 -> 华氏度 """
def c2f(c):
f = c * 1.8 + 32
return f
def f2c(f):
c = (f - 32) / 1.8
return c
# 进行测试
print((f"测试, 0 摄氏度 = {c2f(0):.2f} 华氏度"))
print((f"测试, 0 华氏度 = {f2c(0):.2f} 摄氏度"))
运行结果如下:
测试, 0 摄氏度 = 32.00 华氏度
测试, 0 华氏度 = -17.78 摄氏度
运行结果会打印两条测试语句。
但当我们试图在另一个源文件导入该模块却会出现问题。
tc_test源文件中,代码如下:
import tc
print(f"32 摄氏度等于 {tc.c2f(32):.2f} 华氏度")
print(f"99 华氏度等于 {tc.f2c(99):.2f} 摄氏度")
结果如下:
测试, 0 摄氏度 = 32.00 华氏度
测试, 0 华氏度 = -17.78 摄氏度
32 摄氏度等于 89.60 华氏度
99 华氏度等于 37.22 摄氏度
tc_test源文件中导入了tc模块,并利用tc模块中的摄氏度转换函数执行了两条打印指令。
结果与我们预想的并不一样,我们仅有两行打印语句,但是运行代码得到的结果中,却多了两条打印执行结果,这是为什么呢?
这是因为模块在被源文件执行的过程中,源文件是会从头到尾执行一遍导入模块中的所有语句的。
而在tc模块中是有两条测试打印语句的,所以运行tc_test会多执行两条tc模块中的测试语句。
那么如何解决这个问题呢?
答案就是if__name__==“__main”
我们重新更改tc模块里的代码:
""" 摄氏度 -> 华氏度 """
def c2f(c):
f = c * 1.8 + 32
return f
def f2c(f):
c = (f - 32) / 1.8
return c
# 进行测试
print(__name__)
if __name__ == "__main__":
print((f"1 测试, 0 摄氏度 = {c2f(0):.2f} 华氏度"))
print((f"1 测试, 0 华氏度 = {f2c(0):.2f} 摄氏度"))
相比于之前的tc模块,我们这次加入打印__name__属性的方法,以及将原先的测试语句,放在if __ name __ == “__ main __”: 条件判断语句下面执行。
执行代码,结果如下:
main
1 测试, 0 摄氏度 = 32.00 华氏度
1 测试, 0 华氏度 = -17.78 摄氏度
可以看到测试语句是正常执行的,并且当tc模块被当作脚本执行的时候,(自己执行自己的内容,不作为模块导入到其他源文件中),__ name 属性被赋值为 main __
重新执行以下tc_test源文件。
import tc
print(f"32 摄氏度等于 {tc.c2f(32):.2f} 华氏度")
print(f"99 华氏度等于 {tc.f2c(99):.2f} 摄氏度")
结果如下:
tc
32 摄氏度等于 89.60 华氏度
99 华氏度等于 37.22 摄氏度
可以看到,当tc被作为模块导入到其他源文件当中的时候,tc的__name__属性会更改为模块名tc,此时tc模块里的判断语句 if __ name __ == “__ main __”: 将会被判断为假,判断语句下面的测试语句就不会再执行,那么这样就解决了在源文件tc_test中执行模块tc中的代码内容的问题。
我们先建立一个文件名为TC的文件夹,并在文件夹里放入一个tc1的模块
注意在文件夹里还需要创建__init__.py文件。
tc1模块的代码内容如下:
""" 摄氏度 -> 华氏度 """
def c2f(c):
f = c * 1.8 + 32
return f
def f2c(f):
c = (f - 32) / 1.8
return c
print(f"__name__值是 {__name__}")
代码定义了两个摄氏度华氏度转换的函数,最后打印__name__属性的值。
运行代码,结果如下:
__name__值是 main
接着,我们在TC文件夹外面我们建立一个tc1_test的源文件。
tc1_test源文件的代码内容如下:
import TC.tc1
print(f"32 摄氏度等于 {TC.tc1.c2f(32):.2f} 华氏度")
print(f"99 华氏度等于 {TC.tc1.f2c(99):.2f} 摄氏度")
代码导入了tc1模块,但是因为tc1模块与当前源文件(tc1_test)并不在同一个包(文件夹)内,所以在导入tc1模块时,要在tc1模块前面加上它所在包(TC)的前缀,即TC.tc1,另外,在源文件中调用tc1模块的函数时,也需要加上前缀。
运行代码,结果如下:
__name__值是 TC.tc1
32 摄氏度等于 89.60 华氏度
99 华氏度等于 37.22 摄氏度
结果中,tc1_test成功地调用了tc1模块的函数,并且tc1模块的__name__属性更改为TC.tc1。
这样我们就成功地导入了一个不同包内的模块。
__ init__ .py 文件是Python包中的一个特殊文件,其存在表示该目录是一个包。这个文件可以为空,也可以包含包级别的初始化代码。以下是关于 __ init __.py 文件的一些重要信息:
我们有一个名字为TC的包,并在包里面放入一个tc1模块,和__ init __文件,并在外面创建一个tc1_test文件。
在__init __文件中加入以下代码内容:
print(f"__init__.py 被调用,此时 __name__的值是 {__name__}")
加入了一条打印语句,执行代码结果如下:
init.py 被调用,此时 __name__的值是 main
我们再次执行tc1_test模块,结果如下:
__ init __.py 被调用,此时 __name__的值是 TC
__name__值是 TC.tc1
32 摄氏度等于 89.60 华氏度
99 华氏度等于 37.22 摄氏度
可以看到,结果与上次相比,多了一条__ init__被调用的指令,并且它的__name __ 值是TC,也就是说,当我们的TC包被导入的时候, __ init __ 文件就被调用,此时它的__ name __ 值是TC。
__ init __ 文件 作为负责初始化工作的文件,我们甚至可以在里边定义属于包的全局变量。
更改__ init __文件的内容为:
print(f"__init__.py 被调用,此时 __name__的值是 {__name__}")
x = 520
s = "Fishc"
print(f"__init__中x的值为{x}")
在__ init __ 文件中加了两个变量,x,s.
我们来试试在tc1中能不能使用这两个变量。
更改tc1的代码内容为:
""" 摄氏度 -> 华氏度 """
def c2f(c):
f = c * 1.8 + 32
return f
def f2c(f):
c = (f - 32) / 1.8
return c
print(f"__name__值是 {__name__}")
def printX():
import TC
print(TC.x)
if __name__ == "__main__":
printX()
在tc1中增加了一个函数,函数的作用是导入TC包,并打印TC的变量x.最后执行这个函数。
运行结果如下:
Traceback (most recent call last):
File “C:\Users\16713\PycharmProjects\ml\模块与包\中\TC\tc1.py”, line 11, in
print(TC.x)
NameError: name ‘TC’ is not defined
__name__值是 main
结果发生报错,这是因为tc1这个模块是在TC包里面的,在模块tc1中它自己是看不到这个TC包的。
所以,在同一个内,模块是不能使用包__init__中定义的全局变量的。
所以我们应该在TC 包外去尝试调用 __init __文件的全局变量。
更改 tc1_test 内容为:
import TC.tc1#导入不同文件夹中的模块,也就是导入其他包里的模块
print(f"32 摄氏度等于 {TC.tc1.c2f(32):.2f} 华氏度")
print(f"99 华氏度等于 {TC.tc1.f2c(99):.2f} 摄氏度")
TC.tc1.printX()
print(f"TC.s = {TC.s}")
在tc1_test中调用printX函数
结果如下:
init.py 被调用,此时 __name__的值是 TC
__init__中x的值为520
__name__值是 TC.tc1
32 摄氏度等于 89.60 华氏度
99 华氏度等于 37.22 摄氏度
520
TC.s = Fishc
可以看到TC包的全局变量x和s被包外的源文件tc1_test成功调用。
也就是说,一个包的全局变量只能由包外的文件的调用,不能被包内的文件调用。
我们也可以在tc_test中去修改TC模块中访问的TC.x的值。
更改tc_test代码为:
import TC.tc1
print(f"32 摄氏度等于 {TC.tc1.c2f(32):.2f} 华氏度")
print(f"99 华氏度等于 {TC.tc1.f2c(99):.2f} 摄氏度")
TC.tc1.printX()
print(f"TC.s = {TC.s}")
TC.x = 250
TC.tc1.printX()
在代码中,我们将TC.x的值从520更改为250,再进行打印。
结果如下:
init.py 被调用,此时 __name__的值是 TC
__init__中x的值为520
__name__值是 TC.tc1
32 摄氏度等于 89.60 华氏度
99 华氏度等于 37.22 摄氏度
520
TC.s = Fishc
250
可以看到在tc_test中,TC.x的值成功被修改为250。
值得注意的是,在__ init __中,x的值依旧是520,也就是说,在tc_test中修改TC.x的值,x的值只在tc_test中的改变,不会在 __ init __中发生改变,在其他文件中引用的x值也不会发生改变。
我们也可以在__init__ 中,导入特定的模块,那么,我们在包外导入包时,就会默认导入我们在__ init __中导入的模块。
如:
更改__init __内容为:
import tc1
print(f"__init__.py 被调用,此时 __name__的值是 {__name__}")
x = 520
s = "Fishc"
print(f"__init__中x的值为{x}")
在里面增加了一条导入tc1模块的语句
那么,在tc1_test中,我们想导入tc1模块的方式就可以只导入包,相当于省了一个步骤。
更改tc1_test代码为:
import TC
print(f"32 摄氏度等于 {TC.tc1.c2f(32):.2f} 华氏度")
print(f"99 华氏度等于 {TC.tc1.f2c(99):.2f} 摄氏度")
TC.tc1.printX()
print(f"TC.s = {TC.s}")
TC.x = 250
TC.tc1.printX()
代码中我们只导入了TC这个包,但并不会影响我们的运行结果。
运行结果如下:
__name__值是 tc1
init.py 被调用,此时 __name__的值是 TC
__init__中x的值为520
32 摄氏度等于 89.60 华氏度
99 华氏度等于 37.22 摄氏度
520
TC.s = Fishc
250
all 是一个特殊的属性,用于定义一个模块中哪些成员(变量、函数、类等)应该被导入到使用 from module import * 语句中。这个属性是一个包含字符串的列表,每个字符串都是模块中允许被导入的成员的名称。
主要作用有以下几点:
我们先在一个文件夹里创建如下两个源文件,分别时call_hello,和hello。
hello模块里面的内容如下:
__all__ = ["say_hello","x"]
x = 250
s = "FishC"
def say_hello():
print("hello fishc.")
def say_hi():
print("Hi Fishc.")
在hello模块里,我们定义了两个变量和两个函数,同时在__ all __属性的列表中,我们只放入say_hello函数和x变量。剩下的say_hi函数和s变量不放入。
让我们在call_hello中使用from … import * 中导入hello模块,并在里面尝试访问hello中的两个变量和两个函数,会发生什么样的结果。
代码如下:
from hello import *
print(x)
say_hello()
print(s)
say_hi()
结果如下:
250
hello fishc.
Traceback (most recent call last):
File “C:\Users\16713\PycharmProjects\ml\模块与包\模块与包中1\call_hello.py”, line 4, in
print(s)
NameError: name ‘s’ is not defined
可以看到,结果发生了报错,在__all__属性列表外的say_hi函数和s变量并没用被导入到call_hello中。
尝试在call_hello中使用import语句导入模块,看看会不会报错。
更改call_hello代码如下:
import hello as h
print(h.x)
h.say_hello()
print(h.s)
h.say_hi()
执行结果如下:
250
hello fishc.
FishC
Hi Fishc.
从结果中,我们可以看到hello模块中的变量和属性都被正常执行了,也就是说,__all__属性只针对 from … import * 语句起作用。
__ all __ 属性对于包 来说,与模块恰好相反,如果包里的__init __ 文件没有定义__all __属性,那么from…import * 的语句将不会导入包里面的任何模块。而对于模块来说,没有定义__all __属性,那么from…import * 的语句将会导入模块中所有的东西。
也就是说,当 __ all __ 属性存在于包的 __ init __.py 文件中时,它影响的是使用 from package import * 语句时导入的成员。以下是在包中使用 __ all __ 属性的主要作用:
我们在FC包里创建了三个模块,分别是,fc1,fc2,fc3,以及创建了构造模块__ init __ ,并在FC包外创建了FC_test源文件。
其中,fc1,fc2,fc3内容为空,__ init __文件代码为:
__all__ =["fc1","fc2"]
__ all __属性列表中没有fc3模块。
FC_test中代码内容如下:
from FC import *
print(dir())
代码中使用了from FC import * 语句导入FC中的包,再打印dir()函数的结果。
dir() 函数是一个内置函数,用于列出对象的所有属性和方法。它返回一个包含对象所有有效属性和方法名称的列表。这包括对象的内建属性、方法,以及通过继承或动态添加的属性和方法。
执行结果为:
[‘annotations’, ‘builtins’, ‘cached’, ‘doc’, ‘file’, ‘loader’, ‘name’, ‘package’, ‘spec’, ‘fc1’, ‘fc2’]
从结果中,我们可以看到,只有fc1,fc2,这两个模块被成功导入,而fc3 模块没有没导入,也就是说fc3模块是FC包私有的。通过__all __属性,将fc1,fc2,这两个模块,变为公有的,允许,包外其他源文件使用from FC import * 访问。