python-知识点总结(三)

进程

1、进程和线程的联系及区别:
进程和线程都可以实现多任务。(1)进程是资源分配的基本单位,程序运行时会系统创建一个进程,并为它分配资源。线程是程序执行的最小单位。(2)线程不可独立存在,需要依赖于进程。(3)一个进程可以有多个线程,多个线程共享进程的资源,而多个进程间资源是独立的,所以CPU切换一个线程的花费比切换进程的小,同时创建一个线程的开销也比进程要小很多。(4)多进程稳定性高,一个子进程崩了不会影响其他进程,因为每个进程都有独立的资源;但是任何一个线程挂掉都可能直接造成整个进程崩溃,因为多个线程共享一个进程资源(5)由于GIL锁的缘故,python 中线程实际上是并发运行(即便有多个cpu,线程会在其中一个cpu来回切换,只占用一个cpu资源),而进程才是真正的并行(同时执行多个任务,占用多个cpu资源)(6)IO密集型使用多线程,计算密集型使用多进程。

2、进程状态:就绪、运行、等待(阻塞)、死亡
3、Ps -aux 可查看某个程序的进程数
4、代码实现同线程类似

import multiprocessing

def test_1():
    while True:
        print("进程1")


def test_2():
    while True:
        print("进程2")

if __name__=="__main__":
    p1 = multiprocessing.Process(target=test_1)
    p2 = multiprocessing.Process(target=test_2)
    p1.start()
    p2.start()

5、一个程序至少有一个进程,一个进程至少有一个主线程。
6、可以通过socket、文件读写、Queue队列完成进程间通信

进程池

1、主进程或者主线程会等待子进程或者子线程执行完毕后结束,但是创建线程池后,主进程不会等待进程池中的代码执行完,可以使用进程池的.join()方法等待子进程执行完。
2、示例:

import os
import multiprocessing


def copy_file(file_name):
    with open(file_name) as f:
        cont = f.read()
        with open(file_name,'wb') as f2:
                f2.write(cont)


def main():
    folder_name = ""
    try:
        os.mkdir(folder_name+"复制")
    except Exception as e:
        pass
    files_names = os.listdir(folder_name)
    po = multiprocessing.Pool(5)
    for fi in files_names:
        po.apply_async(copy_file, args = (fi,))
    po.close()
    po.join()


if __name__ == "__main__":
    main()

线程

1、多线程比单线程抓取数据的时候快,
2、线程实现方式有两种:
(1)类式

import threading,time

class myThread(threading.Thread):
    def __init__(self, name, delay, counter):
        super().__init__()
        self.name = name
        self.delay = delay
        self.counter = counter
    # 重写run
    def run(self):
        self.name = threading.Thread.getName(self)
        print_time(self.name, self.delay, self.counter)
        print("线程名:"+self.name)

def print_time(name, delay, counter):
    while counter:
        time.sleep(delay)
        print("{}: {}".format(name, time.ctime(time.time())))
        counter -= 1

#创建线程
thread1 = myThread("THREAD-1", 2, 3)
thread2 = myThread("THREAD-2", 5, 2)
print("THREAD-1 is alive?", thread1.is_alive())
thread1.start()
thread2.start()
print("THREAD-1 is alive?", thread1.is_alive())
thread1.join()
thread2.join()
print("THREAD-1 is alive?", thread1.is_alive()) 

(2)函数式

import threading
def A():
	print("线程1")
def B()print("线程2")
if __name__ == "__main__":
	t1 = threading.Thread(target = A, args=(,))
	t2 = threading.Thread(target = B)
	t1.start()  //start 创建线程,start前不会创建线程
	t2.start()
	print “主线程”

3、主线程在子线程执行结束后结束
4、一个程序运行起来,一定有一个主线程
5、多线程共享全局变量有抢占资源的问题,可采用同步的互斥锁来解决问题
6、threading常用方法
threading.enumerate() 返回当前正在运行的线程list,正在运行是指线程启动后、结束前;
threading.current_thread() 返回当前线程变量
threading.active_count() 返回当前正在运行的线程数

同步 互斥锁

同步是“协同步骤”,不是“一起”
当多个线程几乎同时修改某一个共享数据的时候,需要进行同步控制。线程同步能保证多个线程安全访问竞争资源。最简单的同步机制是引入互斥锁。
互斥锁保证了每次只有一个线程进行写入操作,从而保证了多线程先数据的准确性。

