1. 装饰器之functools.wraps
装饰器很好用极大地复用了代码,但是不可否认在给原函数使用装饰器后原函数的一些元数据信息比如函数名、注释、参数列表不见了,比如swagger api文档函数注释功能不可见,比如django rest_framework视图某个接口下@detail_route()再加上一个其他功能的装饰器甚至没法自动生成路由等。好在有functools.wraps解决了这个问题,wraps也是一个装饰器,它能把原函数的信息拷贝到装饰器里边的函数中,具体用法可参照如下,也建议写装饰器都这么写:
from functools import wraps
def test_dec00(func):
@wraps(func)
def wrapper(*args, **kwargs):
...
func(*args, **kwargs)
...
return wrapper
2. Django之日志处理
Django采用python内置的logging模块来处理系统日志,而一个python logging配置至少包括以下三部分:
- loggers:日志系统的入口,也就是常说的日志记录器;
- handlers:用于将日志记录发送到指定的目标位置,有关logging handlers可参见logging.handlers;
- formatters:用于控制日志记录的具体输出格式,支持的具体格式可参照logging模块支持的格式属性;
其实日志系统还包含一个组件filters
,提供更细粒度的日志过滤功能,不过实际采用中一般不多(因为上述三个组件基本上可以满足我对日志的所有需求),这里就不再赘述。
下面举例来说明一下Django配置logging日志处理的常规做法:
- 先写一个日志的专门配置文件,文件内容包括
loggers、formatters、handlers
等组件的配置,比如文件名log_conf.py
,具体内容可参照如下样式:
# log_conf.py
logging_conf = {
'version': 1,
'disable_existing_loggers': False,
'formatters': {
'simple': {
'format': '%(levelname)s - %(asctime)s - %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
},
'console': {
'format':'%(levelname)s: %(asctime)s -- %(pathname)s:%(lineno)d - %(message)s',
'datefmt': '%Y-%m-%d %H:%M:%S'
}
},
'handlers': {
'console': {
'level': 'INFO',
'class': 'logging.StreamHandler',
'formatter': 'console'
},
'fileHandler': {
'level': 'INFO',
'class': 'logging.handlers.TimedRotatingFileHandler',
'formatter': 'simple',
'filename': '/Users/vienfu/test_logs/test_error.log'
}
},
'loggers': {
'mdjango': {
'handlers': ['console', 'fileHandler'],
'level': 'INFO',
'propagate': False
}
},
'root': {
'level': 'WARNING',
'handlers': ['console', 'fileHandler'],
}
}
- 在django settings.py做日志的配置:
# settings.py
# Django Logging Settings
from log_conf import logging_conf
LOGGING = logging_conf
- 实际调用处理,在需要日志记录的函数或接口先实例化一个日志记录器:
import logging
from rest_framework.viewsets import ViewSet
logger = logging.getLogger(__name__)
# logger = logging.getLogger('mdjango') 一般这两种来实例化日志记录器,前者呢常会对应到root logger,而后则指明具体的logger
class TestViewSet(ViewSet):
def list(self, request):
...
logger.error('this is a demo logging error test')
...
如此,Django下日志配置及处理就基本上完成了。
3. Django之自定义错误码
4. Python中下划线命名含义
- 单划线开头:比如_val,用作函数的私有变量,不能直接
import
。 - 单划线结尾:用作区分比如一些关键字等。
- 双划线开头:双下划线开头的命名形式在 Python 的类成员中使用表示名字改编 (Name Mangling),即如果有一 Test 类里有一成员 __x,那么 dir(Test) 时会看到 _Test__x 而非 __x。这是为了避免该成员的名称与子类中的名称冲突。
- 双划线结尾和开头:python的魔法对象,比如
类中的__init__、__call__、__del__、__getitem__等,全局的__doc__、__func__、__file__、__name__
等,用于跟程序员自定义的作以区分。
5. 鸭子类型
先引用一句关于python鸭子模式最经典的话:如果一只鸟走起路来像鸭子,叫声也跟鸭子一样,然后游泳也跟鸭子一样,那么这只鸟就是鸭子。
6. Python新式类与旧式类
# 新、旧式类的定义
class A(object): # 新式类的定义
pass
class A: # 旧式类的定义
pass
# 多重继承下的MRO(Method Resolution Order)的问题,举个栗子:
class E(object): # 新式类
# class E: # 旧式类
def __init__(self):
print('welcome to class E')
def extra(self):
print('welcome to extral E')
class F(E):
def __init__(self):
print('welcome to class F')
def extra(self):
print('welcome to extral F')
class G(E):
def __init__(self):
print('welcome to class G')
class H(G, F):
def __init__(self):
print('welcome to class H')
if __name__ == '__main__':
class_h = H()
class_h.extra()
# 输出结果:
welcome to class H
welcome to extral F
但如果把例子中新式类换成旧式类,结果就会变成:
welcome to class H
welcome to extral E
这是由各自的MRO导致,新式类MRO是H->G->F->A
(先左后右,广度优先),而旧式类则是H->G->E->F
(先左后右,深度优先)。
7. __new__和__init__的区别
- __new__是用来控制实例的创建,而__init__则是用来初始化实例的;
- 前者会返回一个新建的实例(也就是
self
),而后者什么也不返回(或者返回None
,不过不建议这种写法,规范写法是不做return
); - 只有前者返回一个cls的实例之后后者才会被调用;
举一个通过__new__构建单例模式的栗子:
class Singleton(object):
_instance = None
def __new__(cls, *args, **kwargs):
if not cls._instance:
cls._instance = super(Singleton, cls).__new__(cls, *args, **kwargs)
return cls._instance
class myclass(Singleton):
pass
扩展
这里顺便说一下__call__
的用法,如果在一个类中实现了__call__
方法,则该类的实例变成可调用的,举个简单的例子:
# -*- coding: utf-8 -*-
class TestClass(object):
def __init__(self):
print('Just test __call__')
def __call__(self):
print('testing __call__ func')
if __name__ == '__main__':
class_A = TestClass()
class_A() # 实例可以直接是可调用的
-
运行结果:
通过这种方式可以实现基于类的装饰器,在类里面记录状态实现获取函数的状态等。
8. MetaClass(元类)
python中一切皆为对象,可以这样理解:类的实例对象是类的实例化的结果,而类则是元类的实例化,可以通过自定义元类来创建类并改变类的行为。python默认的元类是type
,所有对象归根结底都是有它创建出来的,又被叫做类工场
,当然也可以通过继承type
自定义一个元类,下面是一个通过元类来实现单例模式的例子:
# -*- coding: utf-8 -*-
# test_metaclass01.py
# 通过元类来实现单例模式
class Singleton(type):
def __init__(self, *args, **kwargs):
self._instance = None
super(Singleton, self).__init__(*args, **kwargs)
def __call__(self, *args, **kwargs):
if self._instance is None:
self._instance = super(Singleton, self).__call__(*args, **kwargs)
return self._instance
class TestSingleton(object):
__metaclass__ = Singleton
def __init__(self):
print('just a test')
-
下图是测试元类实现单例的结果:
9. 猴子补丁
属性(类、模块、方法等)在运行状态下的动态替换,就叫猴子补丁,一般在动态编程语言中使用,比如python中类,下面举一个简单的例子:
# -*- coding: utf-8 -*-
class OneClass(object):
def __init__(self):
print('Just a Monkey_Patch test')
def echo_something(self, value):
print('welcome {}'.format(value))
def show_something(ins, value):
print('Start testing Monkey_Patch ...')
print('welcome {}'.format(value))
print('Test Success.')
if __name__ == '__main__':
class_A = OneClass()
class_A.echo_something(666)
OneClass.echo_something = show_something
class_A.echo_something(666)
运行结果如下:

