tomoya06 / web-developer-guidance

Actually it's just a notebook for keeping down some working experience.
4 stars 0 forks source link

Python #7

Open tomoya06 opened 4 years ago

tomoya06 commented 4 years ago

基本语法

数据类型

Number: int、float、bool、complex

面向对象

完整语法参考廖雪峰的Python教程

__new__ vs __init__

可迭代vs迭代器vs生成器

[x * x for x in range(10)]  // 列表生成式
(x * x for x in range(10))  // 生成器

函数式编程

设计模式

两种实现参考这里。一种是改写__new__方法,每个类有一个_instance属性存单例;一种是用装饰器,所有类及其单例以key-value存在一个dict中。

命名空间和作用域

命名空间

名称到对象的映射。分三种,从外到内依次为:

  1. 内置名称:python 语言内置的名称,比如函数名 abs、char 和异常名称 BaseException、Exception 等等。
  2. 全局名称:模块中定义的名称,记录了模块的变量,包括函数、类、其它导入的模块、模块级的变量和常量。
  3. 局部名称:函数/类中定义的名称,记录了函数的变量,包括函数的参数和局部定义的变量。

查找顺序为:局部-全局-内置

作用域

一个 Python 程序可以直接访问命名空间的正文区域。分四种:

  1. L(Local):最内层,包含局部变量,比如一个函数/方法内部。
  2. E(Enclosing):包含了非局部(non-local)也非全局(non-global)的变量。比如两个嵌套函数,一个函数(或类) A 里面又包含了一个函数 B ,那么对于 B 中的名称来说 A 中的作用域就为 nonlocal。
  3. G(Global):当前脚本的最外层,比如当前模块的全局变量。
  4. B(Built-in): 包含了内建的变量/关键字等,最后被搜索

image

命名空间和作用域的例子参考菜鸟博客

tomoya06 commented 3 years ago

GIL锁

重点:CPU密集型多线程任务受到限制,多进程、协程、或IO密集型多线程不受影响

全局解释器锁(Global Interpreter Lock aka. GIL),计算机程序设计语言解释器用于同步线程的一种机制,它使得任何时刻仅有一个线程在执行。即便在多核心处理器上,使用 GIL 的解释器也只允许同一时间执行一个线程。

解决方法:

  1. 使用多进程:Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响;
  2. 使用协程:注意协程也只是单CPU,但是能减小切换代价提升性能。

对GIL的详细介绍参考这篇博客

tomoya06 commented 3 years ago

多线程、多进程和协程

多进程

创建方法

通信

具体实例参考廖雪峰的多进程教学

多线程

通信

具体实例参考廖雪峰的多线程教学

协程

协程:aka. 微线程/纤程/Coroutine。协程的作用,是在执行函数A时,可以随时中断,去执行函数B,然后中断继续执行函数A(可以自由切换)。但这一过程并不是函数调用(没有调用语句),这一整个过程看似像多线程,然而协程只有一个线程执行。

Python对协程的支持是通过generator实现的。

参考廖雪峰的python-异步IO

tomoya06 commented 3 years ago

垃圾回收机制

回收机制

python采用的是引用计数机制为主,标记-清除和分代收集两种机制为辅的策略。

1. 引用计数

python里每一个东西都是对象,它们的核心就是一个结构体:PyObject

typedef struct_object {
  int ob_refcnt;
  struct_typeobject *ob_type;
} PyObject;

PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少。当引用计数为0时,该对象生命就结束了。

优点:简单,实时性 缺点:循环引用问题

2. 标记-清除

标记清除就是用来解决循环引用的问题的。注意只有容器对象才会出现引用循环。

为了追踪容器对象,需要给每个容器对象维护两个额外的指针, 用来将容器对象组成一个链表,指针分别指向前后两个容器对象,方便插入和删除操作。

python解释器(Cpython)维护了两个这样的双端链表,一个链表存放着需要被扫描的容器对象,另一个链表存放着临时不可达对象。在图中,这两个链表分别被命名为”Object to Scan”和”Unreachable”。每一个节点除了有一个记录当前引用计数的变量ref_count还有一个gc_ref变量,这个gc_ref是ref_count的一个副本,所以初始值为ref_count的大小。

image

gc启动的时候,会逐个遍历”Object to Scan”链表中的容器对象,并且将当前对象所引用的所有对象的gc_ref减一。这一步操作就相当于解除了循环引用对引用计数的影响。

接着,gc会再次扫描所有的容器对象:

  1. 如果对象的gc_ref值为0,那么这个对象就被标记为GC_TENTATIVELY_UNREACHABLE,并且被移至”Unreachable”链表中;
  2. 如果对象的gc_ref不为0,那么这个对象就会被标记为GC_REACHABLE,同时还会递归式的将从该节点出发可以到达的所有节点标记为GC_REACHABLE,如果该节点当前在”Unreachable”链表中的话,还需要将其移回到”Object to Scan”链表中

第二次遍历的所有对象都遍历完成之后,存在于”Unreachable”链表中的对象就是真正需要被释放的对象。

image

分代技术

整体思想:将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量。

python分了三代(generation 0,1,2):0代表幼年对象,1代表青年对象,2代表老年对象。新对象分配在Gen0。每次GC后存活的对象就会晋升到Gen+1。

gc的扫描在什么时候会被触发呢?答案是当某一世代中被分配的对象与被释放的对象之差达到某一阈值的时候,就会触发gc对某一世代的扫描。值得注意的是当某一世代的扫描被触发的时候,比该世代年轻的世代也会被扫描。

tomoya06 commented 3 years ago

Python 3 vs 2

主要有以下几点:

更多差异参考博客