1.元类:动态语言和静态语言最大的不同,就是函数和类的定义,不是编译时定义的,而是运行时动态创建的,不是定义死了,而是可以随时随地添加的
type():查看一个类型或变量的类型又可以创建出新的类型
class Hello(object): def hello(self, name='world'): print('Hello, %s.' % name) h = Hello() h.hello()Hello, world. print(type(Hello))#Hello是一个class,它的类型就是type print(type(h)) #h是一个实例,它的类型就是class Hello
def fn(self, name='world'): # 先定义函数 print('Hello, %s.' % name) Hello = type('Hello', (object,), dict(hello=fn)) # 创建Hello class h = Hello() h.hello()Hello, world.print(type(Hello))print(type(h))
type()创建class要传入三个参数:1.class的名称,2.继承的父类集合(只有一个父类时,别忘了tuple的单元素写法即后面加逗号),3.class的方法名称与函数绑定(列中绑定fn)
大部分情况不要type()方法创立class,type()说明了动态的特点
metaclass(元类):控制类的创建行为
先定义metaclass,就可以创建类,最后创建实例;metaclass允许创建类或者修改类。可以把类看成是metaclass创建出来的“实例”
难,不懂也基本不要,略
2.错误处理
try:try...except...finally...与Java错误处理类似
try: print('try...') r = 10 / 0 print('result:', r)except ZeroDivisionError as e: print('except:', e)finally: print('finally...')print('END')
调用栈:错误以栈的方式抛出。异常栈:
# err.py:def foo(s): return 10 / int(s)def bar(s): return foo(s) * 2def main(): bar('0')main()#执行,结果如下:$ python3 err.pyTraceback (most recent call last): File "err.py", line 11, inmain() File "err.py", line 9, in main bar('0') File "err.py", line 6, in bar return foo(s) * 2 File "err.py", line 3, in foo return 10 / int(s)ZeroDivisionError: division by zero
记录错误:logging把错误堆栈打印出来,程序继续进行,用法:logging.exception(e)
抛出错误:raise
语句抛出错误,用法:raise
错误类型()
3.调试:
断言:
def foo(s): n = int(s) assert n != 0, 'n is zero!' return 10 / ndef main(): foo('0')
assert
的意思:判断表达式n != 0,
是True继续运行
,是false,抛出异常
Python解释器可以用-O
参数来关闭assert
:
$ python -O err.py
logging:logging
不会抛出错误,可以输出到文件:
import logginglogging.basicConfig(level=logging.INFO) #指定记录信息的级别:debug,info,warning,error等s = '0'n = int(s)logging.info('n = %d' % n) #logging.info()可以输出一段文本print(10 / n)
pdb:调试器pdb单步方式运行(在IDE上可以简单实现)
启动:$ python -m pdb err.py
pdb.set_trace():设置断点
4.单元测试:测试驱动开发(TDD):编写某个功能的代码之前先编写测试代码,然后只编写使测试通过的功能代码,单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作
编写单元测试:Python自带的unittest
模块:import unittest
import unittestfrom mydict import Dictclass TestDict(unittest.TestCase): #测试类,从unittest.TestCase继承 def test_init(self): #以test开头的方法就是测试方法,不以test开头的方法不被认为是测试方法,测试的时候不会被执行。 d = Dict(a=1, b='test') self.assertEqual(d.a, 1) self.assertEqual(d.b, 'test') self.assertTrue(isinstance(d, dict)) def test_key(self): d = Dict() d['key'] = 'value' self.assertEqual(d.key, 'value') def test_attr(self): d = Dict() d.key = 'value' self.assertTrue('key' in d) self.assertEqual(d['key'], 'value') def test_keyerror(self): d = Dict() with self.assertRaises(KeyError): value = d['empty'] def test_attrerror(self): d = Dict() with self.assertRaises(AttributeError): value = d.empty
setUp与tearDown:这两个方法会分别在每调用一个测试方法的前后分别被执行。
5.:示例代码在Python的交互式环境下输入并执行,结果写在注释中
6.文件读写:
读文件:与C语言的类似
open()
函数:打开
read():读取,把内容读到内存,调用read()
会一次性读取文件的全部内容,
read(size)
方法每次最多读取size个字节的内容
readline()
可以每次读取一行内容,返回list
close():
方法关闭文件
with
语句:自动帮我们调用close()
方法
with open('/path/to/file', 'r') as f: print(f.read())
file-like Object:像open()
函数返回的这种有个read()
方法的对象,在Python中统称为file-like Object
Open():
二进制文件:rb
字符编码:errors='ignore'忽略非法编码
f = open('/Users/michael/gbk.txt', 'r', encoding='gbk', errors='ignore')
写文件:write()
7.StringIO和BytesIO:
StringIO:在内存中读写str:
写:
>>> from io import StringIO>>> f = StringIO()>>> f.write('hello')5>>> f.write(' ')1>>> f.write('world!')6>>> print(f.getvalue()) #获得写入后的strhello world!
读:
>>> from io import StringIO>>> f = StringIO('Hello!\nHi!\nGoodbye!')>>> while True:... s = f.readline()... if s == '':... break... print(s.strip())...Hello!Hi!Goodbye!
BytesIO:实现了在内存中读写bytes
8.操作文件和目录:操作文件和目录的函数一部分放在os
模块中,一部分放在os.path
模块中
# 查看当前目录的绝对路径:>>> os.path.abspath('.')'/Users/michael'# 在某个目录下创建一个新目录,首先把新目录的完整路径表示出来:>>> os.path.join('/Users/michael', 'testdir')'/Users/michael/testdir'# 然后创建一个目录:>>> os.mkdir('/Users/michael/testdir')# 删掉一个目录:>>> os.rmdir('/Users/michael/testdir')
9.序列化:变量从内存中变成可存储或传输的过程称之为序列化(pickling),变量内容从序列化的对象重新读到内存里称之为反序列化(unpickling)
pickle
模块来实现序列化。把一个对象序列化并写入文件:
pickle.dumps()
方法把任意对象序列化成一个bytes
,然后,就可以把这个bytes
写入文件。或者用另一个方法pickle.dump()
直接把对象序列化后写入一个file-like Object:
>>> 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.'>>> f = open('dump.txt', 'wb')>>> pickle.dump(d, f)>>> f.close()
把对象从磁盘读到内存时,可以先把内容读到一个
bytes
,然后用pickle.loads()
方法反序列化出对象,或者pickle.load()方法从一个file-like Object
中直接反序列化出对象:
>>> f = open('dump.txt', 'rb')>>> d = pickle.load(f)>>> f.close()>>> d{ 'age': 20, 'score': 88, 'name': 'Bob'}
Pickle只用于保存那些不重要的数据
JSON:JSON表示出来就是一个字符串,用于不同的编程语言之间传递对象,可以被所有语言读取,是一种标准的格式,JSON表示的对象就是标准的JavaScript语言的对象
与Python对比:
JSON类型 | Python类型 |
---|---|
{} | dict |
[] | list |
"string" | str |
1234.56 | int或float |
true/false | True/False |
null | None |
Python内置的json
模块提供Python对象到JSON格式的转换:
Python --> JSON:
>>> import json>>> d = dict(name='Bob', age=20, score=88)>>> json.dumps(d) #dumps()方法返回一个str,内容就是标准的JSON,同样dump()方法可以直接把JSON写入一个file-like Object'{"age": 20, "score": 88, "name": "Bob"}'
JSON --> Python:
>>> json_str = '{"age": 20, "score": 88, "name": "Bob"}'>>> json.loads(json_str){ 'age': 20, 'score': 88, 'name': 'Bob'}
大多数时候我们希望用class当做对象。所以需要class
的实例对象序列化为JSON
dumps()
方法的参数列表提供了一大堆的可选参数,帮助我们来定制JSON序列化,文档:
可选参数default
就是把任意一个对象变成一个可序列为JSON的对象
class实例化对象 ——> JSON:
import jsonclass Student(object): #class对象 def __init__(self, name, age, score): self.name = name self.age = age self.score = scores = Student('Bob', 20, 88)def student2dict(std): #为Student专门写一个转换函数 return { 'name': std.name, 'age': std.age, 'score': std.score }>>> print(json.dumps(s, default=student2dict)) #Student实例首先被student2dict()函数转换成dict,然后再被顺利序列化为JSON{ "age": 20, "name": "Bob", "score": 88}
可修改最后一行,把任意class
的实例变为dict
:
print(json.dumps(s, default=lambda obj: obj.__dict__)) #通常class的实例都有一个__dict__属性,它就是一个dict
lambda知识点:lambda 参数1,参数2、、、 : 参数表达式
10.多线程:
Unix/Linux操作系统提供了一个fork()
系统调用。fork()
函数调用一次,返回两次,因为操作系统自动把当前进程(称为父进程)复制了一份(称为子进程),然后分别在父进程和子进程内返回。
fork
调用,一个进程在接到新任务时就可以复制出一个子进程来处理新任。例如Apache服务器就是由父进程监听端口,每当有新的http请求时,就fork出子进程来处理新的http请求
Windows没有fork
调用,Python提供multiprocessing
模块,其中提供了一个Process
类来代表一个进程对象:
from multiprocessing import Processimport 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',)) #传入一个执行函数和函数的参数,创建一个Process实例 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.
Pool:进程池的方式批量创建子进程:
from multiprocessing import Poolimport os, time, randomdef 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) #对Pool对象调用join()方法会等待所有子进程执行完毕 for i in range(5): p.apply_async(long_time_task, args=(i,)) print('Waiting for all subprocesses done...') p.close() #调用join()之前必须先调用close(),调用close()之后就不能继续添加新的Process了 p.join() print('All subprocesses done.') #执行结果如下:Parent process 669.Waiting for all subprocesses done...Run task 0 (671)...Run task 1 (672)...Run task 2 (673)...Run task 3 (674)...Task 2 runs 0.14 seconds.Run task 4 (673)...Task 1 runs 0.27 seconds.Task 3 runs 0.86 seconds.Task 0 runs 1.41 seconds.Task 4 runs 1.91 seconds.All subprocesses done.
子进程:subprocess
模块可以启动一个子进程,然后控制其输入和输出
在Python代码中运行命令nslookup www.python.org
import subprocessprint('$ nslookup www.python.org')r = subprocess.call(['nslookup', 'www.python.org']) print('Exit code:', r)
子线程输入:communicate()方法
进程间通信:multiprocessing
模块包装了底层的机制,提供了Queue
、Pipes
等多种方式来交换数据
11.多线程:Python提供了两个模块:_thread
和threading
,_thread
是低级模块,threading
是高级模块(常使用)
启动一个线程就是把一个函数传入并创建Thread
实例,然后调用start()
开始执行:
import time, threading# 新线程执行的代码:def loop(): print('thread %s is running...' % threading.current_thread().name) #current_thread()函数永远返回当前线程的实例 n = 0 while n < 5: n = n + 1 print('thread %s >>> %s' % (threading.current_thread().name, n)) #LoopThread命名子线程,不起名字Python就自动给线程命名为Thread-1,Thread-2 time.sleep(1) print('thread %s ended.' % threading.current_thread().name) print('thread %s is running...' % threading.current_thread().name)t = threading.Thread(target=loop, name='LoopThread') #t.start()t.join()print('thread %s ended.' % threading.current_thread().name)#执行结果如下:thread MainThread is running... #主线程实例的名字叫MainThreadthread LoopThread is running...thread LoopThread >>> 1thread LoopThread >>> 2thread LoopThread >>> 3thread LoopThread >>> 4thread LoopThread >>> 5thread LoopThread ended.thread MainThread ended.
Lock:锁
多线程和多进程最大的不同:多进程中同一个变量,各自有一份拷贝存在于每个进程中,多线程中,所有变量都由所有线程共享,任何一个变量都可以被任何一个线程修改
threading.Lock():当某个线程执行时,用threading.Lock()给该线程上锁,因此其他线程不能同时执行,只能等待,直到锁被释放后,获得该锁以后才能改
GIL锁:释器执行代码时,有一个GIL锁,任何Python线程执行前,必须先获得GIL锁每执行100条字节码,解释器就自动释放GIL锁让别的线程有机会执行,这个GIL全局锁实际上把所有线程的执行代码都给上了锁,所以多线程在Python中只能交替执行,即使100个线程跑在100核CPU上,也只能用到1个核。
Python虽然不能利用多线程实现多核任务,但可以通过多进程实现多核任务。多个Python进程有各自独立的GIL锁,互不影响。
12.:解决了参数在一个线程中各个函数之间互相传递的问题。
应到Python语言,单线程的异步编程模型称为协程,有了协程的支持,就可以基于事件驱动编写高效的多任务程序。我们会在后面讨论如何编写协程
13.常用内建模块:
是Python处理日期和时间的标准库
是Python内建的一个集合模块,提供了许多有用的集合类。
是一种用64个字符来表示任意二进制数据的方法。
来解决bytes
和其他二进制数据类型的转换
提供了常见的摘要算法,如MD5,SHA1等等
实现了标准的Hmac算法
提供了非常有用的用于操作迭代对象的函数。
提供了一系列用于操作URL的功能。