文章目录
  1. 1. 高级语法特性部分
    1. 1.1. List comprehension
    2. 1.2. Dict comprehension
    3. 1.3. Set comprehension
    4. 1.4. Functional funtion
      1. 1.4.1. map()
      2. 1.4.2. filter()
      3. 1.4.3. Reduce
    5. 1.5. Iterator
      1. 1.5.1. 如何判断container是否是可迭代的
    6. 1.6. Generator
      1. 1.6.1. Generator to list
    7. 1.7. Decorator
      1. 1.7.1. 函数式性质
      2. 1.7.2. Decorator 本质上是什么
        1. 1.7.2.1. Method decorator
        2. 1.7.2.2. Class Decorator
      3. 1.7.3. Decorator的应用
        1. 1.7.3.1. Decorator的另类用法
    8. 1.8. Context manager
    9. 1.9. mataclass
      1. 1.9.1. 什么是metaclass
      2. 1.9.2. 如何自定义和使用一个metaclass
      3. 1.9.3. metaclass 对应的生命周期方法hook
        1. 1.9.3.1. 类指定自定义的metaclass后生命周期方法调用顺序
        2. 1.9.3.2. 实例化类后生命周期方法调用顺序
      4. 1.9.4. metaclass 在实际项目中的应用
        1. 1.9.4.1. metaclass 在pyyaml framework中的使用
      5. 1.9.5. metaclass 在Django ORM中的使用
  2. 2. References

高级语法特性部分

List comprehension

1
2
[i for i in range(10) if i % 2 == 0]
# [0, 2, 4, 6, 8]

Dict comprehension

1
2
dict_square = {i: i**2 for i in range(3)}
# {0: 0, 1: 1, 2: 4}

Set comprehension

1
2
set_comprehension_expres = {i**2 for i in [1, 1, 2]}
# {1, 4}

Functional funtion

map()

1
map(function_to_apply, list_of_inputs)

例如将一个tuple list取出第一个元素并返回列表

1
2
3
_list_tuple = [(1, 1), (2, 3), (3, 5), (4, 7)]
_list_tuple_first_elem = list(map(lambda x : x[0], _list_tuple))
# [1, 2, 3, 4]

filter()

1
filter(function_to_apply, list_of_inputs)

紧接着上面的例子,从tuple list 取出第一个元素,并返回元素为偶数的列表:

1
2
3
_list_tuple = [(1, 1), (2, 3), (3, 5), (4, 7)]
_list_tuple_first_elem = list(map(lambda x: x[0], list(filter(lambda x : x[0] % 2 == 0, _list_tuple))))
# [2, 4]

Reduce

1
Reduce(function_to_apply, list_of_inputs)
1
2
3
4
5
6
product = 1
list = [1, 2, 3, 4]
for num in list:
product = product * num

# product = 24
1
2
3
produce = reduce((lambda x, y: x * y), [1, 2, 3, 4])

# product = 24

Iterator

container implements method next() and __iter__

1
2
3
4
5
6
7
8
9
10
11
class CountdownIterator(Object):
def __init__(self, init_number):
self.__init_number = init_number

def next(self):
if self.__init_number == 0:
raise StopIteration
self.__init_number -= 1

def __iter__(self):
return self
1
2
3
for element in CountdownIterator(2):
print(element)
# 2 1 0

如何判断container是否是可迭代的

  • 通过类型转化
1
2
3
4
5
6
7

def is_iterable(param):
try:
iter(param)
return True
except TypeError:
return False
  • 通过调用 isinstance(object, iterable) 方法判断
1
2
3
from collections.abc import Iterable
isinstance([], Iterable)
# True

Generator

Generator 可以被理解为懒汉加载版的Iterator,在语法上通常与关键词”yield”共同出现。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def index_generator(L, target):
"""

Args:
L: the given list
target: the target element

Yields: the index list of target element in the given list

"""
for i, num in enumerate(L):
if num == target:
yield i

print(list(index_generator([1, 6, 2, 4, 5, 2, 8, 6, 3, 2], 10)))

特别需要注意的是在Google的code style guide中要求在docstring 中将Returns改为Yields Google Python Style Guide - generators

正如style guide中所描述的generator优势,由于在每次调用next之前局部变量和control flow 状态都会保留,这使得无需像iterator和其他函数一样提前创建一整个list的数据, 同时还使得代码变得简洁

generator 的另一种简洁写法生成器表达式(generator expression):

1
generator_for_loop = (i for i in range(100000))

Generator to list