创建锁 :mt = threading.Lock()
锁定 :mt.acquire()
释放 :mt.release()

死锁

两个锁互相等待时会出现死锁,此时可以添加超时时间等来解决死锁问题。

线程并发

并发是指两个或多个事件在同一时间间隔发生,python中的线程属于并发,不管计算机有多少个CPU,不管你开了多少个线程,同一时间多个任务会在其中一个CPU来回切换,只占用一个CPU,效率并不高;

进程并行

并行是指两个或者多个事件在同一时刻发生,python中的进程属于并行,能充分利用计算机资源,效率最高,同时执行多个任务,占用多个cpu资源;

1、栈是先进后出型数据结构
2、写一个模拟栈的程序。用户可以输入的参数如下:
在这里插入图片描述

import sys
class Stack():

    def __init__(self):
        self.stack = []

    def push_stack(self, arg):
        self.stack.append(arg)
        return self.stack

    def get_top(self):
        if self.stack:
            return self.stack[-1]
        else:
            return None

    def get_len(self):
        return len(self.stack)

    def is_empty(self):
        return self.stack == []

    def pop_stack(self):
        self.stack.pop()
        return self.stack

    def show_stack(self):
        return self.stack

    def do_exit(self):
        sys.exit()
s = Stack()
s.push_stack("aaa")
s.push_stack("bbb")
s.push_stack("ccc")
s.push_stack("ddd")
r = s.get_len()
print(r)
q = s.get_top()
print(q)
print(s.is_empty())
s.pop_stack()
print(s.show_stack())

队列Queue

1、队列是先进先出型数据结构
2、写一个模拟队列的程序。用户可以输入的参数如下
在这里插入图片描述

import sys
class Queue():
    def __init__(self, size):
        self.queue = []
        self.size = size

    def enqueue(self, ele):
        if self.isfull():
            raise Exception("queue is full")
        else:
            self.queue.append(ele)

    def dequeue(self):
        if self.isempty():
            raise Exception("queue is empty")
        else:
            self.queue.pop(0)
    def top_queue(self):
        return self.queue[0]

    def last_queue(self):
        return self.queue[-1]

    def len_queue(self):
        return len(self.queue)

    def isfull(self):
        return len(self.queue) == self.size

    def isempty(self):
        return self.queue == []

    def do_exit(self):
        sys.exit()

    def show_queue(self):
        return self.queue

q = Queue(3)
q.enqueue("a")
q.enqueue("b")
q.enqueue("c")
q.dequeue()
q.enqueue("d")
print(q.show_queue())

全局解释器锁(GIL)

1、每个线程执行前都需要先获取到GIL,保证同一时刻只能有一个线程在执行
2、全局解释器锁只对线程有影响,对进程、协程没有影响
3、如果用cpython 解释器,建议用多进程;如果不使用cpython解释器,多线程比多进程更节省资源

迭代器

1、迭代是访问集合元素的一种方式。
2、可直接作用于for循环的对象为可迭代对象Iterable。判断是否可迭代,可以使用isinstance()判断一个对象是否是Iterable对象。如果想让一个对象成为可迭代对象,类必须实现__iter__()方法。
3、迭代器是一个可以记住遍历位置的对象,用来帮助我们记录每次迭代访问到的位置。
迭代器从集合的第一个元素开始访问,直到所有元素都被访问完结束。迭代器只能往前不能往后。
可以被next()函数调用并不断返回下一个值的对象称为迭代器:Iterator
使用next()函数的时候,调用的就是迭代器对象的__next__方法,python要求迭代器本身是可迭代的,所以我们还要为迭代器实现__iter__方法。一个实现了__iter__方法和__next__方法的对象,就是迭代器。
4、判断一个对象是否可以使用for循序,先判断对象是否可迭代,实现__iter__()的对象即可迭代;再判断__iter__()方法是否返回迭代器,iter()需返回一个具有iter方法和next方法的对象引用,这个对象引用叫作迭代器)。
for循环的本质就是先通过iter()函数获取可迭代对象的迭代器,然后调用迭代器的next()方法获取下一个值,直到报出StopIteration.。
在这里插入图片描述
5、判断是否可迭代:
from collections import Iterable
print(isinstance([], Iterable))
6、判断__iter__()方法是否返回一个具有iter方法和next方法的对象引用(迭代器),可直接使用iter(对象)。iter函数会直接调用对象中的__iter__(),并返回__iter__()的返回值。
7、可迭代对象一类是集合数据类型,如str, list、tuple、dict、set、str等;一类是generator,包括生成器和带yield的generator function。
8、迭代器协议:具有next和iter方法的对象
7、list、dict等是可迭代对象,但不是迭代器。执行iter方法可以把可迭代对象变成迭代器
8、迭代器存储的是生成数据的方式,而不是生成数据的结果,迭代器占用内存小。示例:

