函数
函数是Python内建支持的一种封装,我们通过把大段代码拆成函数,通过一层一层的函数调用,就可以把复杂任务分解成简单的任务,这种分解可以称之为面向过程的程序设计。函数就是面向过程的程序设计的基本单元。
函数式编程(Functional Programming)
函数式编程是一种抽象程度很高的编程范式。纯粹的函数式编程语言编写的函数没有变量的,因此任意一个函数,只要输入是确定的,输出就是确定的,这种纯函数我们称之为没有副作用的。而非纯粹的函数(允许使用变量的),由于函数内部的变量状态不确定,同样的输入,有可能得到不同的输出,因此,这种函数是有副作用的。
函数式编程的特点:允许把函数本身作为参数传入另一个函数,还允许返回一个函数。
Python对函数式编程提供部分的支持,由于Python允许使用变量,所以Python不是纯函数式编程语言。
匿名函数就是不需要显示的定义函数,即不需要函数名。在 Python 中可以通过 lambda
关键字来定义匿名函数。
因为函数没有名字,不必担心函数名冲突。与 map()
、reduce()
、filter()
结合使用可以使代码更加 pythoner。此外,匿名函数也是一个函数对象,也可以把匿名函数赋值给一个变量,再利用变量来调用该函数。也可以把匿名函数作为返回值返回。
匿名函数也存在限制,就是只能有一个表达式,不用写 return
,返回值就是该表达式的结果。其代码的可读性也比较差差,有时会难于理解。
Python 捕捉异常可以使用 try/except
语句。
try/except
语句用来检测 try
语句块中的错误,从而让 except
语句捕获异常信息并处理。
如果你不想在异常发生时结束你的程序,只需在 try
里捕获它。
try:
<语句> #运行别的代码
except <名字>:
<语句> #如果在try部份引发了'name'异常
except <名字>,<数据>:
<语句> #如果引发了'name'异常,获得附加的数据
else:
<语句> #如果没有异常发生
finally:
<语句> #退出 try 会执行
else
和 finally
不是必须的。
默认处理机制
如果我们没有对异常进行任何预防,那么在程序执行的过程中发生异常,就会中断程序,调用 Python 默认的异常处理器,并在终端输出异常信息。
try…except
发现 try
语句,进入 try
语句块执行,发生异常,回到 try
语句层,寻找后面是否有 except
语句。找到 except
语句后,会调用这个自定义的异常处理器。except
将异常处理完毕后,程序继续往下执行。
try…finally
finally
语句表示,无论异常发生与否,finally
中的语句都要执行。但是,由于没有 except
处理器,finally
执行完毕后程序便中断。
assert
遇到 assert
后面紧跟的语句是 True 还是 False ,如果是 True 则继续执行,如果是 False 则中断程序,调用默认的异常处理器,同时输出 assert
语句逗号后面的提示信息。
with…as
处理流对象时,with…as
语句十分的非常方便。with
语句块完毕之后,会隐藏地自动关闭流。
如果 with
语句或语句块中发生异常,会调用默认的异常处理器处理,但流还是会正常关闭。
copy()
对于一个复杂对象的子对象并不会完全复制,什么是复杂对象的子对象呢?就比如序列里的嵌套序列,字典里的嵌套序列等都是复杂对象的子对象。对于子对象,python会把它当作一个公共镜像存储起来,所有对他的复制都被当成一个引用,所以说当其中一个引用将镜像改变了之后另一个引用使用镜像的时候镜像已经被改变了。
deepcopy()
的时候会将复杂对象的每一层复制一个单独的个体出来。
作用域又可以被称为命名空间,指变量起作用的范围。Python 变量作用域可以分为四种,分别为局部作用域(local)、嵌套作用域(enclosed)、全局作用域(global)、内置作用域(built-in)。
四种作用域中变量的调用顺序采取“就近原则”,即为 LEGB。
局部作用域指某个函数内部的范围。
嵌套一般是指一个函数嵌套另一个函数的情况,外层函数包含变量的作用范围称为嵌套作用域;也可以指一个类中包含多个函数时的情况。
全局作用域范围指的是在一个.py文件内部,在模块顶部声明即可成为全局作用域。
全局作用域中的变量在函数中一般是不可更改的,例如整数,字符等,但对于列表和字典来说可以更改。如想引用并改变全局变量,可使用global关键字。
内置作用域是python事先定义的内置模块,例如built-in 模块内的变量,程序启动之后由python虚拟机自动加载,在程序的任何地方都可以使用,例如print
函数,随着解释器存在或消亡。
python的新式类是2.2版本引进来的,我们可以将之前的类叫做经典类或者旧式类。新式类的引入是为了统一类(class)和类型(type)。
新式类都从 object
继承,经典类不需要。新式类的MRO(method resolution order 基类搜索顺序)算法采用C3算法广度优先搜索,而旧式类的MRO算法是采用深度优先搜索
新式类相同父类只执行一次构造函数,经典类重复执行多次。
python3.x中取消了经典类。
__new__
和 __init__
的区别__new__
是在实例创建之前被调用的,因为它的任务就是创建实例然后返回该实例对象,是个静态方法。
__init__
是当实例对象创建完成后被调用的,然后设置对象属性的一些初始值,通常用在初始化一个类实例的时候。是一个实例方法。
也就是: __new__
先被调用,__init__
后被调用,__new__
的返回值(实例)将传递给__init__
方法的第一个参数,然后 __init__
给这个实例设置一些参数。
引用计数器
在Python的C源码中有一个名为 refchain
的环状双向链表,Python程序中一旦创建对象都会把这个对象添加到 refchain
这个链表中。它保存着所有的对象。
在 refchain
中的所有对象内部都有一个 ob_refcnt
用来保存当前对象的引用计数器,当值被多次引用时候,不会在内存中重复创建数据,而是引用计数器 +1
。 当对象被销毁时候同时会让引用计数器 -1
,如果引用计数器为 0
,则将对象从 refchain
链表中摘除,同时在内存中进行销毁(暂不考虑缓存等特殊情况)。
标记清楚和分代回收
基于引用计数器进行垃圾回收非常方便和简单,但是存在循环引用的问题,导致无法正常的回收一些数据。
为了解决循环引用的问题,引入了标记清除技术,专门针对那些可能存在循环引用的对象进行特殊处理,可能存在循环应用的类型有:列表、元组、字典、集合、自定义类等那些能进行数据嵌套的类型。
标记清除:创建特殊链表专门用于保存 列表、元组、字典、集合、自定义类等对象,之后再去检查这个链表中的对象是否存在循环引用,如果存在则让双方的引用计数器均 - 1
。
分代回收:对标记清除中的链表进行优化,将那些可能存在循引用的对象拆分到3个链表,链表称为:0/1/2
三代,每代都可以存储对象和阈值,当达到阈值时,就会对相应的链表中的每个对象做一次扫描,除循环引用各自减1并且销毁引用计数器为 0
的对象。
特别注意:0代和1、2代的threshold和count表示的意义不同。其中源码中 0/1/2代默认的大小分别为700、10、10
0代,count表示0代链表中对象的数量,threshold表示0代链表对象个数阈值,超过则执行一次0代扫描检查。
1代,count表示0代链表扫描的次数,threshold表示0代链表扫描的次数阈值,超过则执行一次1代扫描检查。
2代,count表示1代链表扫描的次数,threshold表示1代链表扫描的次数阈值,超过则执行一2代扫描检查。
当0代大于阈值后,底层不是直接扫描0代,而是先判断2、1是否也超过了阈值。
对拼接起来的链表在进行扫描时,主要就是剔除循环引用和销毁垃圾,详细过程为:
gc_refs
中,保护原引用计数器。gc_refs
减 1 。gc_refs
为 0
的对象移动到 unreachable
链表中;不为 0
的对象直接升级到下一代链表中。__del__
方法的对象,需要执行之后再进行销毁处理。至此,垃圾回收的过程结束。
缓存机制
实际上反复的创建和销毁会使程序的执行效率变低。Python中引入了“缓存机制”机制。
例如:引用计数器为 0
时,不会真正销毁对象,而是将他放到一个名为 free_list
的链表中,之后会再创建对象时不会在重新开辟内存,而是在 free_list
中将之前的对象来并重置内部的值来使用。
float类型,维护的
free_list
链表最多可缓存100个float对象。
int类型,不是基于free_list
,而是维护一个small_ints
链表保存常见数据(小数据池),小数据池范围:-5 <= value < 257
。即:重复使用这个范围的整数时,不会重新开辟内存。
str类型,维护unicode_latin1[256]
链表,内部将所有的ascii字符缓存起来,以后使用时就不再反复创建。
list类型,维护的free_list
数组最多可缓存80个list对象。
tuple类型,维护一个free_list
数组且数组容量20,数组中元素可以是链表且每个链表最多可以容纳2000个元组对象。元组的free_list数组在存储数据时,是按照元组可以容纳的个数为索引找到free_list数组中对应的链表,并添加到链表中。
dict类型,维护的free_list
数组最多可缓存80个dict对象。
在绑定属性时,如果我们直接把属性暴露出去,虽然写起来很简单,但是,没办法检查参数。
Python内置的 @property
装饰器就是负责把一个方法变成属性调用的。@property
本身又创建了另一个装饰器 @score.setter
,负责把一个 setter 方法变成属性赋值。只定义 getter 方法,不定义 setter 方法就是一个只读属性。
with语句的作用是通过某种方式简化异常处理,它是所谓的上下文管理器的一种,with
语句会在嵌套的代码执行之后,自动关闭文件。
Python中的内置数据结构(Built-in Data Structure):列表list、元组tuple、字典dict、集合set,涵盖的仅有部分重点。
list:
tuple
dict:
set
元组不可变是指当前变量存放的元素不可变,存放的元素可以是数字、字符、列表、元组、字典;如果你定义的元组最外层变量里面包含可变类型元素,那么这个元组是可变的。
迭代是Python最强大的功能之一,是访问集合元素的一种方式。迭代器是一个可以记住遍历的位置的对象。
迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束。迭代器只能往前不会后退。
迭代器有两个基本的方法:iter() 和 next()。字符串,列表或元组对象都可用于创建迭代器。把一个类作为一个迭代器使用需要在类中实现两个方法 __iter__()
与 __next__()
。
在 Python 中,使用了 yield 的函数被称为生成器(generator)。
跟普通函数不同的是,生成器是一个返回迭代器的函数,只能用于迭代操作,更简单点理解生成器就是一个迭代器。
在调用生成器运行的过程中,每次遇到 yield 时函数会暂停并保存当前所有的运行信息,返回 yield 的值, 并在下一次执行 next() 方法时从当前位置继续运行。
调用一个生成器函数,返回的是一个迭代器对象。
在计算机科学中,闭包(英语:Closure),又称词法闭包(Lexical Closure)或函数闭包(function closures),是引用了自由变量的函数。这个被引用的自由变量将和这个函数一同存在,即使已经离开了创造它的环境也不例外。
函数可以作为另一个函数的参数或返回值,可以赋给一个变量。函数可 以嵌套定义,即在一个函数内部可以定义另一个函数,有了嵌套函数这种结构,便会产生闭包问题。
在函数式语言中,当内嵌函数体内引用到体外的变量时,将会把定义时涉及到的引用环境和函数体打包成一个整体(闭包)返回。
装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。
装饰器这一语法体现了Python中函数是第一公民,函数是对象、是变量,可以作为参数、可以是返回值,非常的灵活与强大。
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。
继承: 所表达的是类之间的相关关系,这种关系使得子类可以继承父类特征和行为,使得复用以前的代码非常容易,能够大大缩短开发周期,降低开发费用。