Python是一种解释型语言,不需要再运行之前进行编译。 Python是一种动态类型语言,不需要声明变量的类型。 python适合面向对象编程,允许类的定义以及组合和继承。
优点
简单易懂,灵活简洁强大的标准库和三方库活跃的社区,许多开源项目开发效率高,迭代便捷应用领域广泛,Web开发、网络编程、自动化运维、Linux系统管理、数据分析、科学计算、人工智能、机器学习缺点
执行效率较差,异步生态不完善,相关的库较少(tornado)GIL的存在,无法充分利用多核的特性官方文档
第一步:使用自带的2to3.py文件,可以实现大部分代码从py2到py3的自动转换。命令:2to3 -w example.py
第二步:使用Pylint或者Pyflakes工具,检测代码错误。
第三步:检查模块和依赖库的变化。
第四步:人工修复被破坏的py2代码。
利用好__future__模块
from __future__ import division # 在Python 2 中表现 Python 3.x 中的整除 from __future__ import unicode_literals # 适应Python 3.x的新的字符串的表示方法 from __future__ import print_function # 使用打印功能 from __future__ import nested_scopes # 静态嵌套范围 from __future__ import generators # 简单生成器 from __future__ import absolute_import # 绝对/相对导入 from __future__ import with_statement # with声明新式类是在创建的时候继承内置的object对象或者是内置类型如list、dict,而旧式类(经典类)是直接声明的,可以用dir()方法查看新式类中内置了很多性的属性和方法。
新式类遍历方法是广度优先,经典类是深度优先。
在一些语言中也叫做反射,简单来说就是对象检查。面向对象的语言所写的程序在运行时,所能知道对象的类型。是什么(isinstance),是什么类型(type),有那些属性(hasattr),有哪些变量方法(dir),有哪些行为(hasattr),getattr、setattr、delattr、callable。
是一种让程序行为在运行时扩展或者变更的方法。
python充分利用动态语言的特性,在程序运行时动态改变类、模块、属性或方法,为的是将第三方代码打补丁在不按预期运行的bug或者feature上,gevent在这方面运用的比较多。
迭代器是一个更抽象的概念,任何对象,如果它的类有next方法和iter方法返回自己本身。对于string、list、dict、tuple等这类容器对象,使用for循环遍历是很方便的。在后台for语句对容器对象调用iter()函数,iter()是python的内置函数。iter()会返回一个定义了next()方法的迭代器对象,它在容器中逐个访问容器内元素,next()也是python的内置函数。在没有后续元素时,next()会抛出一个StopIteration异常。
生成器(Generator)是创建迭代器的简单而强大的工具。它们写起来就像是正规的函数,只是在需要返回数据的时候使用yield语句,生成器使用yield语句返回一个值,yield语句挂起该生成器函数的状态,保留足够的信息,以便之后从它离开的地方继续执行。每次next()被调用时,生成器会返回它脱离的位置(它记忆语句最后一次执行的位置和所有的数据值)。生成器需要注意:只能遍历一次。
区别:生成器能做到迭代器能做的所有事,而且因为自动创建了__iter__()和next()方法,生成器显得特别简洁,而且生成器也是高效的,使用生成器表达式取代列表解析可以同时节省内存。除了创建和保存程序状态的自动方法,当发生器终结时,还会自动抛出StopIteration异常。
Python的底层
要理解GIL的含义,我们需要从Python的基础讲起。像C++这样的语言是编译型语言,所谓编译型语言,是指程序输入到编译器,编译器再根据语言的语法进行解析,然后翻译成语言独立的中间表示,最终链接成具有高度优化的机器码的可执行程序。编译器之所以可以深层次的对代码进行优化,是因为它可以看到整个程序(或者一大块独立的部分)。这使得它可以对不同的语言指令之间的交互进行推理,从而给出更有效的优化手段。
与此相反,Python是解释型语言。程序被输入到解释器来运行。解释器在程序执行之前对其并不了解;它所知道的只是Python的规则,以及在执行过程中怎样去动态的应用这些规则。它也有一些优化,但是这基本上只是另一个级别的优化。由于解释器没法很好的对程序进行推导,Python的大部分优化其实是解释器自身的优化。更快的解释器自然意味着程序的运行也能“免费”的更快。也就是说,解释器优化后,Python程序不用做修改就可以享受优化后的好处。
要想利用多核系统,Python必须支持多线程运行。作为解释型语言,Python的解释器必须做到既安全又高效,但多线程编程会遇到问题,解释器要留意的是避免在不同的线程操作内部共享的数据,同时它还要保证在管理用户线程时保证总是有最大化的计算资源。这时,为了不同线程同时访问时保护数据,当一个线程运行python程序的时候会霸占整个python解释器,也就是给解释器加了一把全局锁,这使得对于任何python程序,不管有多少处理器,多少线程,任何时候都自由一个线程在执行,其他线程在等待正在运行的线程完成才能运行。
如果线程运行过程中遇到耗时操作,超时时间超过一个固定值,则单线线程将会解开全局解释器锁,使其他线程运行。所以在多线程中是有先后顺序的,并不是同时运行的。多进程因每个进程都能被系统分配资源,相当于每个进程有了一个python解释器,所以多进程可以实现多个进程同时运行,缺点就是系统资源开销大。
在python中一切皆对象,num、list、dict、tuple、function、class和模块等都是对象,如公民一样地位平等,可以在运行时创建,并能被赋值给变量,作为集合对象的元素,还能够作为函数的参数和返回值。
Python 中函数是一等公民,意思是 Python 中的函数和整数、字符串等常见概念的地位是平等的,一个整数和一个字符串等对象可以干的事,一个函数也可以办到。
import re def factorial(n): """ returns n! """ return 1 if n < 2 else n * factorial(n - 1) class Domo: pass items = [1, 1.0, 'hello', [1], {'a': 1}, {1}, factorial, Domo(), re, None, object] for item in items: print(f'对象 {item} 的类型是 {type(item).__name__},', f'对象 {type(item).__name__}类 的类型是 {type(type(item)).__name__}.') # 结果 ''' 对象 1 的类型是 int, 对象 int类 的类型是 type. 对象 1.0 的类型是 float, 对象 float类 的类型是 type. 对象 hello 的类型是 str, 对象 str类 的类型是 type. 对象 [1] 的类型是 list, 对象 list类 的类型是 type. 对象 {'a': 1} 的类型是 dict, 对象 dict类 的类型是 type. 对象 {1} 的类型是 set, 对象 set类 的类型是 type. 对象 <function factorial at ...> 的类型是 function, 对象 function类 的类型是 type. 对象 <__main__.Dumb object at ...> 的类型是 Dumb, 对象 Dumb类 的类型是 type. 对象 <module 're' from 'C:\\...\\re.py'> 的类型是 module, 对象 module类 的类型是 type. 对象 None 的类型是 NoneType, 对象 NoneType类 的类型是 type. 对象 <class 'object'> 的类型是 type, 对象 type类 的类型是 type ''' for item in items: print(f'类对象 {item.__class__.__name__} 的基类是: {item.__class__.__bases__}') print(f'类对象 {object.__name__} 的基类是: {object.__bases__}') # 结果 ''' 类对象 int 的基类是: (<class 'object'>,) 类对象 float 的基类是: (<class 'object'>,) 类对象 str 的基类是: (<class 'object'>,) 类对象 list 的基类是: (<class 'object'>,) 类对象 dict 的基类是: (<class 'object'>,) 类对象 set 的基类是: (<class 'object'>,) 类对象 function 的基类是: (<class 'object'>,) 类对象 Dumb 的基类是: (<class 'object'>,) 类对象 module 的基类是: (<class 'object'>,) 类对象 NoneType 的基类是: (<class 'object'>,) 类对象 type 的基类是: (<class 'object'>,) 类对象 object 的基类是: () '''函数的本质是FunctionObject,其中包括内置函数、匿名函数、递归函数、自定义函数。 函数的作用域是从函数调用开始至函数执行完成,返回给调用者后,在执行过程中开辟的空间会自动释放,即函数执行完成后,函数体内部通过赋值等方式修改变量的值不会保留,会随着返回给调用者后,开辟的空间自动释放。 函数通过"函数名()"形式调用。
方法的本质是PyMethodObject,其中包括普通方法(直接用self调用的方法)、私有方法(__函数名,只能在类中被调用的方法)、属性方法(@property,将方法伪装成属性)、特殊方法(__init__, __call__,__getattr__等)、类方法、静态方法。 方法的作用域是通过实例化的对象进行方法的调用,调用后开辟的空间不会释放,即调用方法中对变量的修改值会一直保留。 方法是通过"对象.方法名"的方式进行调用。
面向对象编程OOP(Object Oriented Programming)是一种程序设计思想,把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。面向对象的程序设计过程吧计算机程序视为一组对象的集合,而每个对象都可以接受其他对象发过来的消息,并处理这些消息,计算机程序的执行就是一系列消息在对象之间传递。
在Python中,一切皆对象,当然也可以自定义对象。自定义对象的数据类型就是面向对象中的类的概念,面向对象的设计思想是抽象出类(Class),根据类(Class)创建实例(instance)。面向对象的抽象程度又比函数要高,因为一个类(Class)既包含数据又包含操作数据的方法。
面向对象的三大特性:封装、继承、多态。
AOP(Aspect Oriented Programming)是一种编程方式,就是在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。
AOP主要作用就是将类似于日志记录,性能统计,安全控制,事务处理,异常处理等重复性的代码块从业务逻辑代码中划分出来,对这些行为的分离。并且将它们独立到非知道业务逻辑的方法中,从而做到改变这些行为的时候不影响业务逻辑代码。
AOP带来的优点:
降低系统的反复代码降低模块间的耦合度提高系统可扩展性增加代码可维护性装饰器是一个很著名的设计模式,经常被用于有切面需求的场景,装饰器的作用就是为已经存在的对象添加额外的功能,所以python中使用装饰器实现AOP。
函数是Python内建支持的一种封装,通过把打断代码拆层函数,通过一层层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
函数式编程(Hunctingal Programming)虽然可以归结到面向过程的程序设计,但其思想更接近数学计算。函数式编程就是一种抽象程度很高的编程范式,纯粹的函数式编程语言编写的函数没有变量,因此,任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用。而允许使用变量的程序设计语言,由于函数内部的变量状态不确定,同样的输入,可能得到不同的输出,这种函数是有副作用的。
函数式编程的一个特点就是,允许把函数本身作为参数传入另一个函数,还允许返回一个函数。Python对函数是编程仅提供部分支持,由于Python允许提供变量,因此python不是纯函数式编程语言。
高阶函数出了可以接受函数作为参数外,还可以把函数作为结果值返回。
# 嵌套函数,内层函数返回值是函数对象 def lazy_sum(*args): def sum(): x = 0 for i in args: x += i return x return sum res = lazy_sum(1, 3, 5, 7, 9) # 这里调用的是求和函数,而非求和结果 print(res) print(res()) # 结果 '''<function lazy_sum.<locals>.sum at 0x000001A4B6E55488> 25 '''实际上是一种闭包,详细参见19. 闭包。返回闭包时要牢记:返回函数不要引用任何循环变量,或者后续会发生变化的量。
先观摩下大佬博文**Python 之 lambda 函数完整详解 & 巧妙运用**
匿名函数在python编程语言中使用频率非常高,使用起来非常灵活、巧妙。
python中使用关键字lambda来创建匿名函数,没有函数名字;lambda只是一个表达式,函数体比def简单很多;lambda的主体是一个表达式,而不是一个代码块,所以在表达式中只能封装有限的简单的逻辑表达式,复杂的需要函数来实现;lambda函数拥有自己的命名空间,且不能范围自有列表之外或者全局命名空间里的参数;**注意:**虽然lambda函数看起来只能写一行,却不等同于C或C++的内联函数,内联函数的目的是调用小函数时不占用栈内存从而增加运行效率。
lambda函数语法:lambda [arg1 [,arg2,...,argn]]: expression
lambda是python关键字,[arg…]和expression由用户自定义[arg…]是参数列表,形参可以为多个,逗号隔开expression是个参数表达式,且表达式只能是单行的,只能有一个表达式,且逻辑结束后直接返回数据返回值在冒号之后设置,返回值和正常的函数一样,可以是任意数据类型。(但是想要返回多个元素要以容器的形式返回) # 将lambda函数赋值给一个变量,通过这个变量间接调用该lambda函数 add = lambda x, y: x + y res = add(1, 2) print(res) # 结果 ''' 3 ''' # 将lambda函数赋值给其他函数,从而将其他函数用该lambda函数替换 import time time.sleep = lambda x: None t = time.sleep(3) print(t) # 结果 ''' None ''' # 列表推导式 s = [lambda : i for i in range(5)] print(s) # 结果 ''' [0, 1, 2, 3, 4] ''' # 嵌套的lambda表达式 f = (lambda x: (lambda y: x + y)) res = f(10) print(res(5)) # 结果 ''' 15 '''函数在执行时,要带上所有必要的参数进行调用,有时参数可以在函数被调用之前提前给定,这种情况下,一个函数有一个或多个参数预先就能用上,以便函数能用更少的参数进行调用。
偏函数就是将所要承载的函数作为partial()函数的第一个参数,原函数的个参数一次作为partial()函数后续的参数,除非使用关键字参数。
from functools import partial # 对整数100,取得对于不同数m的 100 % m 的余数 def mod(n, m): return n % m mod_by_100 = partial(mod, 100) print(mod(100, 7)) print(mod_by_100(7)) # 结果 ''' 2 2 ''' # 最大值中每次都有10 max_new = partial(max, 10) # 把10作为max的第一个参数,加到最左边 print(max_new(1, 5, 9)) # 等同于 args = (10, 1, 5, 9) print(max(*args)) # 结果 ''' 10 10 '''ASCII编码,将符号、数字、英文等存储到计算机
GB2312编码和GBK编码,将中文存储到计算机
Unicode字符集,将全世界文字存储到计算机
*args用来将参数打包成tuple给函数体调用,传递可变位置参数
**kwargs打包关键字参数成dict给函数体调用,传递可变关键字参数
实参:调用函数时的参数,赋予的实际数值或者变量,实参和形参都是引用同一个对象
形参:定义函数时的参数,是一个新的、本地的变量名,在函数的本地作用域内
可变对象:list、dict、set、bytearray、array、collections.deque
不可变对象:tuple、str、bytes、bool、int、float
注意:元组是相对不可变的对象, 元组存储的是对象的引用, 当元组中的存在可变对象的引用时, 引用不可变, 但是引用的对象时可变的。
Numpy是数值计算的扩展包,能够高效处理N为数组、复杂函数、线性代数。Numpy专门针对ndarray的操作和运算进行了设计,所以数据的存储效率和输入输出性能远优于Python中的嵌套列表,当数组越大时,Numpy的优势越明显,因此用来存储和处理大型矩阵比嵌套列表结构高效的多,可以说是一种免费而强大的Matlab系统。
Numpy库里有哪些函数
Scipy是专门用于科学计算的一个常用库,可以插值运算、优化算法、图像处理、常微分方程数值解的求解、信号处理、稀疏矩阵和数学统计等等。Scipy是基于Numpy,并且封装了一些高阶抽象和物理模型,是Numpy和Scipy协同工作,高效解决问题。
Python机器学习及分析工具:Scipy篇
Pandas是基于Numpy的一种工具,提供了一套名为DataFrame的数据结构,适合统计分析中的表结构,在上层做数据分析,所以也称为表格容器。Pandas 所包含的数据结构和数据处理工具的设计,使得数据清洗和分析非常快捷,并经常和其他Numpy、Scipy数值计算工具、数据可视化工具Matplotlib结合起来使用,其中大量库和一些标准的数据模型、函数和方法,支持着大型数据集的高效处理。
Pandas库中的函数
**顺序访问:**链表在内存中不是按顺序存储的,而是通过指针连在一起,为了访问某一元素,必须从链头还是顺着指针才能找到某一个元素。
**随机访问:**数组在内存中是按顺序存储的,访问数组中的任意元素可以直接通过下标计算得到元素存放的位置,访问耗时与元素在数组中所处的位置无关。
random.random()用于生成一个0到1的随机浮点数: 0 <= n < 1.0 。
random.uniform(a, b),用于生成一个指定范围内的随机浮点数,两个参数其中一个是上限,一个是下限。如果a > b,则生成的随机数n: a <= n <= b。如果 a < b, 则 b <= n <= a 。
random.randint(a, b),用于生成一个指定范围内的整数。其中参数a是下限,参数b是上限,生成的随机数n: a <= n <= b。
random.randrange([start], stop[, step]),从指定范围内,按指定基数递增的集合中获取一个随机数。如random.randrange(10, 10, 2),结果相当于从[10, 12, 14, 16, ..., 96, 98]序列中获取一个随机数。
random.choice(sequence)从序列中获取一个随机元素,参数sequence表示一个有序类型,list、tuple、字符串等。如random.choice(["Python", "C++", "java"]),随机在列表中选取一个元素。
random.shuffle(x[, random]),用于将一个列表中的元素打乱。如random.shuffle(["Python", "C++", "java"]),每次结果是随机排序的。
random.sample(sequence, k),从指定序列中随机获取指定长度的片断,sample函数不会修改原有序列。如random.sample([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], 2),结果是从列表10个数字中随机选两个元素。
一种约定,一般来说,_object被看作“私有的”,在模块或类外不可以使用,不能用from module import *导入,当变量是私有的时候,用_object来表示变量是很好的习惯。
双下划线开头双下划线开头,__object表示私有成员,只有类对象自己能访问,连子类对象也不能访问到这个数据。
python中的私有不是真正意义上的private,可以通过对象._类名__object机制访问private.
双下划线开头和结尾"object"指的是python中的一些特殊方法,前后双下划线是专用标识,它们是python的魔法函数,编程是不要用这种方式命名自己的变量和函数。
方法描述__str__将对象转换成字符串时会执行__init__初始化方法,为对象变量赋值__new__构造方法,创建一个对象__call__在对象后面加括号会执行该方法__getattr__当使用对象.属性时,若属性存在会调用该方法__setattr__当使用对象.属性 = value时,会调用该方法__iter__类内部定义了该方法,对象就变成了可迭代对象__add__当两个对象使用+号时,会调用该方法__enter__ 和 __exit__上下文管理Python作用域简单说就是一个变量的命名空间。代码中变量被赋值的位置,就决定了哪些范围的对象可以访问这个变量,这个范围就是变量的作用域。 在Python中,只有模块(module),类(class)以及函数(def、lambda)才会引入新的作用域。 Python的变量名解析机制也称为 LEGB 法则:本地作用域(Local)→当前作用域被嵌入的本地作用域(Enclosing locals)→全局/模块作用域(Global)→内置作用域(Built-in)
比特 / bit:计算机中最小的数据单位,是单个的二进制数值 0 或 1
字节 / byte:计算机存储数据的单元,1 个字节由 8 个比特组成
字符:人类能够识别的符号
编码:将人类可识别的字符转换为机器可识别的字节码 / 字节序列
解码:将机器可识别的字节码 / 字节序列转换为人类可识别的字符
编码格式Python2 的默认编码是ASCII,不能识别中文字符,需要显示指定字符编码。
Python3 的默认编码是Unicode,可以识别中文。
计算机内存的数据,统一使用Unicode编码;数据传输或保存在硬盘上,是哟还能UTF-8编码。
编码和解码
编码(encode):将Unicode字符串转换为特定编码格式对应的字节码的过程。 s = "学习python" print(s.encode()) # encode()函数里参数默认为utf-8 print(s.encode('gbk')) # 结果 ''' b'\xe5\xad\xa6\xe4\xb9\xa0python' b'\xd1\xa7\xcf\xb0python' ''' 解码(decode):将特定编码格式的字节码转换为对应的Unicode字符串的过程。 # encode和decode的编码格式必须一致,如gbk编码,必须用gbk解码 s1 = b'\xe5\xad\xa6\xe4\xb9\xa0python' s2 = b'\xd1\xa7\xcf\xb0python' print(s1.decode()) # 用默认的utf-8解码 print(s2.decode('gbk')) # 用gbk解码 # 结果 ''' 学习python 学习python '''编码表和适用性
编码适用性大小特点ASCII码数字、英文字母、特殊字符8bit = 1byte特点编号从0到127,占用空间小GB2312码/GBK码支持中文16bit = 2byteGBK码是GB2312码的升级,也支持日文、韩文等Unicode码支持国际语言32bit = 4byte占用空间大,适用性强,在ASCII码前面补8个就成了Unicode码UTF-8支持国际语言英文:8bit = 1byte 中文:24bit = 3byte 欧洲文字:16bit = 2byte是Unicode的升级,是可变长度的Unicode,两者可以非常容易地互相转换,占用空间小+=操作首先会尝试调用对象的__iadd__方法,如果没有该方法,那么尝试调用__add__方法,所以 += 与 +的区别实质是__iadd__和__add__的区别。
__add__方法接收两个参数,返回它们的和,两个参数的值均不会改变。__iadd__方法同样接收两个参数,但它是属于in-place操作,即会改变第一个参数的值,所以__iadd__方法的对象只能是可变对象,如list对象提供了__iadd__方法,而int对象是没有。*=操作首先会尝试调用对象的__imul__方法,如果没有该方法,那么尝试调用__mul__方法,所以 *= 与 *的区别实质是__imul__和__mul__的区别。
对不可变序列进行重复拼接操作,效率会很低,因为每次都要新建一个序列,然后把原序列中的元素复制到新的序列里面,然后再追加新的元素。
不要把可变对象放在元组里。
增量赋值不是一个原子操作,出现异常时,仍然进行了操作。
exec函数主要用于动态地执行代码字符串,exec 返回值永远为 None。exec()不仅可以执行python代码,还可以共享上下文,但需要注意尽量不要在全局作用域下用exec()函数执行python代码,以免发生命名空间冲突。
为了解决这个问题,可以在执行之前创建一个空字典scope = {},作为exec()函数的第二个参数,这样通过exec执行python代码时创建的变量就会存储在字典scope中。
如果exec()函数执行的python代码中含有参数,且要传参,则可以在exec()中 以字典形式进行传参,这时exec就有了第三个参数args。当说要执行的代码是多行时,可以用""" “”"引起来.
args = {'x': 1, 'y': 2} scope = {} print(exec('print(x + y)', scope, args)) print(scope.keys()) # 结果 ''' 3 dict_keys(['__builtins__']) ''' evaleval()函数来执行一个字符串表达式,实现一个可以计算表达式的计算器,并返回表达式的值。
eval()函数的语法:eval(expression[, globals[, locals]]),expression是表达式;globals是变量作用域,全局命名空间,如果被提供,这必须是一个字典对象;locals是变量作用域,局部变量命名空间,如果被提供,可以是任何映射对象。
python中查找变量的顺序是:局部 --> 全局 --> 内置
a = 1 # 当前变量 b = 2 # 当前变量 c = 3 # 当前变量 globals = {'a': 10, 'b': 20} # 全局变量 locals = {'b': 200, 'c': 300} # 局部变量 print(eval('a + b + c')) # 后两个参数省略,默认在当前作用域下,即a+b+c = 1+2+3 = 6 print(eval('a + b + c', globals, locals)) # 指定globals、locals参数,优先局部,再全局,最后当前作用域,即a+b+c = 10+200+300 = 510 # 结果 ''' 6 510 '''具体一些例子和详细原理可以参考《流畅的Python》 第16章 协程
从句法上看,协程与生成器类似,都是定义体中包含 yield 关键字的函数。可是,在协 程中,yield 通常出现在表达式的右边(例如,datum = yield),可以产出值,也可 以不产出——如果 yield 关键字后面没有表达式,那么生成器产出 None。协程可能会从 调用方接收数据,不过调用方把数据提供给协程使用的是 .send(datum) 方法,而不是 next(…) 函数。通常,调用方会把值推送给协程,通过.send()双向交换数据的生成器就是协程。
yield 关键字甚至还可以不接收或传出数据。不管数据如何流动,yield 都是一种流程控 制工具,使用它可以实现协作式多任务:协程可以把控制器让步给中心调度程序,从而激活其他的协程。
# 产生两个值的协程 >>> from inspect import getgeneratorstate >>> def simple_coro2(a): ... print('-> Started: a =', a) ... b = yield a ... print('-> Received: b =', b) ... c = yield a + b ... print('-> Received: c =', c) >>> my_coro2 = simple_coro2(14) >>> getgeneratorstate(my_coro2) # (1) 'GEN_CREATED' >>> next(my_coro2) # (2) -> Started: a = 14 14 >>> getgeneratorstate(my_coro2) # (3) 'GEN_SUSPENDED' >>> my_coro2.send(28) # (4) -> Received: b = 28 42 >>> my_coro2.send(99) # (5) -> Received: c = 99 Traceback (most recent call last): File "<stdin>", line 1, in <module> StopIteration >>> getgeneratorstate(my_coro2) # (6) 'GEN_CLOSED'(1) inspect.getgeneratorstate函数指明,处于GEN_CREATED 状态(即协程未启动)。
(2) 向前执行协程到以第一个yield表达式,打印 -> Started: a = 14 消息,然后产出a的值,并且暂停,等待为b赋值。
(3) getgeneratorstate 函数指明,处于 GEN_SUSPENDED 状态(即协程在yield表达式处暂停)。
(4) 把数字28发给暂停的协程;就算yield表达式,得到28,然后把那个数按定给b。打印 -> Received: b = 28 消息,产出 a + b 的值(42),然后协程暂停,等待为 c 赋值。
(5) 把数字 99 发给暂停的协程;计算 yield 表达式,得到 99,然后把那个数绑定给 c。打印 -> Received: c = 99 消息,然后协程终止,导致生成器对象抛出StopIteration异常。
(6) getgeneratorstate 函数指明,处于 GEN_CLOSED 状态(即协程执行结束)。
Python 提供了一个办法,把这些定义的方法和变量存放在文件中,为一些脚本或者交互式的解释器实例使用,这个文件被称为模块。模块是一个包含所有你定义的函数和变量的文件,其后缀名是.py,可以被别的程序引入,以使用该模块中的函数等功能。
代码块就是可作为可执行单元的一段Python程序文本;模块、函数体和类定义都是代码块。不仅如此,每一个交互脚本命令也是一个代码块;一个脚本文件也是一个代码块;一个命令行脚本也是一个代码块。
使用上下文管理器最广泛的案例就是with语句,打开文件,对文件进行操作,然后关闭文件,如果在往文件里写数据的时候发生异常,它也会尝试去关闭文件。
# with语句,确保文件会被关闭 with open('aaa.txt', 'wb') as f f.write("sddsa") # 等价于 f = open('aaa.txt', 'wb') try: f.write('sddsa') finally: f.close()利用__enter__和__exit__方法实现一个上下文管理类
class File(object): def __init__(self, file_name, method): self.file_obj = open(file_name, method) def __enter__(self): return self.file_obj def __exit__(self, exc_type, exc_val, exc_tb): self.file_obj.close() with File('aaa.txt', 'wb') as f: f.write("Hello") # 执行顺序 ''' (1) with语句先暂存了File类的__exit__方法 (2) 然后吊桶File类的__enter__方法 (3) __enter__方法返回打开文件对象 (4) 打开文件的对象被传给f (5) 使用write来写文件 (6) 调用之前暂存的__exit__ (7) __exit__关闭了文件 '''python有个contextlib模块,里面的contextmanager结合生成器可以实现上下文管理
from contextlib import contextmanager @contextmanager def open_file(name): f = open(name, 'w') yield f f.close() with open_file('aaa.txt') as ff: ff.write("sssss")持续跟新,更多Python基础知识,详见个人博客归纳。
同时也欢迎留言补充,批评指正!
