小甲鱼 模块与包 中 笔记

小甲鱼笔记 模块与包 中

    • 一 if __name__=="__main__"
      • 1.1 作为主程序运行
      • 1.2 被其他模块导入时不执行:
      • 1.3 示例
    • 二 包的概念
      • 2.1 目录结构:
      • 2.2 命名空间:
      • 2.3 包的层次结构:
      • 2.4 模块的导入:
      • 2.5 __ init __.py 文件
      • 2.6 示例
    • 三 __ init __作用详解
      • 3.1包的标识:
      • 3.2 包的初始化代码:
      • 3.3 空的 __ init__ .py 文件:
      • 3.4 示例
    • 四 __ all __ 属性对模块的作用
      • 4.1 限制导入的成员
      • 4.2 提供公共接口
      • 4.3 避免命名冲突:
      • 4.4 示例
    • 五 __ all __属性对包的作用。
      • 5.1 限制导入的公共接口:
      • 5.2 提供清晰的包接口:
      • 5.3 防止导入私有模块或子包:
      • 5.4 示例

这篇文章 的内容时基于B站up主小甲鱼的视频写的,大家也可以去看看小甲鱼的视频,小甲鱼视频。

一 if name==“main

在Python中,if __ name __ == “__ main __”: 是一个常见的用法,其作用是判断当前模块是否作为主程序运行,还是被其他模块导入并调用。
具体作用如下

1.1 作为主程序运行

  • 当 Python 脚本被直接执行时,__ name__ 的值会被设置为 “__ main __”。
  • 如果脚本中包含 if __ name __ == “__ main __”: 这样的条件语句,其中的代码块将只在该脚本作为主程序运行时执行。
  • 这使得你可以将一些代码片段放在这个条件下,作为脚本的入口点,而不会在被其他模块导入时执行。

1.2 被其他模块导入时不执行:

  • 当一个模块被导入时,__ name __ 的值被设置为模块的名称,而不是 “__ main __”。
  • 这意味着在被其他模块导入时,包含在 if __ name __ == “__ main __”: 条件下的代码块将不会执行,避免不必要的代码执行。

1.3 示例

我们先在文件夹里创建两个源文件 分别是tc和tc_test
在这里插入图片描述

其中,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中的代码内容的问题。

二 包的概念

  • 在Python中,包(Package)是一种用于组织模块的层次结构。它是一个包含模块和子包的文件夹,用于更好地组织和管理代码。包的目的是将相关的模块分组在一起,使得代码更加模块化和可维护。
    以下是关于Python包的一些基本概念和特点:

2.1 目录结构:

  • 包是一个包含特殊文件 __ init __.py 的目录。这个文件可以为空,但它必须存在,以表示这个目录是一个包。
  • 包可以包含子包,子包也是一个包含 __ init __.py 文件的目录。

2.2 命名空间:

  • 包提供了一种命名空间的层次结构。每个包都有自己的命名空间,防止模块名称冲突。
  • 通过使用包,你可以在代码中更清晰地引用模块,如 import mypackage.mymodule。

2.3 包的层次结构:

  • 包可以嵌套,形成层次结构。例如,mypackage 可能包含子包 subpackage,子包中再包含模块。
  • 这种层次结构有助于组织大型项目,并使代码更易于理解和维护。

2.4 模块的导入:

  • 使用 import 语句可以导入包中的模块。语法格式是import 包名.模块名例如,import mypackage.mymodule。
  • 还可以使用 from 语句导入特定的模块。语法格式是from 包名 import 模块名 ,如 from mypackage import mymodule

2.5 __ init __.py 文件

  • __ init __.py 文件是一个可选的包初始化文件。它可以包含包级别的初始化代码,也可以为空。
  • 当你导入一个包时,__ init __.py 文件会在导入其他模块之前执行。
  • 后面我们会详细介绍这个文件的作用。

2.6 示例

我们先建立一个文件名为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的源文件。
小甲鱼 模块与包 中 笔记_第1张图片
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 __作用详解

__ init__ .py 文件是Python包中的一个特殊文件,其存在表示该目录是一个包。这个文件可以为空,也可以包含包级别的初始化代码。以下是关于 __ init __.py 文件的一些重要信息:

3.1包的标识:

  • __ init __.py 文件的存在是告诉Python解释器这个目录应该被视为一个包,而不仅仅是一个包含模块的普通目录。
  • 在Python 3.3及之后的版本中,__ init __.py 文件是可选的,但为了保持向后兼容性和清晰性,建议在包中包含这个文件。

3.2 包的初始化代码:

  • __ init __.py 文件可以包含包级别的初始化代码。这些代码在导入包时会被执行,通常用于执行一些初始化工作或者设置包的属性。
  • 例如,你可能会在 __ init __.py 中定义一些常量、初始化一些变量,或者导入该包中常用的模块,以便在其他模块中直接使用。

3.3 空的 __ init__ .py 文件:

  • 如果你的包不需要包级别的初始化工作,可以简单地将 __ init __.py 文件保持为空。

3.4 示例

继续用上面的例子
小甲鱼 模块与包 中 笔记_第2张图片

我们有一个名字为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 __ 属性对模块的作用

all 是一个特殊的属性,用于定义一个模块中哪些成员(变量、函数、类等)应该被导入到使用 from module import * 语句中。这个属性是一个包含字符串的列表,每个字符串都是模块中允许被导入的成员的名称。

主要作用有以下几点:

4.1 限制导入的成员

  • 当模块中存在 __ all __ 属性时,使用 from module import * 语句导入模块时,只有 __ all __ 中指定的成员会被导入,其他成员不会被导入。
  • 这有助于控制模块的公共接口,避免导入过多不需要的成员。

4.2 提供公共接口

  • 通过使用 __ all __,模块的作者可以明确指定哪些成员是模块的公共接口,这样使用者可以更容易地了解和使用模块。

4.3 避免命名冲突:

  • 通过明确指定 __ all __,模块作者可以避免导入的成员与模块中的其他成员发生命名冲突。这在大型项目中特别有用。

4.4 示例

我们先在一个文件夹里创建如下两个源文件,分别时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 __属性对包的作用。

__ all __ 属性对于包 来说,与模块恰好相反,如果包里的__init __ 文件没有定义__all __属性,那么from…import * 的语句将不会导入包里面的任何模块。而对于模块来说,没有定义__all __属性,那么from…import * 的语句将会导入模块中所有的东西。
也就是说,当 __ all __ 属性存在于包的 __ init __.py 文件中时,它影响的是使用 from package import * 语句时导入的成员。以下是在包中使用 __ all __ 属性的主要作用:

5.1 限制导入的公共接口:

  • 在包的 __ init __.py 文件中使用 __ all __ 可以限制从包中导入的公共接口。这有助于控制包的公共成员,使其更易于维护和理解。

5.2 提供清晰的包接口:

  • 通过在 __ init __.py 中设置 __ all __,包的作者可以为包明确定义一个清晰的公共接口,让使用者更容易了解包的功能和如何使用它。

5.3 防止导入私有模块或子包:

  • 如果包内有一些模块或子包是内部使用的,而不希望被外部使用者导入,可以使用 __ all __ 来防止它们被导入。
  • 私有模块或子包通常可以在 __ all __ 中不列出,从而限制其可见性。

5.4 示例

我们在FC包里创建了三个模块,分别是,fc1,fc2,fc3,以及创建了构造模块__ init __ ,并在FC包外创建了FC_test源文件。
小甲鱼 模块与包 中 笔记_第3张图片
其中,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 * 访问。

你可能感兴趣的:(python基础,笔记,python)