Contents

Python进阶手册

Contents

参考资料

一、调试(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

二十六、元类(Metaclass)

核心概念

  • 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))