参考资料
一、调试(pdb)
Python debugger(pdb)通过再脚本内部设置断点,在某些特定点查看变量信息,有助于捕捉代码 bug。
关键操作:
- 单步进入:进入函数中并暂停
- 单步跳过:执行完函数后,停在函数下一行
使用 import pdb; pdb.set_trace() 在代码中设置断点。
二、生成器(Generator)
迭代器与可迭代对象
- 可迭代对象(iterable):实现了
__iter__ 或者 __getitem__ 方法
- 迭代器(iterator):定义了
__next__ 方法
生成器的特点
- 本身也是一种迭代器,但是只能对其迭代一次
- 生成器只在运行时生成值,并没有把值存在内存中
- 应用场景:不希望一次性将大量结果分配到内存中,防止消耗资源
示例:斐波那契数列生成器
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def fibon(n):
a = b = 1
for i in range(n):
yield a
a, b = b, a+b
# 使用 for 循环迭代
for x in fibon(100000):
print(x)
# 使用 next() 获取下一元素
tmp = fibon(100000)
print(next(tmp))
|
iter() 方法
iter() 可以根据一个可迭代对象返回一个迭代器对象。
1
2
3
4
|
# 字符串是可迭代对象,但不能直接 next()
# 需要先得到迭代器对象
it = iter('hello world')
print(next(it)) # 'h'
|
三、函数式编程
map - 映射
将函数映射到输入列表的所有元素上
1
2
|
map(函数, 列表)
list(map(lambda x: x**2, [1, 2, 3, 4])) # [1, 4, 9, 16]
|
filter - 过滤
过滤列表元素,返回满足条件的元素构成的列表
1
2
|
filter(条件, 列表)
filter(lambda x: x < 0, [-1, 2, -3, 4]) # [-1, -3]
|
reduce - 累积计算
对列表进行一些计算并返回结果
1
2
|
from functools import reduce
reduce(lambda x, y: x * y, [1, 2, 3, 4]) # 24
|
四、集合操作
集合中不能包含重复的值
1
2
3
4
5
6
7
8
|
A = set(['yellow', 'red', 'blue', 'green', 'black'])
B = set(['red', 'brown'])
# 交集
B.intersection(A) # {'red'}
# 差集:B 集合减去 A 集合后的结果
B.difference(A) # {'brown'}
|
五、三元运算符
作为条件表达式使用
1
|
state = 'health' if ate_breakfast else '!!!'
|
六、装饰器(Decorator)
核心概念
- 函数可以像变量一样被传递和赋值
- 将函数作为参数传递给另一个函数
基础装饰器实现
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def a_new_decorator(a_func):
def wrapTheFunction():
print("Hello")
a_func()
print("Done!")
return wrapTheFunction
def a_function_requiring_decoration():
print("我需要一个包装")
# 手动装饰
a_function_requiring_decoration = a_new_decorator(a_function_requiring_decoration)
a_function_requiring_decoration()
|
@语法糖
1
2
3
|
@a_new_decorator
def a_function_requiring_decoration():
print("我需要一个包装")
|
保留原函数元信息
使用 functools.wraps 解决函数名被覆盖的问题
1
2
3
4
5
6
7
8
9
|
from functools import wraps
def a_new_decorator(a_func):
@wraps(a_func)
def wrapTheFunction():
print("Hello")
a_func()
print("Done!")
return wrapTheFunction
|
装饰器应用场景
1. 授权(Web框架中的用户认证)
1
2
3
4
5
6
7
8
9
10
|
from functools import wraps
def requires_auth(f):
@wraps(f)
def decorated(*args, **kwargs):
auth = request.authorization
if not auth or not check_auth(auth.username, auth.password):
authenticate()
return f(*args, **kwargs)
return decorated
|
2. 日志记录
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
from functools import wraps
def logit(func):
@wraps(func)
def with_logging(*args, **kwargs):
print(func.__name__ + " was called")
return func(*args, **kwargs)
return with_logging
@logit
def addition_func(x):
return x + x
result = addition_func(4) # 输出:addition_func was called
|
装饰器类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
from functools import wraps
class logit(object):
def __init__(self, logfile='out.log'):
self.logfile = logfile
def __call__(self, func):
@wraps(func)
def wrapped_function(*args, **kwargs):
log_string = func.__name__ + " was called"
print(log_string)
with open(self.logfile, 'a') as opened_file:
opened_file.write(log_string + '\n')
self.notify()
return func(*args, **kwargs)
return wrapped_function
def notify(self):
pass
# 使用
@logit()
def myfunc1():
pass
@logit(logfile='func2.log')
def myfunc2():
pass
|
带参数的装饰器(三层嵌套)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import functools
import time
def log2(name):
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime()), end='\t')
print(f'{name} is calling: ' + func.__name__)
return func(*args, **kwargs)
return wrapper
return decorator
@log2(name='Tom')
def some_func():
pass
|
七、global 与 return
global 关键字:定义的变量可以在函数以外的区域访问到
返回多个值:使用元组、列表或者字典,不要通过 global 传递参数
八、对象的可变与不可变
- 可变类型变量:当赋值给另一个变量时,对新变量的改动会反映到原变量
- 新变量只是老变量的一个别名
最佳实践:将默认参数设为 None,然后在函数中判断并赋值
1
2
3
4
|
def func(items=None):
if items is None:
items = []
# 安全地使用 items
|
九、__slots__ 魔法
告知 Python 不要使用字典来存储对象的实例属性,只给固定集合的属性分配空间,节省内存。
1
2
3
4
5
|
class Myclass(object):
__slots__ = ['name', 'identifier']
def __init__(self, name, identifier):
self.name = name
self.identifier = identifier
|
十、虚拟环境
当多个 Python 项目的第三方模块出现版本冲突时,使用虚拟环境隔离。
特点:
十一、collections 模块
defaultdict - 无需检查 key 是否存在
1
2
3
|
from collections import defaultdict
d = defaultdict(int)
d['missing'] += 1 # 自动初始化为0
|
Counter - 计数器
1
2
3
|
from collections import Counter
cnt = Counter('abracadabra')
# Counter({'a': 5, 'b': 2, 'r': 2, 'c': 1, 'd': 1})
|
deque - 双端队列
1
2
3
4
5
6
|
from collections import deque
d = deque([1, 2, 3])
d.append(4) # 右端添加
d.appendleft(0) # 左端添加
d.pop() # 右端删除
d.popleft() # 左端删除
|
十二、enumerate 枚举
遍历数据并自动计数
1
2
3
4
5
|
for i, value in enumerate(['a', 'b', 'c']):
print(i, value)
# 0 a
# 1 b
# 2 c
|
十三、对象自省
运行时判断对象类型的能力
dir():返回对象拥有的属性和方法列表
type():返回对象的类型
id():返回对象的 id 值
inspect 模块:获取活跃对象的信息
1
2
|
import inspect
inspect.getmembers(str)
|
十四、推导式(解析式)
简明扼要地创建列表、字典或集合
1
2
3
4
5
|
# 交换键和值
{v: k for k, v in some_dict.items()}
# 集合推导式(使用大括号)
{x**2 for x in range(10)}
|
十五、异常处理
1
2
3
4
5
6
7
8
9
10
11
12
|
try:
file = open('test.txt', 'rb')
except Exception:
# 打印日志,如果想要的话
raise # 重新抛出异常
else:
# try 中没有触发异常时执行
# 注意:else 中的异常不会被捕获
pass
finally:
# 清理工作,始终执行
file.close()
|
十六、lambda 函数(匿名函数)
使用场景有限,适用于排序、聚合等简单操作
1
2
|
# 排序时使用
sorted([('a', 3), ('b', 1)], key=lambda x: x[1])
|
十七、一行式技巧
快速启动HTTP服务器
1
|
python -m http.server 6789
|
漂亮打印
1
2
3
|
from pprint import pprint
my_dict = {'name': 'Yasoob', 'age': 'undefined', 'personality': 'awesome'}
pprint(my_dict)
|
构造器批量赋值
1
2
3
|
class A(object):
def __init__(self, a, b, c, d, e, f):
self.__dict__.update({k: v for k, v in locals().items() if k != 'self'})
|
十八、for-else
for 循环的 else 从句在循环正常结束时执行
1
2
3
4
5
6
7
8
|
for n in range(2, 100):
for x in range(2, n):
if n % x == 0:
print(n, '=', x, '*', n/x)
break
else:
# 循环正常结束(未 break)时执行
print(n, '是一个质数')
|
十九、C 扩展
使用 Python 调用 C 代码的场景:
- 提升代码运行速度
- 复用 C 语言类库
- 访问底层资源
1. ctypes - 调用动态链接库
1
2
3
4
5
6
7
8
9
|
from ctypes import *
adder = CDLL('./adder.dll')
a = c_float(5.5)
b = c_float(4.1)
add_float = adder.add_float
add_float.restype = c_float
add_float(a, b)
|
2. SWIG - 接口生成器
Simplified Wrapper and Interface Generator
3. Python/C API - 编写 C 模块
需要以特定方式编写 C 代码,所有 Python 对象表示为 PyObject 结构体
示例:列表求和的 C 模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
// adder.c
#include <Python.h>
static PyObject* addList_add(PyObject* self, PyObject* args) {
PyObject* listObj;
if (!PyArg_ParseTuple(args, "O", &listObj)) {
return NULL;
}
long length = PyList_Size(listObj);
int i, sum = 0;
for (i = 0; i < length; i++) {
PyObject* temp = PyList_GetItem(listObj, i);
long elem = PyInt_AsLong(temp);
sum += elem;
}
return Py_BuildValue("i", sum);
}
static PyMethodDef addList_funcs[] = {
{"add", (PyCFunction)addList_add, METH_VARARGS, "Add all elements"},
{NULL, NULL, 0, NULL}
};
PyMODINIT_FUNC initaddList(void) {
Py_InitModule3("addList", addList_funcs, "Add all ze lists");
}
|
1
2
3
4
|
# setup.py
from distutils.core import setup, Extension
setup(name='addList', version='1.0',
ext_modules=[Extension('addList', ['adder.c'])])
|
二十、open 函数
最佳实践:使用 with 语句自动管理文件句柄
1
2
|
with open('demo.txt', 'w') as f:
f.write('Hola!')
|
- 文本文件:
'r', 'r+', 'w', 'a'
- 二进制文件:
'rb', 'wb'
- 编码:多数文件使用 UTF-8
二十一、协程(Coroutine)
与生成器类似,但生成器是数据的生产者,协程是数据的消费者
协程示例
1
2
3
4
5
6
7
8
9
10
11
12
13
|
def grep(pattern):
print("Searching for", pattern)
while True:
line = (yield) # 接收外部传入的值
if pattern in line:
print(line)
# 使用
search = grep('coroutine')
next(search) # 启动协程
search.send("I love you")
search.send("I love coroutine instead!") # 输出:I love coroutine instead!
search.close() # 关闭协程
|
二十二、函数缓存(Memoization)
将函数对给定参数的返回值缓存起来,适用于 I/O 密集且频繁使用相同参数的函数
使用 lru_cache
1
2
3
4
5
6
7
8
|
from functools import lru_cache
@lru_cache(maxsize=32)
def somefunc():
pass
# 清空缓存
somefunc.cache_clear()
|
手动实现
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
from functools import wraps
def memoize(function):
memo = {}
@wraps(function)
def wrapper(*args):
if args in memo:
return memo[args]
else:
rv = function(*args)
memo[args] = rv
return rv
return wrapper
@memoize
def somefunc():
pass
|
二十三、上下文管理器
类实现
1
2
3
4
5
6
7
8
9
10
11
12
|
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, type, value, traceback):
print('Exception has been handled')
self.file_obj.close()
return True # 返回 True 表示异常已处理
with File('demo.txt', 'w') as opened_file:
opened_file.write('Hola!')
|
使用 contextlib
1
2
3
4
5
6
7
8
9
10
|
from contextlib import contextmanager
@contextmanager
def myopenfile(name):
f = open(name, 'w')
yield f
f.close()
with myopenfile('some_file') as f:
f.write('hola!')
|
二十四、闭包(Closure)
闭包的特性
- 外部函数的变量被"记住",即使外部函数已执行完毕
- 闭包与闭包之间的状态是隔离的
1
2
3
4
5
6
7
|
def outer_function(x):
def inner_function(y):
return x + y
return inner_function
closure = outer_function(10)
result = closure(5) # 15
|
记录状态的闭包
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
def make_score():
lis = []
def inner(x):
lis.append(x)
print(lis)
return inner
first = make_score()
second = make_score()
first(1) # [1]
first(2) # [1, 2]
second(3) # [3]
second(4) # [3, 4]
|
nonlocal 关键字
操作不可变类型时需要 nonlocal
1
2
3
4
5
6
7
|
def counter():
count = 0
def increment():
nonlocal count
count += 1
return count
return increment
|
解决延迟绑定陷阱
1
2
3
4
5
6
7
8
9
10
11
12
13
|
# 错误示例:所有函数都输出 2
funcs = []
for i in range(3):
funcs.append(lambda: i)
# 正确示例:使用闭包立即捕获值
funcs = []
for i in range(3):
def outer(a):
def inner():
print(a)
return inner
funcs.append(outer(i))
|
二十五、柯里化(Currying)
只接收部分参数,返回携带状态的新函数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
def curry(f):
argc = f.__code__.co_argcount
f_args = []
f_kwargs = {}
def g(*args, **kwargs):
nonlocal f_args, f_kwargs
f_args += args
f_kwargs.update(kwargs)
if len(f_args) + len(f_kwargs) == argc:
return f(*f_args, **f_kwargs)
else:
return g
return g
def add(a, b, c):
return a + b + c
c_add = curry(add)
c_add(1)(2)(3) # 6
|
核心概念
type 是所有类的元类
- 元类 = 创建类的类
- 元类 → 类 → 类实例
type 动态创建类
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
# 等价于 class Bar: pass
Bar = type('Bar', (), {})
# 等价于 class Foo(Bar): pass
Foo = type('Foo', (Bar,), {})
# 带属性和方法
def f(obj):
return 'hello world'
Foo = type('Foo', (Bar,), {
'num': 100,
'hi': f
})
|
自定义元类
1
2
3
4
5
6
7
8
9
10
|
class MyMeta(type):
def __new__(cls, name, bases, dct):
obj = super().__new__(cls, name, bases, dct)
obj.num = 100 # 给所有类添加 num 属性
return obj
class Foo(metaclass=MyMeta):
pass
print(Foo.num) # 100
|
应用1:强制子类实现方法
1
2
3
4
5
6
7
8
9
10
11
12
13
|
class Meta(type):
def __new__(cls, name, bases, dct, **kwargs):
if name != 'Father' and 'bar' not in dct:
raise TypeError('Class must contain bar() method.')
return super().__new__(cls, name, bases, dct, **kwargs)
class Father(metaclass=Meta):
def foo(self):
return self.bar()
# 如果子类没有 bar(),会立即报错
class Child(Father):
pass # TypeError: Class must contain bar() method.
|
应用2:动态添加方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
class Meta(type):
def __new__(cls, name, bases, dct, **kwargs):
if name == 'Apple':
dct.update({
'sayHi': lambda: 'Hi I am Apple'
})
return super().__new__(cls, name, bases, dct, **kwargs)
class Food(metaclass=Meta):
pass
class Apple(Food):
pass
class Pear(Food):
pass
Apple.sayHi() # 'Hi I am Apple'
Pear.sayHi() # AttributeError
|
应用3:ORM(如 Django)
1
2
3
|
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField()
|
补充:装饰器标准模板
1
2
3
4
5
6
7
8
9
10
|
import functools
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
# 原函数运行前
value = func(*args, **kwargs)
# 原函数运行后
return value
return wrapper
|
装饰器叠加:类似洋葱,多层包裹
1
2
3
4
5
|
@decorator1
@decorator2
def func():
pass
# 等价于 func = decorator1(decorator2(func))
|