class Fibonacci(object):
    def __init__(self, count):
        self.m = 0
        self.n = 1
        self.count = count

    def __iter__(self):
        return self

    def __next__(self):
        if self.count<0:
            raise StopIteration
        temp = self.m
        self.m, self.n = self.n, self.m+self.n
        self.count -= 1
        return temp

for i in Fibonacci(10):
    print(i)

9、不是只有for循环可以接受可迭代对象,list,tuple等都可以接收可迭代对象。list(),tuple()转换
10、除了next()方法外,还可以使用send()方法唤醒迭代器
在这里插入图片描述

生成器

1、生成器是一种特殊的迭代器。
2、创建生成器
方法1(不常用):
‘’‘把列表生成式的中括号变成小括号’’’

nums = [x*2 for x in range(10)]    //列表生成式
print(nums)
nums = (x*2 for x in range(10)) 	//生成器
print(nums)

方法2:

'''如果一个函数中有yield语句,那么这个函数就不是一个函数,而是一个生成器的模板'''
def create_num(all_num):
    a, b = 0, 1
    current_num = 0
    while current_num < all_num:
    '''每次yield a 返回的值 其实是next(s)'''
        yield a
        a, b = b, a+b
        current_num += 1
    '''py3中的生成器允许使用return返回最终运行的返回值,py2不允许 '''
   	return "ok"
'''调用create_num 不是调用函数,而是创建一个生成器对象'''
s = create_num(10)
'''调用next()函数唤醒生成器'''
qq = next(s)  
print(qq)
'''可以使用next()函数让生成器从断点处继续执行'''
qq = next(s)
print(qq)
qq = next(s)
print(qq)
//for num in s:
//    print(num)

yield

yield关键字有两个作用:
(1)保存当前运行状态,然后暂停执行,即将生成器函数挂起
(2)将yield关键字后面表达式的值作为返回值返回,此时可理解为起到return作用

Python 的 generator 提供了一种实现迭代器协议的便捷方式。 如果容器对象 iter() 方法被实现为一个生成器,它将自动返回一个迭代器对象(从技术上说是一个生成器对象),该对象提供 iter() 和 next() 方法。
generator 就是生成器

协程

协程切换任务占用资源很少,效率高
1、使用yield实现多任务,就是协程

def test1():
    while True:
        print("--1--")
        yield


def test2():
    while True:
        print("--2--")
        yield


def main():
    t1 = test1()
    t2 = test2()
    while True:
        next(t1)
        next(t2)


if __name__ == "__main__":
    main()

2、为了更好的使用协程来完成多任务,可使用greenlet模块来实现(使用比较少)
安装:sudo pip3 install greenlet

from greenlet import greenlet

def test1():
    while True:
        print("--1--")
        gr2.switch()


def test2():
    while True:
        print("--2--")
        gr1.switch()


gr1 = greenlet(test1)
gr2 = greenlet(test2)
gr2.switch()

3、使用gevent实现协程(常使用方法)
安装sudo pip3 install gevent
gevent不同于yield和greenlet,它有个优点,遇到延时会自动切换
在这里插入图片描述
程序中有耗时时,将程序中用到的耗时操作的代码自动换成gevent.sleep()

import gevent
from gevent import monkey
monkey.patch_all()

批量添加任务

gevent.joinall(
    [
        gevent.spawn(函数名, "参数1"),
        gevent.spawn(函数名, "参数2")
    ]
)

装饰器

1、python装饰器(fuctional decorators)就是用于拓展原来函数功能的一种函数,目的是在不改变原函数名(或类名)的情况下,给函数增加新的功能。
装饰器函数的特殊之处在于它的返回值也是一个函数,这个函数是内嵌“原”函数的函数。
2、python内置装饰器:

  • staticmethod 是类静态方法,其跟成员方法的区别是没有 self 参数,并且可以在类不进行实例化的情况下调用
  • classmethod 与成员方法的区别在于所接收的第一个参数不是 self (类实例的指针),而是cls(当前类的具体类型)
  • property 是属性的意思,表示可以通过类实例直接访问的信息
  • unique 定义枚举函数, value值唯一
    3、原始装饰器实现方法:
