本文最后更新于 2024年7月31日 上午
1. Python解释器 Python语言从规范到解释器都是开源,确实存在多种Python解释器。
CPython、IPython、PyPy、Jython、IronPython
2. Print()遇到逗号“,”会输出一个空格 3. Python提供ord()函数获取字符的整数表示,chr()函数把编码转换为对应的字符 1 2 3 4 5 6 7 8 >>> ord ('A' )65 >>> ord ('中' )20013 >>> chr (66 )'B' >>> chr (25991 )'文'
1 2 >>> 'Hello, {0}, 成绩提升了 {1:.1f}%' .format ('小明' , 17.125 )'Hello, 小明, 成绩提升了 17.1%'
5. list.pop() 删除末尾一个 list.pop(i)删除指定位置 6. dict.pop(key)删除一个key,对应的value也会从dict中删除 7. set 可以看成数学意义上的无序和无重复元素的集合, 通过remove(key)方法可以删除元素 !不可以放入可变对象
8. 定义默认参数要牢记一点 默认参数必须指向不变对象 !
9. 和关键字参数*kw不同,命名关键字参数需要一个特殊分隔符 ,*后面的参数被视为命名关键字参数。 1 2 3 4 def person(name, age, *, city, job): print (name, age, city, job) >>> person('Jack' , 24, city ='Beijing' , job ='Engineer' ) Jack 24 Beijing Engineer
10. 可变参数和关键字参数 可变参数既可以直接传入:
又可以先组装list或tuple,再通过*args传入:
关键字参数既可以直接传入:
又可以先组装dict,再通过**kw传入:
1 func (**{'a' : 1 , 'b' : 2 })
11. 创建一个generator
第一种方法很简单,只要把一个列表生成式的[]改成()
1 2 3 >>> g = (x * x for x in range (10 )) >>> g <generator object <genexpr> at 0x1022ef630>
如果一个函数定义中包含yield关键字,那么这个函数就不再是一个普通函数,而是一个generator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def fib (max ): n, a, b = 0 , 0 , 1 while n < max : yield b a, b = b, a + b n = n + 1 return 'done' >>> f = fib(6 )>>> f <generator object fib at 0x104feaaa0 >def triangles (): L = [1 ] while True : yield L L = [1 ] + [L[i] +L[i+1 ] for i in range (len (L) - 1 )] + [1 ]
12. Iterable和Iterator不同 list、dict、str等是Iterable但不是Iterator,不过可以通过iter()函数获得一个Iterator对象
13. map()、reduce()、filter、sorted函数
map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator 返回
1 2 >>> list (map (str, [1, 2, 3, 4, 5, 6, 7, 8, 9] ))['1' , '2' , '3' , '4' , '5' , '6' , '7' , '8' , '9' ]
reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:
1 2 3 4 5 6 7 8 9 10 reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)from functools import reduce DIGITS = {'0' : 0 , '1' : 1 , '2' : 2 , '3' : 3 , '4' : 4 , '5' : 5 , '6' : 6 , '7' : 7 , '8' : 8 , '9' : 9 }def char2num (s ): return DIGITS[s]def str2int (s ): return reduce(lambda x, y: x * 10 + y, map (char2num, s))
filter()用于过滤序列,接收一个函数和一个序列。把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。
1 2 3 4 def is_odd (n ): return n % 2 == 1 list (filter (is_odd, [1 , 2 , 4 , 5 , 6 , 9 , 10 , 15 ]))
sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:
1 2 >>> sorted ([36 , 5 , -12 , 9 , -21 ], key=abs ) [5, 9, -12, -21, 36]
14. 闭包 当一个函数返回了一个函数后,其内部的局部变量还被新函数引用。返回闭包时牢记一点:返回函数不要引用任何循环变量,或者后续会发生变化的变量。
15. lambda
16. 装饰器 Decorator functools.wraps 的作用是将原函数对象的指定属性复制给包装函数对象, 默认有 module、name、doc
1 2 3 4 5 6 7 8 import functools def log(func): @functools.wraps(func) def wrapper (*args, **kw): print('call %s():' % func.__name__) return func(*args, **kw) return wrapper
同时支持@log和@log(‘something’)的装饰器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 def log (param ): if not callable (param): text = param def decorator (func ): @functools.wraps(func ) def warps (*args, **kwargs ): print ('{} {}' .format (func.__name__, text)) return func(*args, **kwargs) return warps return decorator else : func = param @functools.wraps(func ) def warps (*args, **kwargs ): print ('{}' .format (func.__name__)) return func(*args, **kwargs) return warps
17. Class私有变量 不能直接访问__name是因为Python解释器对外把__name变量改成了_Student__name
18. 继承-》多态 当子类和父类都存在相同的run()方法时,我们说,子类的run()覆盖了父类的run(),在代码运行的时候,总是会调用子类的run()。这样,我们就获得了继承的另一个好处:多态。
19. 实例属性和类属性 千万不要对实例属性和类属性使用相同的名字,因为相同名称的实例属性将屏蔽掉类属性,但是当你删除实例属性后,再使用相同的名称,访问到的将是类属性
20. 使用__slots__ 但是,如果我们想要限制实例的属性怎么办?比如,只允许对Student实例添加name和age属性。
为了达到限制的目的,Python允许在定义class的时候,定义一个特殊的__slots__变量,来限制该class实例能添加的属性:
1 2 class Student (object ): __slots__ = ('name ', 'age ') # 用tuple 定义允许绑定的属性名称
21. 使用@property 和@score.setter Python内置的@property装饰器就是负责把一个方法变成属性调用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 class Student (object): @property def score (self ): return self ._score @score .setter def score (self , value ): if not isinstance(value, int): raise ValueError ('score must be an integer!' ) if value < 0 or value > 100 : raise ValueError ('score must between 0 ~ 100!' ) self ._score = value
把一个getter方法变成属性,只需要加上@property就可以了,此时,@property本身又创建了另一个装饰器@score.setter,负责把一个setter方法变成属性赋值,于是,我们就拥有一个可控的属性操作:
1 2 3 4 5 6 7 8 >>> s = Student() >>> s.score = 60 >>> s.score 60>>> s.score = 9999 Traceback (most recent call last): ... ValueError: score must between 0 ~ 100!
22. 多重继承和MixIn 在设计类的继承关系时,通常,主线都是单一继承下来的,例如,Ostrich继承自Bird。但是,如果需要“混入”额外的功能,通过多重继承就可以实现,比如,让Ostrich除了继承自Bird外,再同时继承Runnable。这种设计通常称之为MixIn。
1 2 class Dog (Mammal, RunnableMixIn , CarnivorousMixIn ): pass
23. 特殊用途的函数 1 2 3 4 5 6 7 8 9 10 11 12 13 14 __len__ ()方法我们也知道是为了能让class作用于len ()函数。__str__ ()定制化打印object __repr__ ()与__str__ ()区别在于:__str__ ()返回用户看到的字符串,而__repr__ ()返回程序开发者看到的字符串,也就是说,__repr__ ()是为调试服务的。 class Student (object): def __init__ (self, name): self.name = name def __str__ (self): return 'Student object (name=%s)' % self.name __repr__ = __str__ 要表现得像list那样按照下标取出元素,需要实现__getitem__ ()方法 如果一个类想被用于for ... in循环,类似list或tuple那样,就必须实现一个__iter__ ()方法,该方法返回一个迭代对象,然后,Python的for循环就会不断调用该迭代对象的__next__ ()方法拿到循环的下一个值,直到遇到StopIteration错误时退出循环。 写一个__getattr__ ()方法,动态返回一个属性
24. 错误处理 try…except…finally… 就可以用try来运行这段代码,如果执行出错,则后续代码不会继续执行,而是直接跳转至错误处理代码,即except语句块,执行完except后,如果有finally语句块,则执行finally语句块,至此,执行完毕。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 def foo (s ): n = int (s) if n==0 : raise ValueError('invalid value: %s' % s) return 10 / ndef bar (): try : foo('0' ) except ValueError as e: print ('ValueError!' ) raise bar()
在bar()函数中,我们明明已经捕获了错误,但是,打印一个ValueError!后,又把错误通过raise语句抛出去了,这不有病么?
其实这种错误处理方式不但没病,而且相当常见。捕获错误目的只是记录一下,便于后续追踪。但是,由于当前函数不知道应该怎么处理该错误,所以,最恰当的方式是继续往上抛,让顶层调用者去处理。好比一个员工处理不了一个问题时,就把问题抛给他的老板,如果他的老板也处理不了,就一直往上抛,最终会抛给CEO去处理。
25. 文件读写 1 2 with open ('/path/to/file' , 'r' ) as f: print(f.read ())
代码更佳简洁,并且不必调用f.close()方法。
26. 序列化 Python提供了pickle 模块来实现序列化。
1 2 3 4 >>> import pickle>>> d = dict (name='Bob' , age=20 , score=88 ) >>> pickle.dumps(d) b'\x80\x03}q\x00(X\x03\x00\x00\x00ageq\x01K\x14X\x05\x00\x00\x00scoreq\x02KXX\x04\x00\x00\x00nameq\x03X\x03\x00\x00\x00Bobq\x04u.'
python的dumps也可以进行序列化,对于非dict可指定转换函数进行序列化。
1 2 >>> print (json.dumps(s, default =student2dict) ) {"age" : 20 , "name" : "Bob" , "score" : 88 }
27. 线程是最小的执行单元,而进程由至少一个线程组成 28. 进程线程 multiprocessing 模块提供了一个Process类来代表一个进程对象,下面的例子演示了启动一个子进程并等待其结束:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 from multiprocessing import Process import os def run_proc(name): print ('Run child process %s (%s)...' % (name, os.getpid()))if __name__ =='__main__': print ('Parent process %s.' % os.getpid()) p = Process(target =run_proc, args=('test' ,)) print ('Child process will start.' ) p.start() p.join() print ('Child process end.' ) 执行结果如下: Parent process 928. Process will start.Run child process test (929).. . Process end.
如果要启动大量的子进程,可以用进程池的方式批量创建子进程:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 from multiprocessing import Pool import os , time , random def long_time_task(name): print ('Run task %s (%s)...' % (name, os .getpid())) start = time .time () time .sleep(random .random () * 3 ) end = time .time () print ('Task %s runs %0.2f seconds.' % (name, (end - start)))if __name__=='__main__' : print ('Parent process %s.' % os .getpid()) p = Pool(4 ) for i in range(5 ): p.apply_async(long_time_task, args=(i,)) print ('Waiting for all subprocesses done...' ) p.close () p.join() print ('All subprocesses done.' )
进程间通信 Process之间肯定是需要通信的,操作系统提供了很多机制来实现进程间的通信。Python的multiprocessing模块包装了底层的机制,提供了Queue、Pipes等多种方式来交换数据。
多线程 线程锁 Lock()进行线程同步 当多个线程同时执行lock.acquire()时,只有一个线程能成功地获取锁,然后继续执行代码,其他线程就继续等待直到获得锁为止。
Python的线程虽然是真正的线程,但解释器执行代码时,有一个GIL锁:Global Interpreter Lock,任何Python线程执行前,必须先获得GIL锁,然后,每执行100条字节码,解释器就自动释放GIL锁,让别的线程有机会执行。这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以,多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
ThreadLocal 一个ThreadLocal变量虽然是全局变量,但每个线程都只能读写自己线程的独立副本,互不干扰。ThreadLocal解决了参数在一个线程中各个函数之间互相传递的问题。
29. 多进程VS多线程 多进程模式最大的优点就是稳定性高,因为一个子进程崩溃了,不会影响主进程和其他子进程。 多进程模式的缺点是创建进程的代价大,如果有几千个进程同时运行,操作系统连调度都会成问题。 多线程模式通常比多进程快一点。
要最高效地利用CPU,计算密集型任务同时进行的数量应当等于CPU的核心数。 对于IO密集型任务,任务越多,CPU效率越高,但也有一个限度。
30. 协程 31. 正则表达式