前言 前几天董大 @董伟明 的爱派森上线了,还发了一套 PPT 和视频,PPT 共有75页干货,讲了很多进阶的用法,也包括很多坑,非常适合刚入门的同学,免得再踩到。
PPT和视频的传送门
这里我提取部分非常规的,也就是大部分教程里没有的,加入自己的理解,同时语法改为 Python 3,也争取延伸一些,就当是做笔记了。
设置全局变量 有时候设置全局变量的需求并不是直接赋值,而是想从某个数据结构里引用生成,可以用下面这两种方法,推荐第二种,golbals()
支持字典用法很方便。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> d = {'a' : 1 , 'b' :2 }>>> >>> for k, v in d.items():... exec "{}={}" .format (k, v) ...>>> >>> globals ().update(d)>>> a, b (1 , 2 )>>> 'a' , 'b' ('a' , 'b' )>>> globals ()['a' ] = 'b' >>> a'b'
字符串格式化 用 format
方法可以支持很多种格式,这里就不多说了,可以看官方文档。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> "{key}={value}" .format (key="a" , value=10 ) 'a=10' >>> "[{0:<10}], [{0:^10}], [{0:*>10}]" .format ("a" ) '[a ], [ a ], [*********a]' >>> "{0.platform}" .format (sys) 'darwin' >>> "{0[a]}" .format (dict (a=10 , b=20 )) '10' >>> "{0[5]}" .format (range (10 )) '5' >>> "{0!r:20}" .format ("Hello" )"'Hello' " >>> "{0!s:20}" .format ("Hello" )'Hello ' >>> "Today is: {0:%a %b %d %H:%M:%S %Y}" .format (datetime.now())'Today is: Mon Mar 31 23:59:34 2014'
列表去重 这里讲了两种方法,正常情况下 set
是更好的选择;
(注:这里董大视频讲解有误,方法一单位是1.1微妙,是慢于956纳秒,我也自己测试了,确实两种情况都不如 set
)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 >>> l = [1 , 2 , 2 , 3 , 3 , 3 ]>>> list ({}.fromkeys(l).keys()) [1 , 2 , 3 ] >>> list (set (l)) [1 , 2 , 3 ] In [2 ]: %timeit list (set (l))1000000 loops, best of 3 : 956 ns per loop In [3 ]: %timeit list ({}.fromkeys(l).keys())1000000 loops, best of 3 : 1.1 µs per loop In [4 ]: l = [random.randint(1 , 50 ) for i in range (10000 )] In [5 ]: %timeit list (set (l))1000 loops, best of 3 : 271 µs per loop In [6 ]: %timeit {}.fromkeys(l).keys()1000 loops, best of 3 : 310 µs per loop
操作字典 字典是 Python 很常用的数据结构,各种函数和方法要掌握。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> dict ((["a" , 1 ], ["b" , 2 ])) {'a' : 1 , 'b' : 2 }>>> dict (zip ("ab" , range (2 ))) {'a' : 0 , 'b' : 1 }>>> dict (map (None , "abc" , range (2 ))) {'a' : 0 , 'c' : None , 'b' : 1 }>>> dict .fromkeys("abc" , 1 ) {'a' : 1 , 'c' : 1 , 'b' : 1 }>>> {k:v for k, v in zip ("abc" , range (3 ))} {'a' : 0 , 'c' : 2 , 'b' : 1 }>>> d = {"a" :1 , "b" :2 }>>> d.setdefault("a" , 100 ) >>> d.setdefault("c" , 200 ) >>> d {'a' : 1 , 'c' : 200 , 'b' : 2 }
对字典进行逻辑操作 只能先转成键值对列表再进行操作,然后转回去;
(注:这里原文是 Python 2 中 viewitems
方法,已经被 items
替代)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 >>> d1 = dict (a = 1 , b = 2 )>>> d2 = dict (b = 2 , c = 3 )>>> d1 & d2 Traceback (most recent call last): File "<stdin>" , line 1 , in <module> TypeError: unsupported operand type (s) for &: 'dict' and 'dict' >>> v1 = d1.items()>>> v2 = d2.items()>>> dict (v1 & v2) {'b' : 2 }>>> dict (v1 | v2) {'a' : 1 , 'b' : 2 , 'c' : 3 }>>> dict (v1 - v2) {'a' : 1 }>>> dict (v1 ^ v2) {'a' : 1 , 'c' : 3 }>>> ('a' , 1 ) in v1 True
vars vars()
的作用是返回对象的属性和属性值的字典对象,如果没有参数,就打印当前调用位置的属性和属性值,类似 locals()
。
1 2 3 4 >>> vars () is locals ()True >>> vars (sys) is sys.__dict__ True
实现上下文管理类 可以用来自动关闭 DB 连接
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> import pymongo>>> class Operation (object ):... def __init__ (self, database, ... host='localhost' , port=27017 ):... self._db = pymongo.MongoClient(... host, port)[database]... def __enter__ (self ):... return self._db... def __exit__ (self, exc_type, exc_val, exc_tb ):... self._db.connection.disconnect() ...>>> with Operation(database='test' ) as db:... print db.test.find_one()
contextlib 这个模块主要包含一个装饰器 contextmanager
,作用是可以省去像上面那样改写魔术魔法,也能实现同样的类
1 2 3 4 5 6 7 8 9 10 >>> @contextlib.contextmanager... def operation (database, host='localhost' , port=27017 ):... db = pymongo.MongoClient(host, port)[database]... yield db... db.connection.disconnect()... >>> import pymongo>>> with operation('test' ) as db:... print (db.test.find_one())
包的构建 如果包里有一些模块不想被 import *
这样引用,可以用 __all__
把允许被引用的放进去;
__all__ = ["add", "x"]
某些时候,包内的文件太多,需要分类存放到多个目录中,但⼜不想拆分成新的包或子包。这么做是允许的, 只要在 __init__.py
中⽤ __path__
指定所有子目录的全路径即可 (子目录可放在包外),下面这段代码可以自动指定子目录。
1 2 3 4 from os.path import abspath, join subdirs = lambda *dirs: [abspath( join(__path__[0 ], sub)) for sub in dirs] __path__ = subdirs("a" , "b" )
slots 限制给类实例绑定属性,大量属性时减少内存占用
1 2 3 4 5 6 7 8 9 10 11 12 13 >>> class User (object ):... __slots__ = ("name" , "age" )... def __init__ (self, name, age ):... self.name = name... self.age = age ...>>> u = User("Dong" , 28 )>>> hasattr (u, "__dict__" )False >>> u.title = "xxx" Traceback (most recent call last): File "<stdin>" , line 1 , in <module> AttributeError: 'User' object has no attribute 'title'
@cached_property 主要实现的功能是,被装饰的类实例方法在第一次调用后,会把值缓存下来,下次再调用会直接从 __dict__
取结果,避免了多次计算;你可以参考下面的代码实现这个装饰器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> class cached_property (object ):... ... def __init__ (self, func, name=None , doc=None ):... self.__name__ = name or func.__name__... self.__module__ = func.__module__... self.__doc__ = doc or func.__doc__... self.func = func... def __get__ (self, obj, type =None ):... if obj is None :... return self... value = obj.__dict__.get(self.__name__, _missing)... if value is _missing:... value = self.func(obj)... obj.__dict__[self.__name__] = value... return value
元类里提前定义类方法 这样可以像定义基类一样,提前给类定义一些方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> class HelloMeta (type ):... def __new__ (cls, name, bases, attrs ):... def __init__ (cls, func ):... cls.func = func... def hello (cls ):... print 'hello world' ... t = type .__new__(cls, name, bases, attrs)... t.__init__ = __init__... t.hello = hello... return t ...>>> class Hello (object ):... __metaclass__ = HelloMeta ...>>> h = Hello(lambda x: x+1 )>>> h.hello() hello world
开发陷阱(一):可变的默认参数 把临时变量作为默认参数里确实是不错的办法,但要警惕默认参数必须指向一个不可变类型,否则会踩到下面的坑
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 >>> def append_to (element, to=[] ):... to.append(element)... return to... >>> my_list = append_to(12 )>>> my_list [12 ]>>> my_other_list = append_to(42 )>>> my_other_list [12 , 42 ] >>> def append_to (element, to=None ):... if to is None :... to = []... to.append(element)... return to
开发陷阱(二):闭包变量绑定 看懂这个坑,需要先理解闭包,推荐一篇文章;
刘志军:一步一步教你认识Python闭包 下面我更换了 PPT 里的代码,坑看得更清楚一些。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 >>> def create ():... a = []... for i in range (4 ):... def demo (x ):... return x*i... a.append(demo)... return a... >>> for demo in create():... print demo(2 )... 6 6 6 6
为什么会这样? 原因是:因为变量 i 是在闭包的作用域(demo 函数的外层作用域),而 Python 的闭包是迟绑定 ,这意味着闭包中用到的变量的值,是在内部函数被调用时查询得到的;
也就是说,create()
生成实例时,内部的 for
循环开始,使变量 i 的最终变成了 3,当随后循环调用闭包 demo(2)
时,在内部调用的 i 实际都是 3,要解决这个问题,可以如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 >>> def create ():... a = []... for i in range (4 ):... def demo (x, i=i ): ... return x*i... a.append(demo)... return a... >>> >>> from functools import partial>>> from operator import mul>>> def create_multipliers ():... return [partial(mul, i) for i in range (5 )]... >>> >>> def create_multipliers ():... return (lambda x : i * x for i in range (4 ))
中间还有一些其他内建模块的用法,廖雪峰的教程 里都有,就不赘述