这段时间一直在写 python, 学了不少东西,今天填填坑,记录一下。

python 装饰器

之前了解过 python 的装饰器, 原理简单说来就是在被装饰的方法前后添加一些自定义的逻辑。参考文档

一个简单的装饰器

1
2
3
4
5
6
7
def a_decorator(func): 
def wrapper(*args, **kwargs):
"""A wrapper function"""
# do something here, or do some changes on params
func(*args, **kwargs)
# do something here
return wrapper

使用方法:

1
2
3
4
@a_decorator
def first_function():
"""This is docstring for first function"""
print("first function")

但是这样的装饰器是不完美的,如果有 log 打印或者其他一些关于方法的需求,如

1
2
3
4
5
print(first_function.__name__) 
print(first_function.__doc__)
# result blow
# wrapper
# A wrapper function

此时的打印的是装饰器的属性,这样可以预想是很不利于实际使用过程中的调试或者日志记录的。

我们手动赋值可以解决这个麻烦:

1
2
3
4
5
6
7
8
9
def a_decorator(func): 
def wrapper(*args, **kwargs):
"""A wrapper function"""
# do something here, or do some changes on params
func(*args, **kwargs)
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__
# do something here
return wrapper

或者使用 @wraps 装饰器也同样可以做到

1
2
3
4
5
6
7
8
9
from functools import wraps
def a_decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
"""A wrapper function"""
# do something here, or do some changes on params
func(*args, **kwargs)
# do something here
return wrapper

本质上来说, @wraps(func) 的作用相当于

1
2
wrapper.__name__ = func.__name__
wrapper.__doc__ = func.__doc__

python 类函数调用

众所周知, python 不同于 C/C+, 其是一个解释型的纯动态语言。所以很多设计模式,其实和编译型的语言有很大不同。

比如,今天在调试 docker containers 库的时候,一个类方法调用了一个私有方法(_开头), 在这个类中并没有找到这个方法,本能地去这个类的父类中找。 但其父类是 object,这就奇了怪了,难不成这个方法是自带的? 这显然不可能。

1
2
3
4
5
6
7
8
9
10
class ContainerApiMixin(object):
……
def create_container_from_config(self, config, name=None):
u = self._url("/containers/create")
params = {
'name': name
}
res = self._post_json(u, data=config, params=params)
return self._result(res, True)
……

第二反应是某个装饰器将其他类的方法 import 进来了,但查看 import, 依然没有。在实际调试的过程中,逐步 debug 定位到了实际的函数。查看原因发现,那个函数所在的类继承了上面那个类,也就是父类调用了子类的一个方法,但父类并没有实现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class APIClient(
requests.Session,
BuildApiMixin,
ConfigApiMixin,
ContainerApiMixin,
DaemonApiMixin,
ExecApiMixin,
ImageApiMixin,
NetworkApiMixin,
PluginApiMixin,
SecretApiMixin,
ServiceApiMixin,
SwarmApiMixin,
VolumeApiMixin):
pass

而在 C++ 程序当中是编译不过的。。至少得定义一个方法,让子类去重写呀。
不过联想到 python 语言的特性,其实也并没有错,但对于代码的可读性来说真的有点难受,只有调试一下才可以读懂里面的逻辑。