一、重写父类的方法
重写包含两种定义,其一是父类的方法不可用了,需要完全重新编写;其二是保留父类方法的同时新增一些功能。
首先是全部重写,很好理解,就是将整个方法都修改掉。比如下面的类Zll中,定义了一个smile方法,笑的方式是“哈哈哈”。而继承Zll的子类Dcg,笑的方式并不是这个,那么他就重写smile方法。
1 class Zll(object): 2 def smile(self): 3 print('哈哈哈') 4 5 6 class Dcg(object): 7 def smile(self): 8 print('嘿嘿嘿')
其次是修改父类方法。就是在保留父类方法的基础上进行添加或者部分修改。如果父类只有一个,那么直接在方法中调用父类方法即可。比如:
1 class Lw(object): 2 def smile(self): 3 Zll.smile() 4 print('啊啊啊啊')
但当子类继承了多个父类时,像这样去调用就非常麻烦了,因为还要分辨这个方法属于哪一个父类。这时候可以用super()方法自动查找父类。当有多个父类时,super()会默认从第一个父类开始查找。
1 class Xz(Zll, Dcg, Lw): 2 def smile(self): 3 # Zll().smile() # 先调用父类的方法,这样可以使用父类的功能 4 super(Xz, self).smile() # 可以帮助子类自动找到其父类 5 # 如果子类继承自多个父类,super自动从第一个开始寻找smile方法 6 print('呵呵呵呵') # 重写了父类的方法
二、经典类和新式类
经典类和新式类的区别主要有以下两点:
1、定义时有所不同
1 class A(object): # 新式类 2 pass 3 4 5 class A1(): # 经典类 6 pass
2、多重继承时,查找方法的方式不同
在python2中,经典类的查找方式是深度查找,新式类的查找方式是广度查找。到python3默认所有的类都是新式类,所以都默认为广度查找了。如下面代码中,实例化对象d使用方法smile时,因为其类D继承自C,B,A,都是新式类,所以D也是新式类,其查找smile的方法是广度优先也就是C->B->A的顺序查找的。而d1是D1类的实例化对象,D1继承自C1,B1和A1都是经典类,所以D1也是经典类,d1.smile()时查找方法是深度优先也就是C1->A1->B1。
1 class A(object): 2 pass 3 class A1(): 4 pass 5 6 class B(A): 7 pass 8 class B1(A1): 9 pass 10 11 class C(A): 12 def smile(self): 13 print('哈哈') 14 class C1(A1) 15 def smile(self): 16 print('haha') 17 18 class D(C,B,A): 19 pass 20 class D1(C1,B1,A1): 21 pass 22 23 d = D() 24 d.smile() 25 d1 = D1() 26 d1.smile
三,多线程和多进程
一个进程可以有多个线程,进程是资源的合集,线程是进程中的最小执行单元。用很简单的方式来展示一下串行和并行。并行就是实例化线程,然后一起start开始干活。
1 import threading 2 import time 3 4 5 def run(): 6 time.sleep(3) # 做一件事需要三秒 7 print('哈哈哈') 8 9 for i in range(5): # 串行,一个人干完另一个人再干活 10 run() 11 12 for i in range(5): # 并行 13 t = threading.Thread(target=run) # 实例化一个线程 14 t.start()
如果我们要比较串行和并行的执行时间,串行的可以直接在程序开始时记录当前时间,结束后记录当前时间,然后相减即可。但是并行不行,因为并行记录的是主线程的时间,其实子线程还在运行中,直接这样记录是不准确的。我们可以用下面的方法,记录每次子线程,然后让主线程等待一下子线程
1 def run(): 2 time.sleep(3) # 做一件事需要三秒 3 print('哈哈哈') 4 start_time = time.time() 5 threads = [] 6 for i in range(5): 7 t = threading.Thread(target=run) 8 t.start() 9 threads.append(t) 10 # t.join() # 这个就是主线程等待子线程执行结束,但是直接这样是不行的因为每次t里面都是不同的线程 11 # 这样就变成了串行了,一个干完等待,继续下一个,然后等待 12 for t in threads: # 主线程先运行了run()然后就继续往下走了,然后启动了子线程,所以此时主线程就已经在等待中了 13 t.join() # 这样就是每个线程都等待一下 14 end_time = time.time() 15 run_time = end_time-start_time 16 print('执行总共花了时间%s' % run_time)
四、线程锁
当所有的线程都在修改同一个数据时,可能出现覆盖的情况,比如一个数不断加1,因为后面的结果数覆盖了前面的数据,导致结果不正确。这个时候就要对这个变量加一个线程锁,让它在正在被修改的时候不会被其他任务修改。其实python3是会自动加锁的,但是我们也需要学习一下这个概念和方法,必须使用python2编程时就要用到了。
1 import threading, time 2 num = 1 3 lock = threading.Lock() # 申请一把锁 4 5 def run(): 6 time.sleep(1) 7 global num 8 lock.acquire() # 加上锁,其实python3是会默认加锁的 9 num += 1 10 lock.release() # 解锁 11 ts = [] 12 for i in range(50): 13 t = threading.Thread(target=run) 14 t.start() 15 ts.append(t) 16 [t.join() for t in ts] # 如果不等待,主线程跑太快,上面那个time.sleep()会导致num=1就执行完了 17 print(num)
五、守护线程
只要主线程结束,立刻结束其他子线程。不论子线程是否运行成功。
1 import threading,time 2 def run(): 3 time.sleep(3) # 做一件事需要三秒 4 print('哈哈哈') 5 6 for i in range(5): 7 t = threading.Thread(target=run) 8 t.setDaemon(True) # 把子线程设置为守护线程 9 t.start() 10 11 print('Done,运行完成。') 12 time.sleep(3) # 等三秒是可以运行下一个子线程的,但是等1秒的话来不及走子线程主线程就结束了
六、多进程
多进程用于处理CPU密集型任务。多线程用于处理IO密集型任务。其实python是无法使用多核的,原因如下:
1 import multiprocessing,threading 2 3 def my(): 4 print('哈哈哈') 5 6 7 def run(num): 8 for i in range(num): 9 t = threading.Thread(target=my) 10 t.start() 11 if __name__ == '__main__': 12 for i in range(5): 13 p = multiprocessing.Process(target=run, args=(6,)) # 启动五个进程,每个进程6个线程 14 # 这样就可以利用CPU的多核 15 p.start()