总之,猴子补丁的功能就是运行时动态地增加或替换模块的属性,实际使用中,比如想引用公司通用库的同时要做一些功能的拓展,除了继承实现外还可以考虑猴子补丁。
10. 抽象基类
ABC(abstract bases class),定义了一组行为,不必实现,不能够直接实例化,一般基于它的子类需要重写函数实现具体的接口,它一般通过如下两种途径来使用:
- 继承方式:继承抽象基类的子类必须要重写或者覆盖基类的抽象方法(
@abstractmethod
)和抽象属性(@abstractproperty
),而其他的方法和属性可直接继承或重写。 - 注册方式:这种子类又叫虚拟子类,调用抽象基类的
register
方法,可以实现抽象基类的部分接口也可以根本不实现,但使用issubclass、isinstance
检测类型时依然是True
,比较灵活。
Python中实现抽象基类的模块:abc
模块,它里面定义的一个叫ABCMeta
的元类以及上文提到抽象方法、抽象属性等装饰器,如果自定义一个抽象基类可参考如下方法:
# python 3.x
class MyAbstractClass(object, metaclass=abc.ABCMeta):
pass
# python 2.x
class MyAbstractClass(object):
__metaclass__ abc.ABCMeta
pass
# 其实跟前文使用元类的方法完全一样!!
# 还有一个兼容python 2.x和3.x方法,使用six模块
@six.add_metaclass(abc.ABCMeta)
class MyAbstractClass(object):
pass
11. 协程
11.1 yield VS yield from
直接看个实例来理解吧:
# -*- coding: utf-8 -*-
# 斐波那契数列
def fab(max):
n, a, b = 0, 0, 1
while n < max:
yield b
a, b = b, a + b
n += 1
def warpper(func):
print('start')
# for item in func(5):
# yield item
yield from func(5) # 这一行作用相当于上面注释的两行语句
print('end')
if __name__ == '__main__':
test_gen = warpper(fab)
for item in test_gen:
print(item)
-
运行结果:
11.2 Python协程的发展
- send和yield
- asyncio.coroutine和yield from
- async和await
网友评论