1
list((i for i in range(100000)))

Decorator

函数在Python中是一等公民(first-class citizen),这里Python中的函数有下面几个性质:

函数式性质

  • 函数可以作为对象赋值给变量
1
2
3
4
5
6
def format_string(message):
print("message : {}".format(message))

print_message = format_string

print_message('hello')
  • 函数可以作为参数传入另一个函数
1
2
3
4
5
6
7
def get_format_string(message):
return 'message: {}'.format(message)

def call_print(func, message)
return print(func(message))

call_print(get_format_string, 'hello')
  • 函数内可以定义函数,即函数的嵌套
1
2
3
4
5
6
def func(message):
def get_format_string():
print('message : {}'.format(message))
return get_format_string()

func('hello')
  • 函数的返回值也可以是函数对象(closure)
1
2
3
4
5
6
7
8
9
def func_closure():
def get_format_string(message):
print('message : {}'.format(message))
return get_format_string

print_format = func_closure()

print_format('hello')
print_format('world')

Decorator 本质上是什么

Method decorator

我们常常说的Decorator是以下面的这种形式出现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def mydecorator(func):
def _decorator_wrapper(*args, **kwargs):
# do something
print('my decorator')
func(*args, **kwargs)
return _decorator_wrapper


@mydecorator
def greet_func():
print('hello')


greet_func()

这里的mydecorator()函数就是一个装饰器,这个函数在没有改变原函数greet_func()内部结构的前提下,改变了外部行为表现。
实际山@mydecorator是一个语法糖,它在语法去糖之后是这样的greet_func = mydecorator(greet_fun).

同时Decorator也支持自定义的参数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

def mydecorator(base_str, repeat_num):
def _decorator_wrapper(func):
def __decorator_wrapper(*xargs, **kwargs):
for i in range(repeat_num):
print(base_str)
func(*xargs, **kwargs)
return __decorator_wrapper
return _decorator_wrapper


@mydecorator(base_str='hello from mydecorator', repeat_num=2)
def greet_func():
print('hello')


greet_func()

当然被包装的函数也可能会有函数返回值,所以更泛化(generic)的decorator的代码模版(pattern)会是:

1
2
3
4
5
6
7
8
9
def mydecorator(arg1, arg2):
def _decorator_wrapper(func):
def __decorator_wrapper(*xargs, **kwargs):
do_something_befor(arg1, arg2, *xargs, **kwargs)
result = func(*xargs, **kwargs)
do_something_after(result, arg1, arg2, *xargs, **kwargs)
return result
return __decorator_wrapper
return _decorator_wrapper

对于method decorator 其实有一个细节需要大家注意,我们回到语法去糖之后的函数表达式greet_func = mydecorator(greet_fun), 其实在经过mydecorator处理后的函数已经不是原先传入的函数对象了,以下是一段验证程序:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
def mydecorator(func):
def _decorator_wrapper(*args, **kwargs):
# do something
print('my decorator')
func(*args, **kwargs)
return _decorator_wrapper


def greet_func():
print('hello')


print(greet_func.__name__)
print(greet_func.__doc__)
greet_func = mydecorator(greet_func)

print(greet_func.__name__)
print(greet_func.__doc__)

输出

1
2
3
4
5
6
7
8
9
greet_func
greet function
:return:

_decorator_wrapper
Decorator wrapper
:param args:
:param kwargs:
:return:

可见经过decorator处理后的函数其函数的元信息(特别是 __name____doc__)是被修改了

那么这会有什么问题么?
主要会有两个问题:

  1. docstring 会引用wrapper函数的docstring
  2. decorator 在应用在多个函数的时候,如果程序出现问题,需要debug的时候,堆栈的信息并不能准确的找出是那个函数出了问题

那么如何解决呢?
其实也比较简洁在decorator的wrapper函数上用functool.warp(func)进行修饰就可以了:

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


def mydecorator(func):
@functools.wraps(func)
def _decorator_wrapper(*args, **kwargs):
# do something
print('my decorator')
func(*args, **kwargs)
return _decorator_wrapper


def greet_func():
print('hello')


print(greet_func.__name__)
greet_func = mydecorator(greet_func)

print(greet_func.__name__)

输出

1
2
greet_func
greet_func
Class Decorator

事实上,decorator 也可以是一个类

1
2
3
4
5
6
7
8
9
class DecoratorAsClass(Object):
def __init__(self, func):
self.func = func

