注意 py 中声明只有一个元素的元组的时候应该写成 t = (1,) 而不是 (1)。后者会被解释器解释为变量而非元组。

多继承

和 java 有着显著区别,python3 的类允许多继承。多继承带来了菱形问题(Diamond Problem):如果多个父类继承同一个基类,可能导致方法调用冲突。如下所示:

1
2
3
4
5
  A
/ \
B C
\ /
D
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
class A:
def method(self):
print("A")

class B(A):
def method(self):
super().method() # 调用 A.method()
print("B")

class C(A):
def method(self):
super().method() # 调用 A.method()
print("C")

class D(B, C):
def method(self):
super().method() # 按 MRO 调用 B → C → A
print("D")

d = D()
d.method() # A C B D
print(D.__mro__) # (D, B, C, A, object)

Python 3 使用 C3 线性化算法(MRO)解决冲突。在上面的例子中:

  1. 调用顺序:D → B → C → A(B 优先于 C,因为 class D(B, C) 里 B 在前)。
  2. 如果 B 没有 method(),则调用 C 的 method()。

上面的例子中 A 虽然被继承了 2 次,但是并不存在调用多次的问题。

多继承可以很方便实现 mix-in 模式。

slots

__slots__的目的是限制当前类所能拥有的属性,避免因为外部属性的操作导致类属性越来越难以管理。

1
2
3
4
5
6
7
8
9
class Student(object):
__slots__ = ('name', 'gender', 'score')
def __init__(self, name, gender, score):
self.name = name
self.gender = gender
self.score = score

s = Student('Bob', 'male', 59)
# s.age = 10 # AttributeError: 'Student' object has no attribute 'age'

call

__call__方法可以让一个类的实例像函数一样被调用。当实例后面加括号时,就会调用 __call__方法。

1
2
3
4
5
6
7
8
9
10
11
class Fib:
def __call__(self, count = 10):
res = []
a, b = 0, 1
for i in range(count):
res.append(a)
a, b = b, a + b
return res

f = Fib()
print(f(10)) # [0, 1, 1, 2, 3, 5, 8, 13, 21, 34]

函数式编程

可以实现延迟计算:

1
2
3
4
def calc_sum(list_):
def lazy_sum():
return sum(list_)
return lazy_sum

装饰器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import time

def performance(unit):
def perf_decorator(f):
def wrapper(*args, **kwargs):
t1 = time.time()
r = f(*args, **kwargs)
t2 = time.time()
t = (t2 - t1) * 1000 if unit == 'ms' else (t2 - t1)
print('call {}() in {} {}'.format(f.__name__, t, unit))
return r
return wrapper
return perf_decorator

@performance('ms')
# @performance('s')
def test(a):
# sleep 1 second
time.sleep(1)
print('test', a)

test(1)