python 历史 🔗
简单易用。
1989 年荷兰人Guido von Rossum开始写 Python 语言的解释器。
1994 年 1 月:Python 1.0 正式发布
2000 年 10 月 Python 2.0 发布,
2008 年 12 月 Python 3.0 发布,不完全兼容之前的 Python 代码
代码中import sys sys.version_info, sys.version 模块查询 python 版本信息。
Python Enhancementt Proposal 8 🔗
PEP 8 对于 python 代码格式的指南。 pylint python 的源码静态分析工具, 检查代码是否符合 PEP 8 的规范。
python 解释器 🔗
python, cpython 解释器 (最常见的实现方式)。
特殊语法 🔗
python data model 🔗
pythonic
获取对象的大小,用 len(collection), 而不是 collection.len().
obj[key] 解释器转为了 obj.__getitem__(key)
Python Data Model
想让对象支持以下基本的语言结构, 需要实现特色方法: 容器(collections), 属性(attribute)访问, 迭代(包括用 async for 的异步迭代), 运算符重载, 函数和方法调用,字符串表示与格式化,使用 await 的异步编程,对象创建与销毁, 用 with 或者 async with 语句管理上下文。
== 比较的是对象的内容, is 比较的是对象的 id。
list(l1), l1[:] 浅拷贝。
每个对象有 id,类型和值。
函数是对象,是 function 类的实例对象。 9 种可调用的对象。函数,生成器,协程,
函数参数 🔗
函数参数*arg代表可变参数,类型为元组。
**kwargs代表关键字参数,可以接收用参数名=参数值的方式传入的参数,传入的参数的会打包成一个字典。
默认参数的值是函数定义时计算的,可能会导致某些陷阱,尤其是在可变对象(如列表或字典)作为默认值时。
def append_item(item, item_list=[]): # 默认列表是可变的
item_list.append(item)
return item_list
print(append_item(1)) # [1]
print(append_item(2)) # [1, 2],问题:第二次调用时默认值已经被修改
def append_item(item, item_list=None):
if item_list is None:
item_list = []
item_list.append(item)
return item_list
内部特殊函数 🔗
名称前后都有双下划线, 例如:__new__方法在创建对象时被调用,比__init__更早执行,主要用于控制实例的创建过程。__init__用于初始化对象,在创建对象时被自动调用。
装饰器 🔗
装饰器:添加一些扩展行为,来实现装饰的目的;装饰器函数在加载模块时执行,被装饰的函数在明确调用时执行。
装饰器的功能上与 java spring 里的 aop, rust 里的过程宏达到相同的目的。
# 装饰器函数
def simple_decorator(func):
def wrapper():
print("Before function call")
func()
print("After function call")
return wrapper
# 使用装饰器
@simple_decorator
def say_hello():
print("Hello, world!")
# 等价于 say_hello = simple_decorator(say_hello)
say_hello()
可迭代对象,迭代器,生成器 🔗
三者的关系:可迭代对象 > 迭代器 > 生成器。
可迭代对象 iterable: 是一个容器,实现__iter__方法返回一个 iterator 迭代器。
迭代器使用 next 函数, iterator: 有状态。 实现__next__() 和__iter__()方法的对象。实现了__next__ 方法的对象都可以称为迭代器。
生成器 generator, 特殊的迭代器。 生成器函数(带 yield)和生成器表达式 (与列表推导式类似, 使用小括号包裹)。
lst = [1,3, 5]
for i in lst:
print(i)
lst = [1,3, 5]
it = iter(lst) # 调用可迭代对象的iter方法返回迭代器。
for i in it:
print(i)
函数的传参方式 🔗
在 Python 中,函数参数传递的机制被称为“传对象引用”(pass-by-object-reference)。
变量的作用域 🔗
Python 中的作用域遵循 LEGB(Local → Enclosing → Global → Built-in)规则,即:
- Local(局部作用域):函数内部定义的变量,函数外部无法访问。
- Enclosing(封闭作用域):嵌套函数的外部函数中的变量, 常出现在闭包函数中访问外部函数中的变量。
- Global(全局作用域):模块级别定义的变量,函数外部可以访问。
- Built-in(内置作用域):Python 内置的函数和变量,比如 print()、int() 等
Python 的一切皆对象 🔗
函数也是对象,包含了需要执行的代码和其他属性(例如支持闭包获取外部变量的 closure 属性)。
def greet():
"""This function greets."""
return "Hello, world!"
print(greet.__name__) # 输出 "greet"
print(greet.__doc__) # 输出 "This function greets."
print(greet.__code__) # 存储代码等信息
python 的闭包函数 🔗
闭包函数: 保存了对外部函数的自由变量的引用。这些自由变量的引用会保存在 闭包的环境 中(通常保存在 __closure__ 属性中)
当你创建一个闭包时,Python 会:
创建一个新的函数对象。
创建一个 cell 对象来持有外部函数的变量的引用。
将这些 cell 对象赋值给该函数的 closure 属性。
def outer(x):
def inner(y):
return x + y # inner 函数引用了 outer 的变量 x
return inner # 返回 inner 函数
closure = outer(10) # 创建闭包
print(closure(5)) # 输出 15
print(closure.__closure__) # 显示闭包的环境
print(closure.__closure__[0].cell_contents) # 输出 10
描述器 🔗
通过描述器控制类属性的设置和访问。当访问对象的属性时,遵循以下优先级: 属性是描述器,调用描述器的__get__()方法返回属性;没有描述器时,访问实例的字典。
描述器: class 中定义了__get__ __set__ __delete__方法。
dis.dis("a.b")编程成LOAD_ATTR字节码。
模块与包 🔗
文件夹就是一个包,要有一个名为__init__.py的文件。每个 Python 文件是一个模块。import导入包,from x import y导入模块
导入的模块除了定义函数之外还有可以执行代码,那么 Python 解释器在导入这个模块时就会执行这些代码。
if __name__ == '__main__': , __name__是 Python 中一个隐含的变量它代表了模块的名字, 只有被 Python 解释器直接执行的模块的名字才是__main__。
访问可见性 🔗
在给属性命名时可以用两个下划线作为开头, 属性是私有的。
Python 并没有从语法上严格保证私有属性或方法的私密性,它只是给私有的属性和方法换了一个名字来妨碍对它们的访问,事实上如果你知道更换名字的规则仍然可以访问到它们。
16 class Test:
15 def __init__(self, foo):
14 self.__foo = foo
13
12 def __bar(self):
11 print(self.__foo)
10 print('__bar')
9
8
7 def main():
6 test = Test('hello')
5 test._Test__bar()
4 print(test._Test__foo)
3
2
1 if __name__ == "__main__":
17 main()
属性命名以单下划线开头,通过这种方式来暗示属性是受保护的,不建议外界直接访问, 但可以直接访问到。
python 程序退出方式 🔗
- quit(), exit(),
- sys.exit() 推荐使用。
- os._exit()
try:
quit()
exit()
sys.exit()
except SystemExit:
print("Ignore Exit")
print("yes")
# try:
# os._exit(0) # 直接退出。
# except:
# pass
# print("no")
GIL (global interpreter lock) 🔗
全局线程锁简单,可靠。
缺点:python 的计算密集型多线程没法利用 cpu 的多核。
起多进程的方式利用 cpu 的多核 or 使用没有 GIL 的 python 解释器(Jython, IronPython, PyPy 是有 GIL 的)。
编译与反编译 🔗
编译为字节码 python3 -m example.pyc example.py。反编译模块dis显示可读的字节码。具体字节码是如何执行的,需要到 cpython 的 ceval.c文件的 _PyEval_EvalFrameDefault()。
code object 🔗
一种内存中表示 Python 字节码的对象, 在 python 的执行机制中最重要。
def f(a, b=3, *args, **kwargs):
pass
# 函数被编译为code object
code = f.__code__
print(code.co_code)
# number of arguments (排除掉*, ** args)
print(code.co_argcount)
# number of positional only arguments
print(code.co_posonlyargcount)
# number of keyword only arguments
print(code.co_kwonlyargcount)
frame 🔗
import inspect
frame = inspect.currentframe()
静态分析工具 🔗
静态代码分析主要用到的是 Pylint 和 Flake8
Flake8 封装了 Pyflakes(检查代码逻辑错误)、McCabe(检查代码复杂性)和 Pycodestyle(检查代码是否符合 PEP-8 规范)工具
python 数据结构与算法 🔗
测试库: unittest 库。test_开头的文件。
collections 集合 🔗
双端队列,保存最后 N 个元素: collections.deque
堆, 找到最大,最小的 N 个元素: heapq模块, 优先队列。
字典中一键多值: collections.defaultdict
字典中保存有序: collections.OrderedDict
模型文件: Yolo V5, pt 文件, onnx, netron 格式。
打包功能 🔗
setuptools
setup.py
python setup.py build, python setup.py install 安装软件。 例如 opencve
MANIFEST.in的文件,来控制文件的分发.
pip install /path/to/directory pip 将会查找目录中的 setup.py 文件并根据其中的配置进行安装
实验场 🔗
https://github.com/makeyourownneuralnetwork Colab 服务: colab.research.google.com
参考 🔗
- 《python cookbook》
- 《fluent python》
- gaotian 的 python 视频