def __call__(self, *xargs, **kwargs):
# do_something_befor(*xargs, **kwargs)
result = self.fun(*xargs, **kwargs)
# do_soming_after(result, *xargs, **kwargs)
return result
1
2
3
4

@DecoratorAsClass
def greet_func():
print("hello")

如果Class Decorator需要参数化修饰,比如下面的这种用法:

1
2
3
@DecoratorAsClass(arg1="Number:")
def greet_func():
print("hello")

直接在原有的类上进行参数话修饰是会提示参数匹配错误typeError: __init__() takes at least 2 arguments”的, 可选的解决办法是有两个:

  1. 使用函数式decorator重写(推荐)
  2. 如果一定要使用class decorator,可以使用function 做一层包装

第一种方式就不多说了, 下面的代码段是展示第二种解决办法的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class _DecoratorAsClass(Object):
def __init__(self, func, arg1='default:'):
self.func = func

def __call__(self, *xargs, **kwargs):
# do_something_befor(*xargs, **kwargs)
result = self.fun(*xargs, **kwargs)
# do_soming_after(result, *xargs, **kwargs)
return result

def DecoratorAsClass(func, arg1 = "default:"):
if func:
return _DecoratorAsClass(func)
else:
def wrapper(func):
return _DecoratorAsClass(func, arg1)
return wrapper

stackoverflow - Python class decorator arguments

另一个紧接着的相关的小问题就是,既然class 可以作为decorator, 那么是所有的class都可以么? 如果不是,那么怎么判断哪些class 可以?

调用函数 callable(object) 进行判断,事实上内部实现是判断目标对象是否具有__call__属性, 换句话说就是内部调用hasattr(obj, '__call__')进行判断。

Decorator的应用

  • 参数检查(argument checking)
  • 缓存(caching)
  • 代理(proxy)
  • context provider
Decorator的另类用法

我们先回头看下method decorator的标准实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def mydecorator(func):
def _decorator_wrapper(*args, **kwargs):
# do something
print('my decorator')
func(*args, **kwargs)
return _decorator_wrapper


@mydecorator
def greet_func():
print('hello')


greet_func()

这里如果在_decorator_wrapper中不再调用方法,那这是不是又起到了跳过函数体执行的效果呢?事实上Python unnittest 单元测试框架中的 @skip decorator就是这样的实现原理,下面是这部分的源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
def skip(reason):
"""
Unconditionally skip a test.
"""
def decorator(test_item):
if not isinstance(test_item, (type, types.ClassType)):
@functools.wraps(test_item)
def skip_wrapper(*args, **kwargs):
raise SkipTest(reason)
test_item = skip_wrapper

test_item.__unittest_skip__ = True
test_item.__unittest_skip_why__ = reason
return test_item
return decorator

上述代码中对于decorator传入的待测函数 test_item 并没有执行函数调用的操作,只是用一个空壳函数 skip_wrapper做了一个函数指针的替换, 从而达到了跳过指定测试方法的目的。

Context manager

// TODO

mataclass

什么是metaclass

在很多书中翻译为元类,但这里meta翻译为元类并不合适,容易误解为所有累的基类。事实上meta在这里的语境是超越的意思,目前在业内还没有合适的翻译,所以下面依旧称作metaclass。
用简单的一句话来解释metaclass就是 “metaclass是class的class”,好像还是比较绕,我们都知道 对象是class的实例, 那么class是谁的实例呢?在python中是type,我们可以把它看作是metaclass的“基类”。

如何自定义和使用一个metaclass

在Python3中使用类似类的继承的方式定义一个metaclass

1
2
class CustomizedMetaClass(type):
pass

使用下面的方式为一个类指定metaclass:

1
2
class CustomizedClass(metaclass=CustomizedMetaClass):
pass

metaclass 对应的生命周期方法hook

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

class Metaclass(type):
def __new__(metacls, name, bases, namespace):
print(metacls, "__new__ called")
return super().__new__(metacls, name, bases, namespace)

@classmethod
def __prepare__(metacls, name, bases, **kwargs):
print(metacls, "__prepare__ called")
return super().__prepare__(name, bases, **kwargs)

def __init__(cls, name, bases, namespace, **kwargs):
print(cls, "__init__ called")
super().__init__(name, bases, namespace)

def __call__(cls, *args, **kwargs):
print(cls, "__call__ called")
return super().__call__(*args, **kwargs)

class MetaRevealingClass(metaclass=Metaclass):
def __new__(cls, *args, **kwargs):
print(cls, "__new__ called")
return super().__new__(cls)

