python 入门

Python

查看版本命令:

1
2
3
python -V

python --version

Python解释器

1
2
#!/usr/bin/python3
print("Hello, World!")
  1. 关于实例中第一行代码**#!/usr/bin/python3** 的理解:

    分成两种情况:

    (1)如果调用python脚本时,使用:

    1
    python script.py 

    #!/usr/bin/python 被忽略,等同于注释。

    (2)如果调用python脚本时,使用:

    1
    ./script.py 

    #!/usr/bin/python 指定解释器的路径。

    脚本语言的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它,就这么简单。

    #!/usr/bin/python3 是告诉操作系统执行这个脚本的时候,调用 /usr/bin 下的 python3 解释器;

    #!/usr/bin/env python3 这种用法是为了防止操作系统用户没有将 python3 装在默认的 /usr/bin 路径里。当系统看到这一行的时候,首先会到 env 设置里查找 python3 的安装路径,再调用对应路径下的解释器程序完成操作。

    #!/usr/bin/python3 相当于写死了 python3 路径;

    #!/usr/bin/env python3 会去环境设置寻找 python3 目录,推荐这种写法

From/Import

import my_module

from my_module import * 有什么区别?

在 Python 中,import my_modulefrom my_module import * 都是用于导入模块的语句,但它们在导入方式、使用方法以及潜在影响上都存在明显区别:

导入内容

  • import my_module:这种方式会将整个 my_module 模块导入到当前命名空间中,通过 my_module. 的方式来访问模块内的函数、类、变量等成员。 例如,假设 my_module 模块中有一个名为 add_numbers 的函数,要调用它就需要写成 my_module.add_numbers(3, 5)
  • from my_module import *:该语句会将 my_module 模块中所有定义的(没有以下划线 _ 开头的)函数、类、变量等直接导入到当前命名空间中,不需要通过模块名作为前缀, 可以直接使用。 比如,对于 my_module 里的 add_numbers 函数,导入后可以直接写 add_numbers(3, 5)

命名空间管理

  • import my_module:能较好地管理命名空间,避免命名冲突。因为模块内的所有成员都被封装在 my_module 这个命名空间下, 只有显式使用 my_module. 才能访问。 即使当前脚本中定义了与 my_module 内同名的变量或函数,也不会相互影响。
  • from my_module import *:会将模块中的所有成员直接暴露在当前命名空间中,如果当前脚本或其他已导入模块中存在同名的对象,就会产生命名冲突,导致程序运行出现意外结果。 例如,当前脚本中有一个名为 count 的变量,而 my_module 中也有一个 count 函数,使用 from my_module import * 导入后,原本的 count 变量就会被 count 函数覆盖。

性能影响

  • import my_module:在导入模块时,Python 解释器只是将模块对象加载到内存中,并在需要时通过模块名去访问其中的成员,相对来说比较高效。
  • from my_module import *:虽然在使用上较为便捷,但解释器需要遍历模块中的所有成员,并将它们逐个导入到当前命名空间,在处理大型模块时, 可能会消耗更多的时间和资源。

假设 my_module.py 中有以下内容:

1
2
3
4
5
# my_module.py
def add(a, b):
return a + b

PI = 3.14159
(1)使用 import my_module 时:

必须通过 模块名.成员名 的方式调用,成员被 “包裹” 在模块的命名空间中:

1
2
3
4
5
6
7
8
9
10
import my_module

# 调用函数:模块名.函数名
result = my_module.add(2, 3) # 正确,返回 5

# 访问变量:模块名.变量名
print(my_module.PI) # 正确,输出 3.14159

# 直接调用会报错(未通过模块名)
result = add(2, 3) # 错误:NameError: name 'add' is not defined
(2)使用 from my_module import * 时:

模块中的成员(非下划线开头的)会被直接导入当前命名空间,可直接通过成员名调用,无需模块名前缀:

1
2
3
4
5
6
7
from my_module import *

# 直接调用函数,无需模块名
result = add(2, 3) # 正确,返回 5

# 直接访问变量,无需模块名
print(PI) # 正确,输出 3.14159

requirements.txt

requirements.txt 是 Python 项目中用于记录依赖包及其版本的配置文件,方便在不同环境中快速安装相同的依赖,确保项目运行环境一致。

1
2
# 在项目根目录执行,将当前环境所有已安装的包导出
pip freeze > requirements.txt

注意

  • pip freeze 会导出当前环境中所有安装的包(包括项目无关的),可能包含冗余内容。

  • 工具(需先安装:pip install pipreqs),它会扫描项目代码,只导出实际使用的依赖:

    1
    pipreqs . --encoding=utf8  # 在项目根目录执行,--encoding 解决中文编码问题
  • 每个项目单独维护 requirements.txt,避免全局环境污染(建议结合虚拟环境 venvconda 使用)。

n = yield r

n = yield r 是生成器(协程)中最核心的一行代码,它的执行逻辑比较特殊,需要拆分来看——这不是一个简单的赋值语句,而是“先返回,后接收”的两步操作

拆分理解

“返回”和“接收”两个动作 可以把 n = yield r 拆成两个逻辑步骤:

第一步:执行 yield r:生成器暂停,返回 r 的值给调用者(比如通过 send()next() 调用的地方)。

第二步:接收值并赋值给 n:当生成器被再次唤醒(比如调用 send(value) 时),会将 send() 传入的 value 赋值给 n,然后继续执行后面的代码。

用“时间线”理解

第一次启动生成器c.send(None)): - 生成器执行到 n = yield r 时,先执行 yield r:返回 r 的初始值 '',然后生成器暂停(停在这一行,不再往下走)。 - 注意:此时 n 还没有被赋值(因为还没收到任何值)。

当生产者调用 c.send(1): - 生成器从暂停的 n = yield r恢复,将 send(1) 传入的 1 赋值给 n。 - 接着执行后面的代码(if not n: ...、打印消费信息、给 r 赋值 '200 OK')。 - 再次进入循环,回到 n = yield r 这一行,执行 yield r:返回 r 的新值 '200 OK',然后生成器再次暂停,等待下一次唤醒。

后续每次 send(n) 都会重复第二步: - 唤醒生成器 → 把传入的值给 n → 执行代码 → 到 yield r 处返回 r 并暂停。

3.

一句话总结:

n = yield r 可以读作: “先返回 r 的值,然后暂停;等下次被唤醒时,接收一个值并赋给 n,再继续执行”。 它的关键在于:**yield 的返回动作发生在赋值给 n 之前**,两者不是同时执行的,而是被“暂停-唤醒”分割成了两个阶段。这也是生成器能实现“双向通信”的核心原因——既可以返回数据,又能接收外部传入的数据。