import time

def deco(f):
    def wrapper():
        start_time = time.time()
        f()
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" %execution_time )
    return wrapper

@deco  
def f():
    print("hello")
    time.sleep(1)
    print("world")
if __name__ == '__main__':
	f()

@deco //等价于 f = deco(f)

4、带有固定参数的装饰器

import time

def deco(f):
    def wrapper(a,b):
        start_time = time.time()
        f(a,b)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
    return wrapper

@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))
if __name__ == '__main__':
    f(3,4)

5、无固定参数的装饰器

import time

def deco(f):
    def wrapper(*args, **kwargs):
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time_ = (end_time - start_time)*1000
        print("time is %d ms" %execution_time)
    return wrapper

@deco
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))

@deco
def f2(a,b,c):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b+c))

if __name__ == '__main__':
    f2(3,4,5)
    f(3,4)

6、使用多个装饰器,装饰一个函数

import time

def deco01(f):
    def wrapper(*args, **kwargs):
        print("this is deco01")
        start_time = time.time()
        f(*args, **kwargs)
        end_time = time.time()
        execution_time = (end_time - start_time)*1000
        print("time is %d ms" % execution_time)
        print("deco01 end here")
    return wrapper
def deco02(f):
    def wrapper(*args, **kwargs):
        print("this is deco02")
        f(*args, **kwargs)

        print("deco02 end here")
    return wrapper

@deco01
@deco02
def f(a,b):
    print("be on")
    time.sleep(1)
    print("result is %d" %(a+b))
    
if __name__ == '__main__':
    f(3,4)
”f(3, 4) = deco01(deco02(f(3, 4)))

7、装饰器的作用和功能
引入日志
函数执行时间统计
执行函数前预备处理
执行函数后的清理功能
权限校验等场景
缓存
8、带有返回值的装饰器

def line(f):
    def create_y():
        return f()
    return create_y

@line
def test1():
    return "test1"

ret = test1()
print(ret)

9、用类对函数进行装饰

class Test(object):
    def __init__(self, func):
        self.func = func

    def __call__(self):
        print("为装饰器添加功能")
        return self.func()

@Test
def get_str():
    return "haha"

print(get_str())

框架搭建

1、开发时,python代码模式
导入模块
定义全局变量
定义类
定义函数

def main():
pass

if name==“main”:
main()

设计模式

单例设计模型

1、用类创建的对象,在系统中只有唯一的实例(每次执行类名创建对象, 返回对象的内存地址都是一样的)
2、实现

class A():
    instance = None
    def __new__(cls, *args, **kwargs):
        print("创建对象分配空间")
        if A.instance is None:
            A.instance =  super().__new__(cls)
        return A.instance

p1 = A()
p2 = A()
print(id(p1))
print(id(p2))

虽然每次都使用第一次创建的对象,但是会多次调用初始化方法。
修改方法:

class A():
    	instance = None
	init_flag = False

    	def __new__(cls, *args, **kwargs):
        		print("创建对象分配空间")
        		if A.instance is None:
            		A.instance =  super().__new__(cls)
		return A.instance

	def __init__(self):
		if not init_flag:
			print(“初始化”)
			init_flag = True
p1 = A()
p2 = A()
print(id(p1))
print(id(p2))

哈希

1、哈希是一种算法,hash函数接受一个不可变类型的数据作为参数,返回一个整数。传入数据相等返回的哈希值也相等。

深拷贝、浅拷贝

1、浅拷贝是对一个对象的顶层拷贝。浅拷贝后,改变原始对象中为可变类型的元素的值,会同时影响拷贝对象;改变原始对象中为不可变类型的元素的值,不会响拷贝对象。
2、深拷贝,除了顶层拷贝,还对子元素也进行了拷贝。经过深拷贝后,原始对象和拷贝对象所有的可变元素地址都不相同。
在这里插入图片描述
3、不论深拷贝还是浅拷贝,拷贝list时 会新建地址,但是拷贝int、str不可变数据类型时不会新建地址。
4、元组中没有可变类型时,深拷贝浅拷贝都不会拷贝,都是引用指向;但是元组中有可变类型时,浅拷贝不拷,深拷贝会新建地址
5、copy.copy()是浅拷贝
copy.deepcopy()是深拷贝
6、另外 列表的切片是浅拷贝;
字典的.copy()方法也是浅拷贝

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值