高级语法特性部分 List comprehension 1 2 3 [i for i in range (10 ) if i % 2 == 0 ]
Dict comprehension 1 2 dict_square = {i: i**2 for i in range (3 )}
Set comprehension 1 2 set_comprehension_expres = {i**2 for i in [1 , 1 , 2 ]}
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))
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))))
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
1 2 3 produce = reduce((lambda x, y: x * y), [1 , 2 , 3 , 4 ])
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)
如何判断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 Iterableisinstance ([], Iterable)
Generator Generator 可以被理解为懒汉加载版的Iterator,在语法上通常与关键词”yield”共同出现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 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' )
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 ): 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 ): 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__
)是被修改了
那么这会有什么问题么? 主要会有两个问题:
docstring 会引用wrapper函数的docstring
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 functoolsdef mydecorator (func ): @functools.wraps(func ) def _decorator_wrapper (*args, **kwargs ): 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__)
输出
Class Decorator 事实上,decorator 也可以是一个类
1 2 3 4 5 6 7 8 9 10 class DecoratorAsClass (Object ): def __init__ (self, func ): self.func = func def __call__ (self, *xargs, **kwargs ): result = self.fun(*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
”的, 可选的解决办法是有两个:
使用函数式decorator重写(推荐)
如果一定要使用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 ): result = self.fun(*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 ): 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 在很多书中翻译为元类,但这里meta翻译为元类并不合适,容易误解为所有累的基类。事实上meta在这里的语境是超越的意思,目前在业内还没有合适的翻译,所以下面依旧称作metaclass。 用简单的一句话来解释metaclass就是 “metaclass是class的class”,好像还是比较绕,我们都知道 对象是class的实例, 那么class是谁的实例呢?在python中是type
,我们可以把它看作是metaclass的“基类”。
在Python3中使用类似类的继承的方式定义一个metaclass
1 2 class CustomizedMetaClass (type ): pass
使用下面的方式为一个类指定metaclass:
1 2 class CustomizedClass (metaclass=CustomizedMetaClass): pass
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 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 __prepare__
metaclass __new__
class __new__
实例化类后生命周期方法调用顺序
class __call__
class __new__
instance __init__
由于metaclass具有强大的修改类型的能力,同时就意味着如果使用不当就会造成类型继承体系变混乱的糟糕结果,所以在实际的项目中需要谨慎使用。 目前已知的使用场景是在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 44 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__ = () 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
Python – how to implement ORM with metaclasses
References