*args和**kwargs
*args 和 **kwargs 主要用于函数定义。 你可以将不定数量的参数传递给一个函数.
*args 是用来发送一个非键值对的可变数量的参数列表给一个函数
1 | def print_args(f_arg, *argv): |
输出:
1 | first normal arg: a |
可以看到argv获得了b、c、d这三个传进来的参数,其实就是一个(“b”, “c”, “d”)这样的元组。
**kwargs 允许你将不定长度的键值对, 作为参数传递给一个函数.
1 | def print_kw(**kwargs): |
输出:
1 | name == jack |
这里kwargs就是一个字典,键值对就是调用时传进来的命名参数。
仍旧使用上面的两个函数,传参的时候就可以使用*args, **kwargs,注意args就是元组,kwargs就是字典。
1 | 1, 2, 3) args = ( |
注意普通参数与args、**kwargs的顺序:func(fargs, args, **kwargs)。
常见使用场景:定义装饰器时。
生成器
生成器也是迭代器,不过只能迭代一次,这是因为它们并没有把所有的值存在内存中,而是在运行时生成值。许多Python 2里的标准库函数都会返回列表,而Python 3都修改成了返回生成器,因为生成器占用更少的资源。函数返回使用yield而不是return的都是生成器函数。
1 | def generator(): |
注意可迭代对象和迭代器还有一些分别,迭代器是定义了next()或next方法的对象,可迭代对象是定义了iter或者getitem方法的对象。例如字符串是一个可迭代对象,但不是迭代器。
map filter reduce
1 | >>>items = [1, 2, 3] |
可变与不可变
可变对象: list、dict、set
不可变对象:tuple, str,
1 | foo = ['hi'] |
每当你将一个变量赋值为另一个可变类型的变量时,对这个数据的任意改动会同时反映到这两个变量上去,新变量只不过是老变量的一个别名而已。
slots 魔法
在Python中,定义类的时候定义其属性和方法,实例创建之后仍可动态为其添加属性和方法,这样很方便但也占用大量内存。使用slots可以限定允许绑定的属性,就只能是slots中指定的那些属性。
1 | class Point(object): |
注意slots 设置的属性仅对当前类有效,对继承的子类不起效,除非子类也定义了 slots,这样,子类允许定义的属性就是自身的 slots 加上父类的 slots.
协程
用户态的轻量级线程,线程和进程都是操作系统进行调度的,协程是用户程序自己调度的,可以暂时中断,之后再继续执行的程序,不需要陷入内核态,适用于 IO 密集型任务
协程的本质就是在单线程下,由用户自己控制一个任务遇到io阻塞了就切换另外一个任务去执行,以此来提升效率
1 | def coro(): |
重点在receive = yield value这一行,这一行可以分成三部分
- 向函数外返回value
- 暂停,等待next()或send()时恢复
- 外部send()时传入的数据被赋值给receive
整个的流程就是send(None)或者next()来启动协程,执行到yield的位置,返回value给外部并暂停,外部继续调用send(), 协程取出send()传递的参数并赋值给receive, 继续执行while循环到yield,返回value并暂停。
闭包
必须有一个内嵌函数
内嵌函数必须引用外部函数中的变量
外部函数的返回值必须是内嵌函数
Python垃圾回收机制
主要是引用计数,辅以标记清除和分代回收
python 2 3的区别
range xrange
raw_input input
print语句
整数除法, 1/4, python2下是0,python3下是0.25
unicode python3源码文件默认使用utf-8编码,字符串是unicode字符串
返回可迭代对象,而不是列表,如dict.keys(),dict.values(), dict.items()
函数缓存
Python3.2+:
1 | from functools import lru_cache |
Python2:
1 | from functools import wraps |
上下文管理器
最广泛使用的是with语句,用于资源的分配和释放,
1 | with open('some_file', 'w') as opened_file: |
等价于:
1 | file = open('some_file', 'w') |
可以看到with语句会确保文件的关闭,即使发生了异常,避免了常见的异常处理语句。
常见的场景:文件的打开、数据库会话的建立、资源的加锁和解锁。
让你定义的对象支持with语句,需要实现enter() 和 exit() 方法,实现了这两个方法就表明支持了上下文管理协议。
1 | from socket import socket, AF_INET, SOCK_STREAM |
这个类是一个网络连接,但初始化的时候并没有建立连接,连接的建立和关闭是使用 with 语句自动完成的。
1 | from functools import partial |
当出现 with 语句的时候,对象的 enter() 方法被触发, 它返回的值(如果有的话)会被赋值给 as 声明的变量。然后,with 语句块里面的代码开始执行,进行清理工作。如果with语句内发生异常,也是由exit方法来处理,如果执行过程没有出现异常,或者语句体中执行了语句 break/continue/return,则以 None 作为参数调用 exit(None, None, None);如果执行过程中出现异常,则使用 sys.exc_info 得到的异常信息为参数调用 exit(exc_type, exc_value, exc_traceback)。
出现异常时,如果 exit(type, value, traceback) 返回 False 或 None,则会重新抛出异常,让 with 之外的语句逻辑来处理异常;如果返回 True,则忽略异常,不再对异常进行处理。
contextlib 模块提供了三个对象:装饰器 contextmanager、函数 nested 和上下文管理器 closing。ontextmanager 是一个装饰器,用于装饰生成器函数,并返回一个上下文管理器。
1 | from contextlib import contextmanager |
虽然通过使用 contextmanager 装饰器,我们可以不必再编写 enter 和 exit 方法,但是『获取』和『清理』资源的操作仍需要我们自己编写:『获取』资源的操作定义在 yield 语句之前,『释放』资源的操作定义在 yield 语句之后。
大文件的读写
1 | read()#所有数据读成一个字符串 |
推荐做法:
1 | with open('foo.txt', 'r') as f: |
通过这种方式,Python将处理文件对象为1个迭代器,并自动使用缓存IO和内存管理,这样我们就不需要关注大的文件了。
赋值
Python中的=操作符用于给变量赋值,但其实Python中没有赋值,只有引用,例如 x = “hello”, 这个语句其实是创建了一个字符串对象”hello”, 然后给它贴了个标签叫x, 这是引用。
== 和 is 区别
==比较对象的值,is比较内存中的地址
init 和 new
init 只是单纯的对实例进行某些属性的初始化,以及执行一些需要在新建对象时的必要自定义操作,无返回值。而 new 返回的是用户创建的实例,这个才是真正用来创建实例的,所以 new 是在 init 之前执行的,先创建再初始化。
GIL
GIL 全称 Global Interpreter Lock(全局解释器锁),任何 Python 线程执行前,必须先获得 GIL 锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行
下面的两本小书强烈建议读一下,当然很多内容可能都已经了解了,但总会有新的收获的。
参考: