可变对象和不可变对象
python中所有东西都是一个对象
- 不可变对象:int,string,float,tuple
- 可变对象 :list,dictionary
不可变对象immutable
python中的对象存放的是对象引用,所以尽管不可变对象本身不可变,但其对象引用是可变的
可变对象mutable
可变对象的内容可以发生变化,但其引用不会改变
地址问题
id()返回一个对象的地址或者用is;==对比值
不可变对象的引用改变后,其地址发生变化;可变对象改变后其地址不变
参数传递
python中向函数传参都是引用传递(传地址)
当传的参数是不可变对象时,函数复制一份引用,所以无论怎么操作传入的参数都不会对函数外的变量造成影响;当传入的参数是可变对象时,函数内操作会改变函数外变量的内容
不要用可变对象作为参数的默认值
迭代器和生成器
迭代
可迭代对象:实现了iter方法的对象
1 | for k, v in d.items() |
生成器
优点:对延迟操作提供了支持,在需要的时候才产生结果,而不是立即产生结果,减少资源消耗;可读性强(认准yield即可知道返回的结果)
生成器函数
每次调用next()执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行
1 | def gensquatres(N): |
生成式表达式
1 | squares = (x**2 for x in range(5)) |
注意:生成器只能遍历一次
迭代器
迭代器:实现了迭代器协议的对象,可以用isinstance()判断
迭代器协议:对象需要提供next()方法和iter()方法返回迭代器类的实例,返回下一项或引起StopIteration异常,以终止迭代
yield from
yield from后加上可迭代对象可以将其每个元素yield出来
生成嵌套
在yield from后面加上生成器
调用方:调用委派生成器的客户端(调用方)代码
委托生成器:包含yield from表达式的生成器函数
- 子生成器:yield from后面加的生成器函数
1 | # 子生成器 |
委托生成器,在调用方与子生成器之间建立一个双向通道,可以简化代码,而且会自动处理异常
函数式编程
map-reduce
1 | from functools import reduce |
配合lambda函数可以进一步简化
filter
filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list
1 | def is_odd(n): |
sorted
装饰器
被用于有切面需求的场景,较为经典的有插入日志、性能测试、事务处理等。为已经存在的对象添加额外的功能,并重用代码
1 | def log(text): |
另一个例子
1 | from functools import wraps |
1 | print hello() # returns "<b><i>hello world</i></b>" |
面向对象
类中定义的函数只有一点不同,就是第一个参数永远是实例变量self,并且,调用时,不用传递该参数
私有变量
__开头的变量,原理为将该变量重命名为(真正的私有变量)
1 | _classname__varname |
_开头是一种约定,为私有变量
函数重载
函数重载能解决的问题:
- 可变参数类型:python 可以接受任何类型的参数,如果函数的功能相同,那么不同的参数类型在 python 中很可能是相同的代码,没有必要做成两个不同函数
- 可变参数个数:对那些缺少的参数设定为缺省参数即可解决问题。因为你假设函数功能相同,那么那些缺少的参数终归是需要用的
故python中不需要函数重载
属性
实例可以任意绑定属性和方法。给实例绑定属性的方法是通过实例变量,或者通过self变量
类属性直接在class中定义属性
注意实例属性会屏蔽类属性,故类属性和实例属性不要重名
__slots _
_slots__变量用于限制该class实例能添加的属性
1 | class Student(object): |
仅对当前类实例起作用,对继承的子类是不起作用的
@property
既能检查参数,又可以用类似属性这样简单的方式来访问类的变量
1 | class Student(object): |
1 | s = Student() |
静态方法、实例方法、类方法
\ | 实例方法 | 类方法 | 静态方法 |
---|---|---|---|
a = A() | a.foo(x) | a.class_foo(x) | a.static_foo(x) |
A | 不可用 | A.class_foo(x) | A.static_foo(x) |
init()和new()方法
__new()方法在init()方法之前执行
真正创建实例是new方法,init__方法做的事情是在对象创建好之后初始化变量
new方法返回一个创建的实例,init方法不返回
异常处理
捕获错误时也会捕获其子类
序列化
class和json的转化
1 | json.dumps(item, default=lambda obj: obj.__dict__) |
协程
python对协程的支持是通过generator实现的
特点
在单线程里实现任务的切换的
利用同步的方式去实现异步
不再需要锁,提高了并发性能
生产者消费者:
1 | def consumer(): |
注意 send(n)为将参数传给yield
垃圾回收机制
Python GC主要使用引用计数(reference counting)来跟踪和回收垃圾。在引用计数的基础上,通过“标记-清除”(mark and sweep)解决容器对象可能产生的循环引用问题,通过“分代回收”(generation collection)以空间换时间的方法提高垃圾回收效率
引用计数
PyObject是每个对象必有的内容,其中ob_refcnt就是做为引用计数。当一个对象有新的引用时,它的ob_refcnt就会增加,当引用它的对象被删除,它的ob_refcnt就会减少.引用计数为0时,该对象生命就结束了。
优点:
- 简单
- 实时性
缺点:
- 维护引用计数消耗资源
- 循环引用
标记-清除机制
基本思路是先按需分配,等到没有空闲内存的时候从寄存器和程序栈上的引用出发,遍历以对象为节点、以引用为边构成的图,把所有可以访问到的对象打上标记,然后清扫一遍内存空间,把所有没标记的对象释放
分代技术
将系统中的所有内存块根据其存活时间划分为不同的集合,每个集合就成为一个“代”,垃圾收集频率随着“代”的存活时间的增大而减小,存活时间通常利用经过几次垃圾回收来度量
单例模式
new方法
1 | class Singleton(object): |
装饰器版本
1 | def singleton(cls, *args, **kw): |
GIL线程全局锁
线程全局锁(Global Interpreter Lock),即Python为了保证线程安全而采取的独立线程运行的限制,就是一个核只能在同一时间运行一个线程.
解决办法就是多进程和下面的协程(协程也只是单CPU,但是能减小切换代价提升性能)
闭包
- 在一个外函数中定义了一个内函数
- 内函数里运用了外函数的临时变量
- 外函数的返回值是内函数的引用
其中的临时变量只能被读取不能更改,若要等该要引入nonlocal语句
作用:类似装饰器
1 | def make_sing(animal): |
细节
连续赋值
1 | a, b = b, a + b |
相当于
1 | t = (b, a + b) # t是一个tuple |
*args & **kwargs
- *args:可以传递任意数量的参数,index-value
- **kwargs:可以使用没有事先定义的参数名,name-value
拷贝
1 | import copy |