def __init__(self):
print(self, "__init__ called")
super().__init__()
1
2
3
<class 'Metaclass'> __prepare__ called
<class 'Metaclass'> __new__ called
<class 'MetaRevealingClass'> __init__ called

>>> instance = MetaRevealingClass()

1
2
3
<class 'MetaRevealingClass'> __call__ called
<class 'MetaRevealingClass'> __new__ called
<MetaRevealingClass object at 0x10ff28978> __init__ called
类指定自定义的metaclass后生命周期方法调用顺序
  1. metaclass __prepare__
  2. metaclass __new__
  3. class __new__
实例化类后生命周期方法调用顺序
  1. class __call__
  2. class __new__
  3. instance __init__

metaclass 在实际项目中的应用

由于metaclass具有强大的修改类型的能力,同时就意味着如果使用不当就会造成类型继承体系变混乱的糟糕结果,所以在实际的项目中需要谨慎使用。
目前已知的使用场景是在framework的开发中会有部分的应用场景。
常见的有:

  • DSL
  • ORM
metaclass 在pyyaml framework中的使用

pyyaml是一个非常流行的YAML framework, 在定义好对应的实体类后只要继承YAMLObject类后就可以使用dump()和反向的load()进行YAML的反序列化和序列化操作。
我们如果不去看framework的内部实现,猜想内部一定会存在一个dictionary 用来进行{‘yaml_tag’: YAMLObject}的映射,那么具体是怎么做的呢?我们查看下面源码。

pyyaml/lib3/yaml/__init__.py

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
class YAMLObjectMetaclass(type):
"""
The metaclass for YAMLObject.
"""
def __init__(cls, name, bases, kwds):
super(YAMLObjectMetaclass, cls).__init__(name, bases, kwds)
if 'yaml_tag' in kwds and kwds['yaml_tag'] is not None:
if isinstance(cls.yaml_loader, list):
for loader in cls.yaml_loader:
loader.add_constructor(cls.yaml_tag, cls.from_yaml)
else:
cls.yaml_loader.add_constructor(cls.yaml_tag, cls.from_yaml)

cls.yaml_dumper.add_representer(cls, cls.to_yaml)

class YAMLObject(metaclass=YAMLObjectMetaclass):
"""
An object that can dump itself to a YAML stream
and load itself from a YAML stream.
"""

__slots__ = () # no direct instantiation, so allow immutable subclasses

yaml_loader = [Loader, FullLoader, UnsafeLoader]
yaml_dumper = Dumper

yaml_tag = None
yaml_flow_style = None

@classmethod
def from_yaml(cls, loader, node):
"""
Convert a representation node to a Python object.
"""
return loader.construct_yaml_object(node, cls)

@classmethod
def to_yaml(cls, dumper, data):
"""
Convert a Python object to a representation node.
"""
return dumper.represent_yaml_object(cls.yaml_tag, data, cls,
flow_style=cls.yaml_flow_style)

Github - pyyaml/lib3/yaml/__init__.py

metaclass 在Django ORM中的使用

Python – how to implement ORM with metaclasses

References

文章目录
  1. 1. 高级语法特性部分
    1. 1.1. List comprehension
    2. 1.2. Dict comprehension
    3. 1.3. Set comprehension
    4. 1.4. Functional funtion
      1. 1.4.1. map()
      2. 1.4.2. filter()
      3. 1.4.3. Reduce
    5. 1.5. Iterator
      1. 1.5.1. 如何判断container是否是可迭代的
    6. 1.6. Generator
      1. 1.6.1. Generator to list
    7. 1.7. Decorator
      1. 1.7.1. 函数式性质
      2. 1.7.2. Decorator 本质上是什么
        1. 1.7.2.1. Method decorator
        2. 1.7.2.2. Class Decorator
      3. 1.7.3. Decorator的应用
        1. 1.7.3.1. Decorator的另类用法
    8. 1.8. Context manager
    9. 1.9. mataclass
      1. 1.9.1. 什么是metaclass
      2. 1.9.2. 如何自定义和使用一个metaclass
      3. 1.9.3. metaclass 对应的生命周期方法hook
        1. 1.9.3.1. 类指定自定义的metaclass后生命周期方法调用顺序
        2. 1.9.3.2. 实例化类后生命周期方法调用顺序
      4. 1.9.4. metaclass 在实际项目中的应用
        1. 1.9.4.1. metaclass 在pyyaml framework中的使用
      5. 1.9.5. metaclass 在Django ORM中的使用
  2. 2. References