• 进程 相关概念 进程 进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行...

    进程

    相关概念

    进程

    进程(Process)是计算机中的程序关于某数据集合上的一次运行活动,是系统进行资源分配和调度的基本单位,是操作系统结构的基础。在早期面向进程设计的计算机结构中,进程是程序的基本执行实体;在当代面向线程设计的计算机结构中,进程是线程的容器。程序是指令、数据及其组织形式的描述,进程是程序的实体。

    狭义定义:进程是正在运行的程序的实例(an instance of a computer program that is being executed)。

    广义定义:进程是一个具有一定独立功能的程序关于某个数据集合的一次运行活动。它是操作系统动态执行的基本单元,在传统的操作系统中,进程既是基本的分配单元,也是基本的执行单元。

    同步/异步

    所谓同步就是一个任务的完成需要依赖另外一个任务时,只有等待被依赖的任务完成后,依赖的任务才能算完成,这是一种可靠的任务序列。要么成功都成功,失败都失败,两个任务的状态可以保持一致。

    所谓异步是不需要等待被依赖的任务完成,只是通知被依赖的任务要完成什么工作,依赖的任务也立即执行,只要自己完成了整个任务就算完成了。至于被依赖的任务最终是否真正完成,依赖它的任务无法确定,所以它是不可靠的任务序列。

    阻塞/非阻塞

    阻塞和非阻塞这两个概念与程序等待消息通知(无所谓同步或者异步)时的状态有关。也就是说阻塞与非阻塞主要是程序(线程)等待消息通知时的状态角度来说的。

    并发/并行

    并行 : 并行是指多个任务同时执行,比如两个男人同时在给自己女朋友发微信。

    并发 : 并发是多个任务交替轮流使用资源,比如一个男人在给他7个女朋友发微信,只要他发的够快,宏观上来说他在同时聊7个人。

    进程状态与调度

    在了解其他概念之前,我们首先要了解进程的几个状态。在程序运行的过程中,由于被操作系统的调度算法控制,程序会进入几个状态:就绪,运行和阻塞。

    (1)就绪(Ready)状态

    当进程已分配到除CPU以外的所有必要的资源,只要获得处理机便可立即执行,这时的进程状态称为就绪状态。

    (2)执行/运行(Running)状态当进程已获得处理机,其程序正在处理机上执行,此时的进程状态称为执行状态。

    (3)阻塞(Blocked)状态正在执行的进程,由于等待某个事件发生而无法执行时,便放弃处理机而处于阻塞状态。引起进程阻塞的事件可有多种,例如,等待I/O完成、申请缓冲区不能满足、等待信件(信号)等。

    Python中使用多进程

    之前我们已经了解了很多进程相关的理论知识,了解进程是什么应该不再困难了,刚刚我们已经了解了,运行中的程序就是一个进程。所有的进程都是通过它的父进程来创建的。因此,运行起来的python程序也是一个进程,那么我们也可以在程序中再创建进程。多个进程可以实现并发效果,也就是说,当我们的程序中存在多个进程的时候,在某些时候,就会让程序的执行速度变快。以我们之前所学的知识,并不能实现创建进程这个功能,所以我们就需要借助python中强大的模块。

    multiprocessing模块

    仔细说来,multiprocess不是一个模块而是python中一个操作、管理进程的包。 之所以叫multi是取自multiple的多功能的意思,在这个包中几乎包含了和进程有关的所有子模块。由于提供的子模块非常多,为了方便大家归类记忆,我将这部分大致分为四个部分:创建进程部分,进程同步部分,进程池部分,进程之间数据共享。

    multiprocessing.Process介绍

    process模块是一个创建进程的模块,借助这个模块,就可以完成进程的创建。

    Process([group [, target [, name [, args [, kwargs]]]]]),由该类实例化得到的对象,表示一个子进程中的任务(尚未启动)
    
    强调:
    1. 需要使用关键字的方式来指定参数
    2. args指定的为传给target函数的位置参数,是一个元组形式,必须有逗号
    
    参数介绍:
    1 group参数未使用,值始终为None
    2 target表示调用对象,即子进程要执行的任务
    3 args表示调用对象的位置参数元组,args=(1,2,'egon',)
    4 kwargs表示调用对象的字典,kwargs={'name':'egon','age':18}
    5 name为子进程的名称
    1 p.start():启动进程,并调用该子进程中的p.run()
    2 p.run():进程启动时运行的方法,正是它去调用target指定的函数,我们自定义类的类中一定要实现该方法
    3 p.terminate():强制终止进程p,不会进行任何清理操作,如果p创建了子进程,该子进程就成了僵尸进程,使用该方法需要特别小心这种情况。如果p还保存了一个锁那么也将不会被释放,进而导致死锁
    4 p.is_alive():如果p仍然运行,返回True
    5 p.join([timeout]):主线程等待p终止(强调:是主线程处于等的状态,而p是处于运行的状态)。timeout是可选的超时时间,需要强调的是,p.join只能join住start开启的进程,而不能join住run开启的进程 
    方法介绍
    1 p.daemon:默认值为False,如果设为True,代表p为后台运行的守护进程,当p的父进程终止时,p也随之终止,并且设定为True后,p不能创建自己的新进程,必须在p.start()之前设置
    2 p.name:进程的名称
    3 p.pid:进程的pid
    4 p.exitcode:进程在运行时为None、如果为–N,表示被信号N结束(了解即可)
    5 p.authkey:进程的身份验证键,默认是由os.urandom()随机生成的32字符的字符串。这个键的用途是为涉及网络连接的底层进程间通信提供安全性,这类连接只有在具有相同的身份验证键时才能成功(了解即可)
    属性介绍
    在Windows操作系统中由于没有fork(linux操作系统中创建进程的机制),在创建子进程的时候会自动 import 启动它的这个文件,而在 import 的时候又执行了整个文件。因此如果将process()直接写在文件中就会无限递归创建子进程报错。所以必须把创建子进程的部分使用if __name__ ==‘__main__’ 判断保护起来,import 的时候  ,就不会递归运行了。
    在windows中使用process模块的注意事项

    使用process模块创建进程

    在一个python进程中开启子进程,start方法和并发效果。

    from multiprocessing import Process
    import time
    
    
    def task(name):
        print('{} is running!'.format(name))
        time.sleep(3)
        print('{} is done!'.format(name))
    
    
    # Windows开子进程要写在__name__==__main__下
    # 因为开子进程会重新加载父进程的内容
    if __name__ == '__main__':
        # 创建一个Python中的进程对象
        p = Process(target=task, args=('t1', ))
        # p = Process(target=task, kwargs={'name': 't1'})
        p.start()  # 调用操作系统接口启动一个进程执行命令
        print('--- 主进程 ----')

    查看主进程id和父进程id:

    from multiprocessing import Process
    import time
    import os
    
    
    def task(name):
        print('{} is running!'.format(name))
        print('子进程id :', os.getpid(), '父进程id :', os.getppid())
        time.sleep(3)
        print('{} is done!'.format(name))
    
    
    # Windows开子进程要写在__name__==__main__下
    # 因为开子进程会重新加载父进程的内容
    if __name__ == '__main__':
        print('主进程id :', os.getpid())
        # 创建一个Python中的进程对象
        p = Process(target=task, args=('t1', ))
        # p = Process(target=task, kwargs={'name': 't1'})
        p.start()  # 调用操作系统接口启动一个进程执行命令
        print('--- 主进程 ----')
    查看进程id

    我们再启动多个进程看一下,运行的效果:(注意,子进程的执行顺序不是根据启动顺序决定的)

    from multiprocessing import Process
    import time
    
    
    def task(name):
        print('{} is running!'.format(name))
        time.sleep(3)
        print('{} is done!'.format(name))
    
    
    if __name__ == '__main__':
        for i in range(10):
            p = Process(target=task, args=(i, ))
            p.start()
        print('--- 主进程 ----')

    进程间的数据隔离

    进程的运行时数据是互相独立的,不会相互影响。

    想个办法,验证一下:

    from multiprocessing import Process
    import time
    
    x = 100
    
    
    def change():
        global x
        x = 10
        print('子进程修改了x,子进程结束了!')
    
    
    if __name__ == '__main__':
        p = Process(target=change)
        p.start()
        time.sleep(5)  # 想办法等子进程结束 此处应该有思考
        print(x)

    上面的例子中可以看到,父进程中的X变量并没有被子进程中的代码修改掉。

    join

    接下来我们看一下上面代码中的问题,也就是我们应该如何优雅的实现父进程等待子进程结束呢?

    这里就用到了Python中进程对象的join()。

    from multiprocessing import Process
    
    x = 100
    
    
    def change():
        global x
        x = 10
        print('子进程修改了x,子进程结束了!')
    
    
    if __name__ == '__main__':
        p = Process(target=change)
        p.start()
        p.join()  # 优雅地实现主进程等待子进程结束
        print(x)

    再来几个多线程的例子,验证一下:

    from multiprocessing import Process
    import time
    
    
    def task(n):
        print('这是子进程:{}'.format(n))
        time.sleep(n)
        print('子进程:{}结束了!'.format(n))
    
    
    if __name__ == '__main__':
        start_time = time.time()
        p1 = Process(target=task, args=(1,))
        p2 = Process(target=task, args=(2,))
        p3 = Process(target=task, args=(3,))
        p1.start()
        p2.start()
        p3.start()
    
        p1.join()
        p2.join()
        p3.join()
        print('我是主进程')
        print('共耗时:{}'.format(time.time()-start_time))

    或者写成下面这样的简写方式:

    from multiprocessing import Process
    import time
    
    
    def task(n):
        print('这是子进程:{}'.format(n))
        time.sleep(n)
        print('子进程:{}结束了!'.format(n))
    
    
    if __name__ == '__main__':
        start_time = time.time()
        # p1 = Process(target=task, args=(1,))
        # p2 = Process(target=task, args=(2,))
        # p3 = Process(target=task, args=(3,))
        # p1.start()
        # p2.start()
        # p3.start()
        #
        # p1.join()
        # p2.join()
        # p3.join()
        # 或者简写成下面的方式
        p_list = []
        for i in range(1, 4):
            p = Process(target=task, args=(i,))
            p.start()
            p_list.append(p)
        for p in p_list:
            p.join()
        print('我是主进程')
        print('共耗时:{}'.format(time.time()-start_time))
    for循环起进程

    思考一下:

    下面的代码有何不可?

    from multiprocessing import Process
    import time
    
    
    def task(n):
        print('这是子进程:{}'.format(n))
        time.sleep(n)
        print('子进程:{}结束了!'.format(n))
    
    
    if __name__ == '__main__':
        start_time = time.time()
        p1 = Process(target=task, args=(1,))
        p2 = Process(target=task, args=(2,))
        p3 = Process(target=task, args=(3,))
        p1.start()
        p1.join()
        p2.start()
        p2.join()
        p3.start()
        p3.join()
    
        print('我是主进程')
        print('共耗时:{}'.format(time.time() - start_time))
    这样写的问题是?

    除了上面这些开启进程的方法,还有一种以继承Process类的形式开启进程的方式

    import os
    from multiprocessing import Process
    
    
    class MyProcess(Process):
        def __init__(self,name):
            super().__init__()
            self.name=name
        def run(self):
            print(os.getpid())
            print('%s 正在和女主播聊天' %self.name)
    
    p1=MyProcess('wupeiqi')
    p2=MyProcess('yuanhao')
    p3=MyProcess('nezha')
    
    p1.start() #start会自动调用run
    p2.start()
    # p2.run()
    p3.start()
    
    
    p1.join()
    p2.join()
    p3.join()
    
    print('主线程')
    通过继承Process类开启进程

    守护进程

    父进程中将一个子进程设置为守护进程,那么这个子进程会随着主进程的结束而结束。

    主进程创建守护进程

    其一:守护进程会在主进程代码执行结束后就终止

    其二:守护进程内无法再开启子进程,否则抛出异常:AssertionError: daemonic processes are not allowed to have children

    注意:进程之间是互相独立的,主进程代码运行结束,守护进程随即终止

    import os
    import time
    from multiprocessing import Process
    
    class Myprocess(Process):
        def __init__(self,person):
            super().__init__()
            self.person = person
        def run(self):
            print(os.getpid(),self.name)
            print('%s正在和女主播聊天' %self.person)
    
    
    p=Myprocess('哪吒')
    p.daemon=True #一定要在p.start()前设置,设置p为守护进程,禁止p创建子进程,并且父进程代码执行结束,p即终止运行
    p.start()
    time.sleep(10) # 在sleep时查看进程id对应的进程ps -ef|grep id
    print('')
    守护进程的启动
    from multiprocessing import Process
    
    def foo():
        print(123)
        time.sleep(1)
        print("end123")
    
    def bar():
        print(456)
        time.sleep(3)
        print("end456")
    
    
    p1=Process(target=foo)
    p2=Process(target=bar)
    
    p1.daemon=True
    p1.start()
    p2.start()
    time.sleep(0.1)
    print("main-------")#打印该行则主进程代码结束,则守护进程p1应该被终止.#可能会有p1任务执行的打印信息123,因为主进程打印main----时,p1也执行了,但是随即被终止.
    主进程代码执行结束守护进程立即结束

    socket聊天并发实例

    from socket import *
    from multiprocessing import Process
    
    server=socket(AF_INET,SOCK_STREAM)
    server.setsockopt(SOL_SOCKET,SO_REUSEADDR,1)
    server.bind(('127.0.0.1',8080))
    server.listen(5)
    
    def talk(conn,client_addr):
        while True:
            try:
                msg=conn.recv(1024)
                if not msg:break
                conn.send(msg.upper())
            except Exception:
                break
    
    if __name__ == '__main__': #windows下start进程一定要写到这下面
        while True:
            conn,client_addr=server.accept()
            p=Process(target=talk,args=(conn,client_addr))
            p.start()
    使用多进程实现socket聊天并发-server
    from socket import *
    
    client=socket(AF_INET,SOCK_STREAM)
    client.connect(('127.0.0.1',8080))
    
    
    while True:
        msg=input('>>: ').strip()
        if not msg:continue
    
        client.send(msg.encode('utf-8'))
        msg=client.recv(1024)
        print(msg.decode('utf-8'))
    client端

    多进程中的其他方法

    from multiprocessing import Process
    import time
    import random
    
    class Myprocess(Process):
        def __init__(self,person):
            self.name=person
            super().__init__()
    
        def run(self):
            print('%s正在和网红脸聊天' %self.name)
            time.sleep(random.randrange(1,5))
            print('%s还在和网红脸聊天' %self.name)
    
    
    p1=Myprocess('哪吒')
    p1.start()
    
    p1.terminate()#关闭进程,不会立即关闭,所以is_alive立刻查看的结果可能还是存活
    print(p1.is_alive()) #结果为True
    
    print('开始')
    print(p1.is_alive()) #结果为False
    进程对象的其他方法:terminate,is_alive
     1 class Myprocess(Process):
     2     def __init__(self,person):
     3         self.name=person   # name属性是Process中的属性,标示进程的名字
     4         super().__init__() # 执行父类的初始化方法会覆盖name属性
     5         #self.name = person # 在这里设置就可以修改进程名字了
     6         #self.person = person #如果不想覆盖进程名,就修改属性名称就可以了
     7     def run(self):
     8         print('%s正在和网红脸聊天' %self.name)
     9         # print('%s正在和网红脸聊天' %self.person)
    10         time.sleep(random.randrange(1,5))
    11         print('%s正在和网红脸聊天' %self.name)
    12         # print('%s正在和网红脸聊天' %self.person)
    13
    14
    15 p1=Myprocess('哪吒')
    16 p1.start()
    17 print(p1.pid)    #可以查看子进程的进程id
    进程对象的其他属性:pid和name

    互斥锁

    通过刚刚的学习,我们千方百计实现了程序的异步,让多个任务可以同时在几个进程中并发处理,他们之间的运行没有顺序,一旦开启也不受我们控制。尽管并发编程让我们能更加充分的利用IO资源,但是也给我们带来了新的问题。

    当多个进程使用同一份数据资源的时候,就会因为竞争而引发数据安全或顺序混乱问题。

    互斥锁介绍

    下面的代码演示了不同的任务争抢一个资源(终端输出)的场景。

    from multiprocessing import Process
    import time
    import random
    
    
    def task1():
        print('这是 task1 任务'.center(30, '-'))
        print('task1 进了洗手间')
        time.sleep(random.randint(1, 3))
        print('task1 办事呢...')
        time.sleep(random.randint(1, 3))
        print('task1 走出了洗手间')
    
    
    def task2():
        print('这是 task2 任务'.center(30, '-'))
        print('task2 进了洗手间')
        time.sleep(random.randint(1, 3))
        print('task2 办事呢...')
        time.sleep(random.randint(1, 3))
        print('task2 走出了洗手间')
    
    
    def task3():
        print('这是 task3 任务'.center(30, '-'))
        print('task3 进了洗手间')
        time.sleep(random.randint(1, 3))
        print('task3 办事呢...')
        time.sleep(random.randint(1, 3))
        print('task3 走出了洗手间')
    
    
    if __name__ == '__main__':
        p1 = Process(target=task1)
        p2 = Process(target=task2)
        p3 = Process(target=task3)
    
        p1.start()
        p2.start()
        p3.start()
    进程之间竞争资源

    通过加锁来控制。

    from multiprocessing import Process, Lock
    import time
    import random
    
    # 生成一个互斥锁
    mutex_lock = Lock()
    
    
    def task1(lock):
        # 锁门
        lock.acquire()
        print('这是 task1 任务'.center(30, '-'))
        print('task1 进了洗手间')
        time.sleep(random.randint(1, 3))
        print('task1 办事呢...')
        time.sleep(random.randint(1, 3))
        print('task1 走出了洗手间')
        # 释放锁
        lock.release()
    
    
    def task2(lock):
        # 锁门
        lock.acquire()
        print('这是 task2 任务'.center(30, '-'))
        print('task2 进了洗手间')
        time.sleep(random.randint(1, 3))
        print('task2 办事呢...')
        time.sleep(random.randint(1, 3))
        print('task2 走出了洗手间')
        # 释放锁
        lock.release()
    
    
    def task3(lock):
        # 锁门
        lock.acquire()
        print('这是 task3 任务'.center(30, '-'))
        print('task3 进了洗手间')
        time.sleep(random.randint(1, 3))
        print('task3 办事呢...')
        time.sleep(random.randint(1, 3))
        print('task3 走出了洗手间')
        # 释放锁
        lock.release()
    
    
    if __name__ == '__main__':
        p1 = Process(target=task1, args=(mutex_lock, ))
        p2 = Process(target=task2, args=(mutex_lock, ))
        p3 = Process(target=task3, args=(mutex_lock, ))
    
        # 释放新建进程的信号,具体谁先启动无法确定
        p1.start()
        p2.start()
        p3.start()
    使用互斥锁解决竞争

    上面这种情况虽然使用加锁的形式实现了顺序的执行,但是程序又重新变成串行了,这样确实会浪费了时间,却保证了数据的安全。

    互斥锁示例

    接下来,我们以模拟抢票为例,来看看数据安全的重要性。

    from multiprocessing import Process, Lock
    import json
    import time
    import random
    import os
    
    
    def search():
        time.sleep(0.5)
        with open('db.json', 'r', encoding='utf8') as f:
            data = json.load(f)
            print('剩余票数:{}'.format(data.get('count')))
    
    
    def buy():
    
        with open('db.json', 'r', encoding='utf8') as f:
            data = json.load(f)
        if data.get('count', 0) > 0:
            data['count'] -= 1
            time.sleep(random.randint(1, 3))
            with open('db.json', 'w', encoding='utf8') as f2:
                json.dump(data, f2)
            print('{}购票成功!'.format(os.getpid()))
        else:
            print('购票失败')
    
    
    def task():
        search()  # 查票并发
        buy()  # 串行买票
    
    
    if __name__ == '__main__':
        for i in range(10):
            p = Process(target=task)
            p.start()
    多进程同时抢票

    使用互斥锁,保证数据安全。

    from multiprocessing import Process, Lock
    import json
    import time
    import random
    import os
    
    # 设置互斥锁
    mutex_lock = Lock()
    
    
    def search():
        time.sleep(0.5)
        with open('db.json', 'r', encoding='utf8') as f:
            data = json.load(f)
            print('剩余票数:{}'.format(data.get('count')))
    
    
    def buy():
    
        with open('db.json', 'r', encoding='utf8') as f:
            data = json.load(f)
        if data.get('count', 0) > 0:
            data['count'] -= 1
            time.sleep(random.randint(1, 3))
            with open('db.json', 'w', encoding='utf8') as f2:
                json.dump(data, f2)
            print('{}购票成功!'.format(os.getpid()))
        else:
            print('购票失败')
    
    
    def task(lock):
        search()  # 查票并发
        lock.acquire()
        buy()  # 串行买票
        lock.release()
    
    
    if __name__ == '__main__':
        for i in range(10):
            p = Process(target=task, args=(mutex_lock, ))
            p.start()
    互斥锁版抢票 

    一点思考

    加锁可以保证多个进程修改同一块数据时,同一时间只能有一个任务可以进行修改,即串行的修改,在牺牲速度的前提下保证数据安全。
    虽然可以用文件共享数据实现进程间通信,但问题是:
    1. 效率低(共享数据基于文件,而文件是硬盘上的数据)
    2. 需要自己加锁处理

    因此我们最好找寻一种解决方案能够兼顾:
    1. 效率高(多个进程共享一块内存的数据)
    2. 帮我们处理好锁问题。
    mutiprocessing模块中为我们提供了一个IPC通信机制:队列和管道。
    队列和管道都是将数据存放于内存中,队列又是基于(管道+锁)实现的,可以让我们从复杂的锁问题中解脱出来,
    我们应该尽量避免使用共享数据,尽可能使用消息传递和队列,避免处理复杂的同步和锁问题,而且在进程数目增多时,往往可以获得更好的可扩展性。

    队列

    Queue介绍

    我们可以创建一个共享的进程队列,Queue是多进程安全的队列,可以使用Queue实现多进程之间的数据传递。

    Queue([maxsize])
    创建共享的进程队列。
    参数 :maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。
    底层队列使用管道和锁定实现。
    Queue([maxsize])
    创建共享的进程队列。maxsize是队列中允许的最大项数。如果省略此参数,则无大小限制。底层队列使用管道和锁定实现。另外,还需要运行支持线程以便队列中的数据传输到底层管道中。
    Queue的实例q具有以下方法:
    
    q.get( [ block [ ,timeout ] ] )
    返回q中的一个项目。如果q为空,此方法将阻塞,直到队列中有项目可用为止。block用于控制阻塞行为,默认为True. 如果设置为False,将引发Queue.Empty异常(定义在Queue模块中)。timeout是可选超时时间,用在阻塞模式中。如果在制定的时间间隔内没有项目变为可用,将引发Queue.Empty异常。
    
    q.get_nowait( )
    同q.get(False)方法。
    
    q.put(item [, block [,timeout ] ] )
    将item放入队列。如果队列已满,此方法将阻塞至有空间可用为止。block控制阻塞行为,默认为True。如果设置为False,将引发Queue.Empty异常(定义在Queue库模块中)。timeout指定在阻塞模式中等待可用空间的时间长短。超时后将引发Queue.Full异常。
    
    q.qsize()
    返回队列中目前项目的正确数量。此函数的结果并不可靠,因为在返回结果和在稍后程序中使用结果之间,队列中可能添加或删除了项目。在某些系统上,此方法可能引发NotImplementedError异常。
    
    
    q.empty()
    如果调用此方法时 q为空,返回True。如果其他进程或线程正在往队列中添加项目,结果是不可靠的。也就是说,在返回和使用结果之间,队列中可能已经加入新的项目。
    
    q.full()
    如果q已满,返回为True. 由于线程的存在,结果也可能是不可靠的(参考q.empty()方法)。。
    方法介绍
    q.close()
    关闭队列,防止队列中加入更多数据。调用此方法时,后台线程将继续写入那些已入队列但尚未写入的数据,但将在此方法完成时马上关闭。如果q被垃圾收集,将自动调用此方法。关闭队列不会在队列使用者中生成任何类型的数据结束信号或异常。例如,如果某个使用者正被阻塞在get()操作上,关闭生产者中的队列不会导致get()方法返回错误。
    
    q.cancel_join_thread()
    不会再进程退出时自动连接后台线程。这可以防止join_thread()方法阻塞。
    
    q.join_thread()
    连接队列的后台线程。此方法用于在调用q.close()方法后,等待所有队列项被消耗。默认情况下,此方法由不是q的原始创建者的所有进程调用。调用q.cancel_join_thread()方法可以禁止这种行为。
    其他方法(了解)

    队列简单示例

    '''
    multiprocessing模块支持进程间通信的两种主要形式:管道和队列
    都是基于消息传递实现的,但是队列接口
    '''
    
    from multiprocessing import Queue
    q=Queue(3)
    
    #put ,get ,put_nowait,get_nowait,full,empty
    q.put(3)
    q.put(3)
    q.put(3)
    # q.put(3)   # 如果队列已经满了,程序就会停在这里,等待数据被别人取走,再将数据放入队列。
               # 如果队列中的数据一直不被取走,程序就会永远停在这里。
    try:
        q.put_nowait(3) # 可以使用put_nowait,如果队列满了不会阻塞,但是会因为队列满了而报错。
    except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去,但是会丢掉这个消息。
        print('队列已经满了')
    
    # 因此,我们再放入数据之前,可以先看一下队列的状态,如果已经满了,就不继续put了。
    print(q.full()) #满了
    
    print(q.get())
    print(q.get())
    print(q.get())
    # print(q.get()) # 同put方法一样,如果队列已经空了,那么继续取就会出现阻塞。
    try:
        q.get_nowait(3) # 可以使用get_nowait,如果队列满了不会阻塞,但是会因为没取到值而报错。
    except: # 因此我们可以用一个try语句来处理这个错误。这样程序不会一直阻塞下去。
        print('队列已经空了')
    
    print(q.empty()) #空了
    单看队列用法

    上面这个例子还没有加入进程通信,只是先来看看队列为我们提供的方法,以及这些方法的使用和现象。

    import time
    from multiprocessing import Process, Queue
    
    def f(q):
        q.put([time.asctime(), 'hi', 'hello'])  #调用主函数中p进程传递过来的进程参数 put函数为向队列中添加一条数据。
    
    if __name__ == '__main__':
        q = Queue() #创建一个Queue对象
        p = Process(target=f, args=(q,)) #创建一个进程
        p.start()
        print(q.get())
        p.join()
    子进程发送数据给父进程

    上面是一个queue的简单应用,使用队列q对象调用get函数来取得队列中最先进入的数据。 接下来看一个稍微复杂一些的例子:

    import os
    import time
    import multiprocessing
    
    # 向queue中输入数据的函数
    def inputQ(queue):
        info = str(os.getpid()) + '(put):' + str(time.asctime())
        queue.put(info)
    
    # 向queue中输出数据的函数
    def outputQ(queue):
        info = queue.get()
        print ('%s%s\033[32m%s\033[0m'%(str(os.getpid()), '(get):',info))
    
    # Main
    if __name__ == '__main__':
        multiprocessing.freeze_support()
        record1 = []   # store input processes
        record2 = []   # store output processes
        queue = multiprocessing.Queue(3)
    
        # 输入进程
        for i in range(10):
            process = multiprocessing.Process(target=inputQ,args=(queue,))
            process.start()
            record1.append(process)
    
        # 输出进程
        for i in range(10):
            process = multiprocessing.Process(target=outputQ,args=(queue,))
            process.start()
            record2.append(process)
    
        for p in record1:
            p.join()
    
        for p in record2:
            p.join()
    批量生产数据放入队列再批量获取结果 x

    生产者消费者模型

    在并发编程中使用生产者和消费者模式能够解决绝大多数并发问题。该模式通过平衡生产数据和消费数据的工作能力来提高程序的整体处理数据的速度。

    为什么要使用生产者和消费者模式

    生产者就是生产数据的一方,消费者就是消费数据的一方。通常生产者和消费者的能力很难协调,例如:如果生产者处理速度很快,而消费者处理速度很慢,那么生产者就必须等待消费者处理完,才能继续生产数据。同样的道理,如果消费者的处理能力大于生产者,那么消费者就必须等待生产者。为了解决这个问题于是引入了生产者和消费者模式。

    什么是生产者消费者模式

    生产者消费者模式是通过一个容器来解决生产者和消费者的强耦合问题。生产者和消费者彼此之间不直接通讯,而通过阻塞队列来进行通讯,所以生产者生产完数据之后不用等待消费者处理,直接扔给阻塞队列,消费者不找生产者要数据,而是直接从阻塞队列里取,阻塞队列就相当于一个缓冲区,平衡了生产者和消费者的处理能力。

    基于队列实现生产者消费者模型

    from multiprocessing import Process,Queue
    import time,random,os
    def consumer(q):
        while True:
            res=q.get()
            time.sleep(random.randint(1,3))
            print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
    
    def producer(q):
        for i in range(10):
            time.sleep(random.randint(1,3))
            res='包子%s' %i
            q.put(res)
            print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
    
    if __name__ == '__main__':
        q=Queue()
        #生产者们:即厨师们
        p1=Process(target=producer,args=(q,))
    
        #消费者们:即吃货们
        c1=Process(target=consumer,args=(q,))
    
        #开始
        p1.start()
        c1.start()
        print('')
    基于队列实现生产者消费者模型

    此时的问题是主进程永远不会结束,原因是:生产者p在生产完后就结束了,但是消费者c在取空了q之后,则一直处于死循环中且卡在q.get()这一步。

    解决方式无非是让生产者在生产完毕后,往队列中再发一个结束信号,这样消费者在接收到结束信号后就可以break出死循环。

    from multiprocessing import Process,Queue
    import time,random,os
    def consumer(q):
        while True:
            res=q.get()
            if res is None:break #收到结束信号则结束
            time.sleep(random.randint(1,3))
            print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
    
    def producer(q):
        for i in range(10):
            time.sleep(random.randint(1,3))
            res='包子%s' %i
            q.put(res)
            print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
        q.put(None) #发送结束信号
    if __name__ == '__main__':
        q=Queue()
        #生产者们:即厨师们
        p1=Process(target=producer,args=(q,))
    
        #消费者们:即吃货们
        c1=Process(target=consumer,args=(q,))
    
        #开始
        p1.start()
        c1.start()
        print('')
    改良版——生产者消费者模型

    注意:结束信号None,不一定要由生产者发,主进程里同样可以发,但主进程需要等生产者结束后才应该发送该信号

    from multiprocessing import Process,Queue
    import time,random,os
    def consumer(q):
        while True:
            res=q.get()
            if res is None:break #收到结束信号则结束
            time.sleep(random.randint(1,3))
            print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
    
    def producer(q):
        for i in range(2):
            time.sleep(random.randint(1,3))
            res='包子%s' %i
            q.put(res)
            print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
    
    if __name__ == '__main__':
        q=Queue()
        #生产者们:即厨师们
        p1=Process(target=producer,args=(q,))
    
        #消费者们:即吃货们
        c1=Process(target=consumer,args=(q,))
    
        #开始
        p1.start()
        c1.start()
    
        p1.join()
        q.put(None) #发送结束信号
        print('')
    主进程在生产者生产完毕后发送结束信号None

    但上述解决方式,在有多个生产者和多个消费者时,我们则需要用一个很low的方式去解决

    from multiprocessing import Process,Queue
    import time,random,os
    def consumer(q):
        while True:
            res=q.get()
            if res is None:break #收到结束信号则结束
            time.sleep(random.randint(1,3))
            print('\033[45m%s 吃 %s\033[0m' %(os.getpid(),res))
    
    def producer(name,q):
        for i in range(2):
            time.sleep(random.randint(1,3))
            res='%s%s' %(name,i)
            q.put(res)
            print('\033[44m%s 生产了 %s\033[0m' %(os.getpid(),res))
    
    if __name__ == '__main__':
        q=Queue()
        #生产者们:即厨师们
        p1=Process(target=producer,args=('包子',q))
        p2=Process(target=producer,args=('骨头',q))
        p3=Process(target=producer,args=('泔水',q))
    
        #消费者们:即吃货们
        c1=Process(target=consumer,args=(q,))
        c2=Process(target=consumer,args=(q,))
    
        #开始
        p1.start()
        p2.start()
        p3.start()
        c1.start()
    
        p1.join() #必须保证生产者全部生产完毕,才应该发送结束信号
        p2.join()
        p3.join()
        q.put(None) #有几个消费者就应该发送几次结束信号None
        q.put(None) #发送结束信号
        print('')
    多个消费者的例子:有几个消费者就需要发送几次结束信号

    线程

    线程介绍

    有了进程为什么要有线程

    进程有很多优点,它提供了多道编程,让我们感觉我们每个人都拥有自己的CPU和其他资源,可以提高计算机的利用率。很多人就不理解了,既然进程这么优秀,为什么还要线程呢?其实,仔细观察就会发现进程还是有很多缺陷的,主要体现在两点上:

    • 进程只能在一个时间干一件事,如果想同时干两件事或多件事,进程就无能为力了。

    • 进程在执行的过程中如果阻塞,例如等待输入,整个进程就会挂起,即使进程中有些工作不依赖于输入的数据,也将无法执行。

    如果这两个缺点理解比较困难的话,举个现实的例子也许你就清楚了:如果把我们上课的过程看成一个进程的话,那么我们要做的是耳朵听老师讲课,手上还要记笔记,脑子还要思考问题,这样才能高效的完成听课的任务。而如果只提供进程这个机制的话,上面这三件事将不能同时执行,同一时间只能做一件事,听的时候就不能记笔记,也不能用脑子思考,这是其一;如果老师在黑板上写演算过程,我们开始记笔记,而老师突然有一步推不下去了,阻塞住了,他在那边思考着,而我们呢,也不能干其他事,即使你想趁此时思考一下刚才没听懂的一个问题都不行,这是其二。

    现在你应该明白了进程的缺陷了,而解决的办法很简单,我们完全可以让听、写、思三个独立的过程,并行起来,这样很明显可以提高听课的效率。而实际的操作系统中,也同样引入了这种类似的机制——线程。

    线程的出现

    60年代,在OS中能拥有资源和独立运行的基本单位是进程,然而随着计算机技术的发展,进程出现了很多弊端,一是由于进程是资源拥有者,创建、撤消与切换存在较大的时空开销,因此需要引入 轻型进程 ;二是由于对称多处理机(SMP)出现, 可以满足多个运行单位 ,而多个进程并行开销过大。
    因此在80年代,出现了 能独立运行的基本单位 ——线程(Threads)
    注意:进程是资源分配的最小单位,线程是CPU调度的最小单位.
    每一个进程中至少有一个线程。

    进程和线程的关系

    线程与进程的区别 可以归纳为以下4点:
    1)地址空间和其它资源(如打开文件):进程间相互独立,同一进程的各线程间共享。某进程内的线程在其它进程不可见。
    2)通信: 进程间通信 IPC ,线程间可以直接读写进程数据段(如全局变量)来进行通信——需要 进程同步 和互斥手段的辅助,以保证数据的一致性。
    3)调度和切换:线程上下文切换比进程上下文切换要快得多。
    4)在多线程操作系统中,进程不是一个可执行的实体。

    线程的特点

    在多线程的操作系统中,通常是在一个进程中包括多个线程,每个线程都是作为利用CPU的基本单位,是花费最小开销的实体。线程具有以下属性。
    1)轻型实体
    线程中的实体基本上不拥有系统资源,只是有一点必不可少的、能保证独立运行的资源。
    线程的实体包括程序、数据和TCB。线程是动态概念,它的动态特性由线程控制块TCB(Thread Control Block)描述。
    TCB包括以下信息:
    (1)线程状态。
    (2)当线程不运行时,被保存的现场资源。
    (3)一组执行堆栈。
    (4)存放每个线程的局部变量主存区。
    (5)访问同一个进程中的主存和其它资源。
    用于指示被执行指令序列的程序计数器、保留局部变量、少数状态参数和返回地址等的一组寄存器和堆栈。
    TCB包括以下信息
    2)独立调度和分派的基本单位。
    在多线程OS中,线程是能独立运行的基本单位,因而也是独立调度和分派的基本单位。由于线程很“轻”,故线程的切换非常迅速且开销小(在同一进程中的)。
    3)共享进程资源。
    线程在同一进程中的各个线程,都可以共享该进程所拥有的资源,这首先表现在:所有线程都具有相同的进程id,这意味着,线程可以访问该进程的每一个内存资源;此外,还可以访问进程所拥有的已打开文件、定时器、信号量机构等。由于同一个进程内的线程共享内存和文件,所以线程之间互相通信不必调用内核。
    4 )可并发执行。
    在一个进程中的多个线程之间,可以并发执行,甚至允许在一个进程中所有线程都能并发执行;同样,不同进程中的线程也能并发执行,充分利用和发挥了处理机与外围设备并行工作的能力。

    内存中的线程

     

    多个线程共享同一个进程的地址空间中的资源,是对一台计算机上多个进程的模拟,有时也称线程为轻量级的进程。

    而对一台计算机上多个进程,则共享物理内存、磁盘、打印机等其他物理资源。多线程的运行也多进程的运行类似,是cpu在多个线程之间的快速切换。

    不同的进程之间是充满敌意的,彼此是抢占、竞争cpu的关系,如果迅雷会和QQ抢资源。而同一个进程是由一个程序员的程序创建,所以同一进程内的线程是合作关系,一个线程可以访问另外一个线程的内存地址,大家都是共享的,一个线程干死了另外一个线程的内存,那纯属程序员脑子有问题。

    类似于进程,每个线程也有自己的堆栈,不同于进程,线程库无法利用时钟中断强制线程让出CPU,可以调用thread_yield运行线程自动放弃cpu,让另外一个线程运行。

    线程通常是有益的,但是带来了不小程序设计难度,线程的问题是:

    1. 父进程有多个线程,那么开启的子线程是否需要同样多的线程

    2. 在同一个进程中,如果一个线程关闭了文件,而另外一个线程正准备往该文件内写内容呢?

    因此,在多线程的代码中,需要更多的心思来设计程序的逻辑、保护程序的数据。

    在python中使用线程

    threading模块

    multiprocess模块的完全模仿了threading模块的接口,二者在使用层面,有很大的相似性,因而不再详细介绍( 官方链接

    线程的创建

    from threading import Thread
    import time
    def sayhi(name):
        time.sleep(2)
        print('%s say hello' %name)
    
    if __name__ == '__main__':
        t=Thread(target=sayhi,args=('egon',))
        t.start()
        print('主线程')
    创建线程的方式1
    from threading import Thread
    import time
    class Sayhi(Thread):
        def __init__(self,name):
            super().__init__()
            self.name=name
        def run(self):
            time.sleep(2)
            print('%s say hello' % self.name)
    
    
    if __name__ == '__main__':
        t = Sayhi('egon')
        t.start()
        print('主线程')
    创建线程的方式2

    多线程与多进程

    from threading import Thread
    from multiprocessing import Process
    import os
    
    def work():
        print('hello',os.getpid())
    
    if __name__ == '__main__':
        #part1:在主进程下开启多个线程,每个线程都跟主进程的pid一样
        t1=Thread(target=work)
        t2=Thread(target=work)
        t1.start()
        t2.start()
        print('主线程/主进程pid',os.getpid())
    
        #part2:开多个进程,每个进程都有不同的pid
        p1=Process(target=work)
        p2=Process(target=work)
        p1.start()
        p2.start()
        print('主线程/主进程pid',os.getpid())
    pid的比较
    from threading import Thread
    from multiprocessing import Process
    import os
    
    def work():
        print('hello')
    
    if __name__ == '__main__':
        #在主进程下开启线程
        t=Thread(target=work)
        t.start()
        print('主线程/主进程')
        '''
        打印结果:
        hello
        主线程/主进程
        '''
    
        #在主进程下开启子进程
        t=Process(target=work)
        t.start()
        print('主线程/主进程')
        '''
        打印结果:
        主线程/主进程
        hello
        '''
    开启效率的较量
    from  threading import Thread
    from multiprocessing import Process
    import os
    def work():
        global n
        n=0
    
    if __name__ == '__main__':
        # n=100
        # p=Process(target=work)
        # p.start()
        # p.join()
        # print('主',n) #毫无疑问子进程p已经将自己的全局的n改成了0,但改的仅仅是它自己的,查看父进程的n仍然为100
    
    
        n=1
        t=Thread(target=work)
        t.start()
        t.join()
        print('',n) #查看结果为0,因为同一进程内的线程之间共享进程内的数据
    同一进程内的线程共享该进程的数据?
    内存数据的共享问题

    练习 :多线程实现socket

    #_*_coding:utf-8_*_
    #!/usr/bin/env python
    import multiprocessing
    import threading
    
    import socket
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.bind(('127.0.0.1',8080))
    s.listen(5)
    
    def action(conn):
        while True:
            data=conn.recv(1024)
            print(data)
            conn.send(data.upper())
    
    if __name__ == '__main__':
    
        while True:
            conn,addr=s.accept()
    
    
            p=threading.Thread(target=action,args=(conn,))
            p.start()
    server
    #_*_coding:utf-8_*_
    #!/usr/bin/env python
    
    
    import socket
    
    s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    s.connect(('127.0.0.1',8080))
    
    while True:
        msg=input('>>: ').strip()
        if not msg:continue
    
        s.send(msg.encode('utf-8'))
        data=s.recv(1024)
        print(data)
    client

    Thread类的其他方法

    Thread实例对象的方法
      # isAlive(): 返回线程是否活动的。
      # getName(): 返回线程名。
      # setName(): 设置线程名。
    
    threading模块提供的一些方法:
      # threading.currentThread(): 返回当前的线程变量。
      # threading.enumerate(): 返回一个包含正在运行的线程的list。正在运行指线程启动后、结束前,不包括启动前和终止后的线程。
      # threading.activeCount(): 返回正在运行的线程数量,与len(threading.enumerate())有相同的结果。
    from threading import Thread
    import threading
    from multiprocessing import Process
    import os
    
    def work():
        import time
        time.sleep(3)
        print(threading.current_thread().getName())
    
    
    if __name__ == '__main__':
        #在主进程下开启线程
        t=Thread(target=work)
        t.start()
    
        print(threading.current_thread().getName())
        print(threading.current_thread()) #主线程
        print(threading.enumerate()) #连同主线程在内有两个运行的线程
        print(threading.active_count())
        print('主线程/主进程')
    
        '''
        打印结果:
        MainThread
        <_MainThread(MainThread, started 140735268892672)>
        [<_MainThread(MainThread, started 140735268892672)>, <Thread(Thread-1, started 123145307557888)>]
        主线程/主进程
        Thread-1
        '''
    代码示例
    from threading import Thread
    import time
    def sayhi(name):
        time.sleep(2)
        print('%s say hello' %name)
    
    if __name__ == '__main__':
        t=Thread(target=sayhi,args=('egon',))
        t.start()
        t.join()
        print('主线程')
        print(t.is_alive())
        '''
        egon say hello
        主线程
        False
        '''
    join方法

    守护线程

    无论是进程还是线程,都遵循:守护xx会等待主xx运行完毕后被销毁。 需要强调的是:运行完毕并非终止运行

    #1.对主进程来说,运行完毕指的是主进程代码运行完毕
    #2.对主线程来说,运行完毕指的是主线程所在的进程内所有非守护线程统统运行完毕,主线程才算运行完毕
    #1 主进程在其代码结束后就已经算运行完毕了(守护进程在此时就被回收),然后主进程会一直等非守护的子进程都运行完毕后回收子进程的资源(否则会产生僵尸进程),才会结束,
    #2 主线程在其他非守护线程运行完毕后才算运行完毕(守护线程在此时就被回收)。因为主线程的结束意味着进程的结束,进程整体的资源都将被回收,而进程必须保证非守护线程都运行完毕后才能结束。
    详细解释
    from threading import Thread
    import time
    def sayhi(name):
        time.sleep(2)
        print('%s say hello' %name)
    
    if __name__ == '__main__':
        t=Thread(target=sayhi,args=('egon',))
        t.setDaemon(True) #必须在t.start()之前设置
        t.start()
    
        print('主线程')
        print(t.is_alive())
        '''
        主线程
        True
        '''
    守护线程例1
    from threading import Thread
    import time
    def foo():
        print(123)
        time.sleep(1)
        print("end123")
    
    def bar():
        print(456)
        time.sleep(3)
        print("end456")
    
    
    t1=Thread(target=foo)
    t2=Thread(target=bar)
    
    t1.daemon=True
    t1.start()
    t2.start()
    print("main-------")
    守护线程例2

    同步锁

    from threading import Thread
    import os,time
    def work():
        global n
        temp=n
        time.sleep(0.1)
        n=temp-1
    if __name__ == '__main__':
        n=100
        l=[]
        for i in range(100):
            p=Thread(target=work)
            l.append(p)
            p.start()
        for p in l:
            p.join()
    
        print(n) #结果可能为99
    多个线程抢占资源的情况
    import threading
    R=threading.Lock()
    R.acquire()
    '''
    对公共数据的操作
    '''
    R.release()
    from threading import Thread,Lock
    import os,time
    def work():
        global n
        lock.acquire()
        temp=n
        time.sleep(0.1)
        n=temp-1
        lock.release()
    if __name__ == '__main__':
        lock=Lock()
        n=100
        l=[]
        for i in range(100):
            p=Thread(target=work)
            l.append(p)
            p.start()
        for p in l:
            p.join()
    
        print(n) #结果肯定为0,由原来的并发执行变成串行,牺牲了执行效率保证了数据安全
    同步锁的引用
    #不加锁:并发执行,速度快,数据不安全
    from threading import current_thread,Thread,Lock
    import os,time
    def task():
        global n
        print('%s is running' %current_thread().getName())
        temp=n
        time.sleep(0.5)
        n=temp-1
    
    
    if __name__ == '__main__':
        n=100
        lock=Lock()
        threads=[]
        start_time=time.time()
        for i in range(100):
            t=Thread(target=task)
            threads.append(t)
            t.start()
        for t in threads:
            t.join()
    
        stop_time=time.time()
        print('主:%s n:%s' %(stop_time-start_time,n))
    
    '''
    Thread-1 is running
    Thread-2 is running
    ......
    Thread-100 is running
    主:0.5216062068939209 n:99
    '''
    
    
    #不加锁:未加锁部分并发执行,加锁部分串行执行,速度慢,数据安全
    from threading import current_thread,Thread,Lock
    import os,time
    def task():
        #未加锁的代码并发运行
        time.sleep(3)
        print('%s start to run' %current_thread().getName())
        global n
        #加锁的代码串行运行
        lock.acquire()
        temp=n
        time.sleep(0.5)
        n=temp-1
        lock.release()
    
    if __name__ == '__main__':
        n=100
        lock=Lock()
        threads=[]
        start_time=time.time()
        for i in range(100):
            t=Thread(target=task)
            threads.append(t)
            t.start()
        for t in threads:
            t.join()
        stop_time=time.time()
        print('主:%s n:%s' %(stop_time-start_time,n))
    
    '''
    Thread-1 is running
    Thread-2 is running
    ......
    Thread-100 is running
    主:53.294203758239746 n:0
    '''
    
    #有的同学可能有疑问:既然加锁会让运行变成串行,那么我在start之后立即使用join,就不用加锁了啊,也是串行的效果啊
    #没错:在start之后立刻使用jion,肯定会将100个任务的执行变成串行,毫无疑问,最终n的结果也肯定是0,是安全的,但问题是
    #start后立即join:任务内的所有代码都是串行执行的,而加锁,只是加锁的部分即修改共享数据的部分是串行的
    #单从保证数据安全方面,二者都可以实现,但很明显是加锁的效率更高.
    from threading import current_thread,Thread,Lock
    import os,time
    def task():
        time.sleep(3)
        print('%s start to run' %current_thread().getName())
        global n
        temp=n
        time.sleep(0.5)
        n=temp-1
    
    
    if __name__ == '__main__':
        n=100
        lock=Lock()
        start_time=time.time()
        for i in range(100):
            t=Thread(target=task)
            t.start()
            t.join()
        stop_time=time.time()
        print('主:%s n:%s' %(stop_time-start_time,n))
    
    '''
    Thread-1 start to run
    Thread-2 start to run
    ......
    Thread-100 start to run
    主:350.6937336921692 n:0 #耗时是多么的恐怖
    '''
    互斥锁与join的区别

    死锁与可重入锁

    进程也有死锁与可重入锁,使用方法都是一样的,所以放到这里一起说:

    所谓死锁: 是指两个或两个以上的进程或线程在执行过程中,因争夺资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁,这些永远在互相等待的进程称为死锁进程,如下就是死锁。

    from threading import Lock as Lock
    import time
    mutexA=Lock()
    mutexA.acquire()
    mutexA.acquire()
    print(123)
    mutexA.release()
    mutexA.release()
    死锁

    解决方法:使用可重入锁,在Python中为了支持在同一线程中多次请求同一资源,提供了可重入锁RLock。

    这个RLock内部维护着一个Lock和一个counter变量,counter记录了acquire的次数,从而使得资源可以被多次require。直到一个线程所有的acquire都被release,其他的线程才能获得资源。上面的例子如果使用RLock代替Lock,则不会发生死锁:

    from threading import RLock
    import time
    mutexA=RLock()
    mutexA.acquire()
    mutexA.acquire()
    print(123)
    mutexA.release()
    mutexA.release()
    可重入锁示例

    典型问题:科学家吃面

    import time
    from threading import Thread,Lock
    noodle_lock = Lock()
    fork_lock = Lock()
    def eat1(name):
        noodle_lock.acquire()
        print('%s 抢到了面条'%name)
        fork_lock.acquire()
        print('%s 抢到了叉子'%name)
        print('%s 吃面'%name)
        fork_lock.release()
        noodle_lock.release()
    
    def eat2(name):
        fork_lock.acquire()
        print('%s 抢到了叉子' % name)
        time.sleep(1)
        noodle_lock.acquire()
        print('%s 抢到了面条' % name)
        print('%s 吃面' % name)
        noodle_lock.release()
        fork_lock.release()
    
    for name in ['哪吒','egon','yuan']:
        t1 = Thread(target=eat1,args=(name,))
        t2 = Thread(target=eat2,args=(name,))
        t1.start()
        t2.start()
    死锁问题
    import time
    from threading import Thread,RLock
    fork_lock = noodle_lock = RLock()
    def eat1(name):
        noodle_lock.acquire()
        print('%s 抢到了面条'%name)
        fork_lock.acquire()
        print('%s 抢到了叉子'%name)
        print('%s 吃面'%name)
        fork_lock.release()
        noodle_lock.release()
    
    def eat2(name):
        fork_lock.acquire()
        print('%s 抢到了叉子' % name)
        time.sleep(1)
        noodle_lock.acquire()
        print('%s 抢到了面条' % name)
        print('%s 吃面' % name)
        noodle_lock.release()
        fork_lock.release()
    
    for name in ['哪吒','egon','yuan']:
        t1 = Thread(target=eat1,args=(name,))
        t2 = Thread(target=eat2,args=(name,))
        t1.start()
        t2.start()
    可重入锁解决死锁问题

    全局解释器锁GIL

    GIL介绍

    首先需要明确的一点是GIL并不是Python的特性,它是在实现Python解析器(CPython)时所引入的一个概念。

    Python也一样,同样一段代码可以通过CPython,PyPy,Psyco等不同的Python执行环境来执行。像其中的JPython就没有GIL。然而因为CPython是大部分环境下默认的Python执行环境。所以在很多人的概念里CPython就是Python,也就想当然的把GIL归结为Python语言的缺陷。所以这里要先明确一点:GIL并不是Python的特性,Python完全可以不依赖于GIL。

    简单来说,在Cpython解释器中,因为有GIL锁的存在同一个进程下开启的多线程,同一时刻只能有一个线程执行,无法利用多核优势。

    常见问题1

    我们有了GIL锁为什么还要自己在代码中加锁呢?

    GIL保护的是Python解释器级别的数据资源,自己代码中的数据资源就需要自己加锁防止竞争。如下图:

    常见问题2

    有了GIL的存在,同一时刻同一进程中只有一个线程被执行,进程可以利用多核,但是开销大,而Python的多线程开销小,但却无法利用多核优势,也就是说Python这语言难堪大用。

    其实编程所解决的现实问题大致分为IO密集型和计算密集型。

    对于IO密集型的场景,Python的多线程编程完全OK,而对于计算密集型的场景,Python中有很多成熟的模块或框架如Pandas等能够提高计算效率。

     

    推荐阅读:理解GIL

    池 —— concurrent.futures

    Python标准模块--concurrent.futures

    https://docs.python.org/dev/library/concurrent.futures.html

    concurrent.futures模块提供了高度封装的异步调用接口,其中:

    ThreadPoolExecutor:线程池

    ProcessPoolExecutor: 进程池

    借助上面两个类,我们可以很方便地创建进程池对象和线程池对象。

    p_pool = ProcessPoolExecutor(max_workers=5)  # 创建一个最多5个woker的进程池
    t_pool = ThreadPoolExecutor(max_workers=5)  # 创建一个最多5个woker的线程池

    可用方法介绍:

    # 基本方法
    #submit(fn, *args, **kwargs)
    提交任务
    
    # map(func, *iterables, timeout=None, chunksize=1) 
    取代for循环submit的操作
    
    # shutdown(wait=True) 
    相当于进程池的pool.close()+pool.join()操作
    wait=True,等待池内所有任务执行完毕回收完资源后才继续
    wait=False,立即返回,并不会等待池内的任务执行完毕
    但不管wait参数为何值,整个程序都会等到所有任务执行完毕
    submit和map必须在shutdown之前
    
    # result(timeout=None)
    取得结果
    
    # add_done_callback(fn)
    回调函数
    #介绍
    The ProcessPoolExecutor class is an Executor subclass that uses a pool of processes to execute calls asynchronously. ProcessPoolExecutor uses the multiprocessing module, which allows it to side-step the Global Interpreter Lock but also means that only picklable objects can be executed and returned.
    
    class concurrent.futures.ProcessPoolExecutor(max_workers=None, mp_context=None)
    An Executor subclass that executes calls asynchronously using a pool of at most max_workers processes. If max_workers is None or not given, it will default to the number of processors on the machine. If max_workers is lower or equal to 0, then a ValueError will be raised.
    
    
    #用法
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    
    import os,time,random
    def task(n):
        print('%s is runing' %os.getpid())
        time.sleep(random.randint(1,3))
        return n**2
    
    if __name__ == '__main__':
    
        executor=ProcessPoolExecutor(max_workers=3)
    
        futures=[]
        for i in range(11):
            future=executor.submit(task,i)
            futures.append(future)
        executor.shutdown(True)
        print('+++>')
        for future in futures:
            print(future.result())
    ProcessPoolExecutor
    #介绍
    ThreadPoolExecutor is an Executor subclass that uses a pool of threads to execute calls asynchronously.
    class concurrent.futures.ThreadPoolExecutor(max_workers=None, thread_name_prefix='')
    An Executor subclass that uses a pool of at most max_workers threads to execute calls asynchronously.
    
    Changed in version 3.5: If max_workers is None or not given, it will default to the number of processors on the machine, multiplied by 5, assuming that ThreadPoolExecutor is often used to overlap I/O instead of CPU work and the number of workers should be higher than the number of workers for ProcessPoolExecutor.
    
    New in version 3.6: The thread_name_prefix argument was added to allow users to control the threading.Thread names for worker threads created by the pool for easier debugging.
    
    #用法
    与ProcessPoolExecutor相同
    ThreadPoolExecutor

    使用线程池改进网络编程的例子:

    import socket
    from concurrent.futures import ThreadPoolExecutor
    
    t_pool = ThreadPoolExecutor(max_workers=3)
    
    server = socket.socket()
    server.bind(('127.0.0.1', 8080))
    server.listen(5)
    
    
    def comm(conn):
        while 1:
            try:
                data = conn.recv(1024)
                if not data:
                    break
                conn.send(data.upper())
            except ConnectionResetError:
                break
        conn.close()
    
    
    def serve():
        while 1:
            conn, addr = server.accept()
            t_pool.submit(comm, conn)
        server.close()
    
    
    if __name__ == '__main__':
        serve()
    server端
    import socket
    client = socket.socket()
    client.connect(('127.0.0.1', 8080))
    
    while 1:
        msg = input('>>>').strip()
        if not msg: continue
        client.send(msg.encode('utf8'))
        data = client.recv(1024)
        print(data.decode('utf8'))
    
    client.close()
    client端

    其他方法:

    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    
    import os,time,random
    def task(n):
        print('%s is runing' %os.getpid())
        time.sleep(random.randint(1,3))
        return n**2
    
    if __name__ == '__main__':
    
        executor=ThreadPoolExecutor(max_workers=3)
    
        # for i in range(11):
        #     future=executor.submit(task,i)
    
        executor.map(task,range(1,12)) #map取代了for+submit
    map的用法
    from concurrent.futures import ThreadPoolExecutor,ProcessPoolExecutor
    from multiprocessing import Pool
    import requests
    import json
    import os
    
    def get_page(url):
        print('<进程%s> get %s' %(os.getpid(),url))
        respone=requests.get(url)
        if respone.status_code == 200:
            return {'url':url,'text':respone.text}
    
    def parse_page(res):
        res=res.result()
        print('<进程%s> parse %s' %(os.getpid(),res['url']))
        parse_res='url:<%s> size:[%s]\n' %(res['url'],len(res['text']))
        with open('db.txt','a') as f:
            f.write(parse_res)
    
    
    if __name__ == '__main__':
        urls=[
            'https://www.baidu.com',
            'https://www.python.org',
            'https://www.openstack.org',
            'https://help.github.com/',
            'http://www.sina.com.cn/'
        ]
    
        # p=Pool(3)
        # for url in urls:
        #     p.apply_async(get_page,args=(url,),callback=pasrse_page)
        # p.close()
        # p.join()
    
        p=ProcessPoolExecutor(3)
        for url in urls:
            p.submit(get_page,url).add_done_callback(parse_page) #parse_page拿到的是一个future对象obj,需要用obj.result()拿到结果
    回调函数

     

    转载于:https://www.cnblogs.com/liwenzhou/p/9661463.html

    展开全文
  • python作业周末班0325

    2018-03-29 09:47:09
    s=raw_input("请输入字符串:") li=(list(s)) if len(s)&gt;100 or not s.isalpha(): print "Error" for i in range(len(s)): for j in range(i+1,len(s)): if li[i]==li[j]: ...pr...

    这里写图片描述

    s=raw_input("请输入字符串:")
    li=(list(s))
    if len(s)>100 or not s.isalpha():
        print "Error"
    for i in range(len(s)):
        for j in range(i+1,len(s)):
            if li[i]==li[j]:
                li[j]=" "
    print (" ".join(li)).replace(" "," ")
    

    这里写图片描述

    num = input("请输入一个正整数:")
    count = 1
    for i in range(2, num + 1):
        if (num % i) == 0:
            count += 1
    if count > 2:
        print "yes"
    else:
         print "no"

    这里写图片描述

    a='hello xiao mi'
    b=a.split()
    c=b[::-1]
    print " ".join(c)
    

    这里写图片描述

    while True:
        a = raw_input("第一串字符:")
        b = raw_input("第二串字符:")
        for i in b:
            a=a.replace(i,"")
        else:
            print a

    这里写图片描述

    word = raw_input("请输入单词")
    if len(word) > 100:
        print "Beyond Max Length"
        exit()
    if not word.isupper():
        print "dislike"
        exit()
    li = list(word)
    for i in range(0,len(word)+1):
        if li[i] == li[i - 1]:
                print "dislike"
                break
    else:
         print "like"
    

    1.python中如何让列表中的姓名按行输出, names = [‘fenytiao’, ‘fendai’, ‘fensi’]:
    A. print ‘\n’.join(names)
    B. print names.join(‘\n’)
    C. print names.append(‘\n’)
    D. print(‘\n’.join(names))
    E. print(names.join(“%s\n”, names))
    F. print(names.join(‘\n’))
    答案:D

    2.下面代码的执行结果为():
    代码:
    url = ‘westos.org’
    url[-3:-1]=”.com”
    A. ‘westos.com’
    B. ‘westos’
    C. ‘westos.moc’
    D. 无显示
    E. 报错
    答案:E

    3.下面代码的执行结果为:
    li = [1, 2, 3]
    li.append(4)+list(‘abc’)
    print li
    A. [1, 2, 3, 4, ‘abc’]
    B. [1, 2, 3, 4, ‘a’, ‘b’, ‘c’]
    C. [1, 2, 3, [4], ‘a’, ‘b’, ‘c’]D. [1, 2, 3, 4, a, b, c]
    E. 异常
    答案:E

    4.python中缩进表示语法边界, 一般建议一个缩进为 _ 4__ 个空格?

    5.100-25*3%4输出结果为()
    A. 1
    B. 0
    C. 25
    D. 97
    答案:97

    6.如何获取字符串s=’python’的从右向左的第2个字符()
    A. s[3]
    B. s[2]
    C. s[-2]
    D. s[-2:-1]
    E. s[3:4]
    答案:C,D

    7.strip的作用是 , ljust的作用是 __ , split的作用是 _ .
    答案:删除两边空格;向左对齐;分割字符串

    8.append的作用是 _ , insert(3,’a’)的作用是 _ , extend([1,2,3])的作用是 ;
    答案:在数组中追加元素;在数组的第四个位置追加’a’元素;在数组中再扩展一个为【1,2,3】的数组

    9.urls= [‘http://www.baidu.com‘, ‘https://www.westos.org‘, ‘file:///hello.txt’,
    https://www.westos.com‘] 找出该列表中协议为https并且所在域为.com的url地址.

    答案:

    urls = ['http://www.baidu.com', 'http://www.westos.org', 'file:///hello.txt', 'https://www.westos.com']
    for i in urls:
        if i.startswith('https') and i.endswith('.com'):
            print i

    10.python中的布尔类型有 ___ ;
    答案:True ;False

    11.常见的python解释器有(至少列举3种) __ ;
    答案:ipython,jpython,cpython

    12.分析ASCII,Unicode和utf-8三种编码格式的异同点。
    答案:ASCII:所有的信息最终都表示为一个二进制的字符串,每一个二进制位(bit)有0和1两种状态,因此八个二进制位就可以组合出256种状态,这被称为一个字节(byte)。也就是说,一个字节一共可以用来表示256种不同的状态,每一个状态对应一个符号,就是256个符号,从00000000到11111111
    Unicode:一种所有符号的编码
    utf-8:在互联网上使用最广的一种Unicode的实现方式。它是一种变长的编码方式,它可以使用1~4个字节表示一个符号,根据不同的符号而变化字节长度

    13.在python脚本中如何指定解释器位置? 请列举.
    答案:#!/usr/bin/env python

    14.a=10, b=0, 求b/a*10 的结果为 _ ;
    答案:0

    15.请说明python2和python3中input函数的不同点;
    答案:python2有input和raw_input两种,input可以输入整形的数据,但是python3中input都为str

    16.如何查看变量a=1的类型 ___ , 如何将a转换为字符串类型 _ ;
    答案:print type(s) ; str(a)

    17.下面哪条语句在python中是不合法的?()
    A. x = y = z = 1
    B. x = (y = z + 1)
    C. x,y = y,z
    D. x+=y
    答案:B

    18.sum(range(5))的结果为 ; reversed(range(5))的结果为 ___ ;
    答案:10,内存地址

    19.你知道的可迭代数据类型有 _ , 可变数据类型有 _ ;
    答案:字符串,列表;列表

    20.x = [3 == 3, min(5,8)] 那么x的值为 __ ;
    答案:True

    展开全文
  • python周末班

    2019-09-26 15:45:06
    日期 星期 知识点分享/上课视频 地址链接 博客地址 2019/7/7 日 上课 链接:https://pan.baidu.com/s/1DZfM92046fMc3zHRG78veQ 提取码:5rcn ... 2019/7...
    日期 星期 知识点分享/上课视频

    地址链接

    博客地址

     2019/7/7  上课 链接:https://pan.baidu.com/s/1DZfM92046fMc3zHRG78veQ 提取码:5rcn https://www.cnblogs.com/Eva-J/articles/11135411.html
     2019/7/8  关于变量的重复赋值  https://www.cnblogs.com/Eva-J/articles/11149991.html  
    2019/7/9   直播  在QQ群  
    2019/7/10   关于变量的重复赋值2  https://www.cnblogs.com/Eva-J/articles/11150067.html  
    2019/7/11 直播 链接:https://pan.baidu.com/s/1oPkv6KmAPawPohLmFPCxYg  密码:g1f6  
    2019/7/12 关于复合赋值运算符 https://www.cnblogs.com/Eva-J/articles/11168769.html  
    2019/7/14 深浅copy https://www.cnblogs.com/Eva-J/p/5534037.html  
    2019/7/14 上课 链接:https://pan.baidu.com/s/1DZfM92046fMc3zHRG78veQ 提取码:5rcn https://www.cnblogs.com/Eva-J/articles/11168936.html
    2019/7/15 列表的使用扩展 https://www.cnblogs.com/Eva-J/articles/11189819.html  
    2019/7/16 直播 链接:https://pan.baidu.com/s/1QbjxqR2DQkBArHWUffvU5Q  密码:q2zq  
    2019/7/17 如何去掉一个列表中重复的项 https://www.cnblogs.com/Eva-J/articles/11201112.html  
    2019/7/18 直播 链接:https://pan.baidu.com/s/1ZD4ybl5-YusKF6EwXa77Ew  密码:j677  
    2019/7/19 不可变元组嵌套可变类型骨灰级内存解说 https://www.cnblogs.com/Eva-J/articles/11213353.html  
    2019/7/21 上课 链接:https://pan.baidu.com/s/1DZfM92046fMc3zHRG78veQ 提取码:5rcn https://www.cnblogs.com/Eva-J/articles/11214642.html
    2019/7/22 解包操作 https://www.cnblogs.com/Eva-J/articles/11227188.html  
    2019/7/23 直播 链接:https://pan.baidu.com/s/11CCMFqNC9xSHhlzhpf79ng  密码:iwut  
    2019/7/24 题目分享 https://www.cnblogs.com/a438842265/p/11238092.html  
    2019/7/25 直播 链接:https://pan.baidu.com/s/1pzi3HCyZKEsUkSSH_6ywbA  密码:wi2n  
    2019/7/26 递归函数 https://www.cnblogs.com/Eva-J/articles/7205734.html  
        go语言 链接:https://pan.baidu.com/s/1q22VZW_TsjojjZU5nHCO6w 提取码:y5zu   
    2019/7/28 上课 链接:https://pan.baidu.com/s/1DZfM92046fMc3zHRG78veQ 提取码:5rcn https://www.cnblogs.com/Eva-J/articles/11254517.html
    2019/7/29 题目分享 https://www.cnblogs.com/a438842265/p/11266030.html  
    2019/7/30 直播 链接:https://pan.baidu.com/s/1iFPhu_xa9RVDY-xIQgaIYQ 提取码:enxg   
    2019/7/31 生成器进阶 新的yield from语法 https://www.cnblogs.com/Eva-J/articles/11278900.html?dsourcetag=s_pcqq_aiomsg  
    2019/8/1 直播装饰器和生成器 链接:https://pan.baidu.com/s/18cxIKfw6xqevXPPNLjbfSw 提取码:2cxm   
        go语言 https://pan.baidu.com/s/1Omh7rhgT27ymDaRTsUHBsg 提取码:885v   
    2019/8/4 上课 链接:https://pan.baidu.com/s/1DZfM92046fMc3zHRG78veQ 提取码:5rcn https://www.cnblogs.com/Eva-J/articles/11266790.html?tdsourcetag=s_pcqq_aiomsg
    2019/8/5 分享内置函数
     https://www.cnblogs.com/Eva-J/articles/11304427.html 
     
    2019/8/6  直播计算器讲解  链接:https://pan.baidu.com/s/1npIpz8pUgGXgdFMoYILb-g 提取码:pfcr   

    转载于:https://www.cnblogs.com/a438842265/p/11288499.html

    展开全文
  • python3周末班2期第2天

    2019-08-02 02:32:19
    今日课程大纲: 01、上周内容回顾 02、pycharm使用 03、格式化输出 04、运算符 05、编码 06、数据类型转换 07、字符串 str 08、for 循环 09、列表 list 10、.../usr/bin/env python3 2 #author:Alnk(李成果) ...
     

    01、上周内容回顾

     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 '''
     4 print('打印')
     5 python的历史
     6 编译型和解释型
     7 python解释器种类:
     8     Cpython ipython jpython ... pypy
     9 变量:
    10     必须是数字,字母,下划线任意组合
    11     不能以数字开头
    12     不能使用python中的关键字
    13     要具有描述性 age = 12
    14     最好不要使用中文
    15     使用下划线命名
    16 常量:
    17     一直不变的量,全部大写的变量就是常量
    18 注释:
    19     单行注释 #
    20     多行注释 '''   ''' """   """
    21 数据类型初始
    22     int bool str
    23 input:用户输入
    24 流程控制语句:if
    25     if 条件:
    26         执行语句
    27     单独if
    28     if else
    29     if elif ....
    30     if elif .... else
    31     嵌套if
    32 流程控制语句:while
    33     while 条件:
    34         循环体
    35     如何终止while循环
    36         1,不满足条件
    37         2,break
    38     while ......else:只要while循环不被break打断,就执行else语句
    39 '''

     

    02、pycharm使用 

    1 #!/usr/bin/env python3
    2 #author:Alnk(李成果)
    3 '''
    4 1,pycharm安装以及简单的使用教程
    5 https://www.cnblogs.com/jin-xin/articles/9811379.html
    6 '''

     

    03、格式化输出

     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 """
     4 # % 格式化输出
     5 name = input('姓名')
     6 age = input('年龄')
     7 sex = input('性别')
     8 hobby = input('爱好')
     9 job = input('工作')
    10 
    11 msg = '''------------ info of %s -----------
    12 Name  : %s
    13 Age   : %s
    14 job   : %s
    15 Hobbie: %s
    16 ------------- end -----------------''' % (name,name,age,job,hobby)
    17 print(msg)
    18 
    19 #格式化输出
    20 #补充:注意其中的一个坑
    21 # 当用格式化输出的时候,里面有百分号,表示百分之几,需要两个百分号转义
    22 msg1 = '我叫%s ,今年%s岁,学习进度3%%' % ('alnk',18)
    23 print(msg1)
    24 """
    25 
    26 """
    27 format 格式化输出
    28 """
    29 # # 第一种
    30 # msg = '我叫{} 今年{} 性别{}'.format('太白', '18', '男')
    31 # print(msg)
    32 
    33 # 第二种
    34 # msg = '我叫{0} 今年{1} 性别{2},我依然叫{0}'.format('太白', '18', '男')
    35 # print(msg)
    36 
    37 # # 第三种
    38 # msg = '我叫{name} 今年{age} 性别{sex}'.format(name='太白', sex='男',age='18',)
    39 # print(msg)

     

    04、运算符

     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 '''
     4 运算符:
     5     算数运算,逻辑运算,赋值运算,比较运算,位运算,成员运算,身份运算...
     6     算数运算:+ - * /  **  //  %
     7     比较运算: == < > >= <=  !=
     8     赋值运算: = += -= *= /= ....
     9     逻辑运算: and or not
    10 '''
    11 # 逻辑运算: and or not
    12 # 优先级:() > not > and > or ,同一优先级从左至右依次计算
    13 # 1,运算符两边全部是比较运算
    14 print(1 > 2 and 3 < 4 and 2 > 7 or 4 < 5 )
    15 print(1 > 2 and 3 < 4 or 4 > 5 and 2 > 1 or 9 < 8)
    16 
    17 # 2,运算符两边全部是数值
    18 '''
    19 x or y: if x is True return x else y
    20 '''
    21 print(1 or 3)
    22 print(2 or 3)
    23 print(-1 or 3)
    24 print(0 or 3)
    25 print(1 or 3 or 0 or -1)
    26 
    27 # print(1 and 3)
    28 # print(2 and 3)
    29 # print(-1 and 3)
    30 # print(0 and 3)
    31 
    32 '''
    33 int ---> bool: 非0即True,0即False
    34 int ---> bool: True  1,False  0
    35 # i = 0
    36 # print(bool(i))
    37 # print(int(True))
    38 # print(int(False))
    39 '''
    40 
    41 # 3,运算符两边即是比较又是数值
    42 print(1 > 2 or 3 and 4)
    43 
    44 
    45 # 成员运算:in    not in
    46 s1 = 'abcde'
    47 s2 = 'ac'
    48 print(s2 in s1)
    49 
    50 if s2 in s1:
    51     print(333)
    52 else:
    53     print(111)

     

    05、编码

      1 #!/usr/bin/env python3
      2 #author:Alnk(李成果)
      3 '''
      4 谍战剧: 场景 发电报 滴滴滴滴滴滴滴
      5     计算机:文件的存储或者网络传输 高低电平01010101
      6     密码本:记录的就是010101 与文字的对应关系。
      7         今       0000001
      8         晚       0000101
      9         吃       0000110
     10         鸡       0010001
     11 
     12         # 0010101011010001
     13         0000001000010100001100010001
     14 
     15     ASCII:最初版本的密码本:英文字母,数字,特殊字符
     16 
     17         最开始:
     18             0000 0001   256种可能。
     19             0000 0001  8位 = 1个字节(bytes)。
     20             s1 = 'hello1' 6个字节
     21             一个字节表示一个字符。
     22             h: 01101000
     23             e: 01101001
     24             l: 01101100
     25             o: 01101110
     26             1:
     27             01101000 01101001 01101100 01101100 01101110 01101111
     28 
     29     unicode:万国码。把全世界所有的文字都记录起来。
     30         起初:一个字符两个字节
     31             h: 01101000 01101000
     32             中:01101000 01101000
     33             不够用
     34         升级:一个字符四个字节
     35             h: 01101000 01101000 01101000 01101000
     36             中: 01111000 01101000 01101000 01101000
     37         浪费空间,浪费流浪
     38 
     39     utf-8: 最少用8位一个字节表示一个字符。
     40         英文: 01101000 ASCII 英文字符:1个字节表示
     41         欧洲文: 01101000 01101000 一个字符两个字节表示
     42         中文: 01101000 01101100 01101000 一个字符用3个字节表示。
     43         'old男孩' utf-8的编码方式: 9个字节
     44 
     45     gbk:国标。
     46         只包含:英文和本国语言。
     47          h: 01101000 ASCII 英文字符:1个字节表示
     48          中: 01101000 01101100 一个字符用2个字节表示
     49          'old男孩' gbk的编码方式: 7个字节
     50 
     51     单位转化:
     52         8bit  ==   1Bytes
     53         1024Bytes ==  1KB
     54         1024KB    ==  1MB
     55         1024MB    ==  1GB
     56         1024GB    ==  1TB
     57 '''
     58 # 1, 不同密码本之间不能互相识别。报错,乱码。
     59 # 2, 数据的存储或者网络传输不能用unicode的密码本进行编码。
     60 #
     61 # python3x 环境:
     62 #         str类型内部编码方式为Unicode。
     63 #         所以:str类数据不能直接存储硬盘,或者网络传输。
     64 #
     65 #       str: 操作方法
     66 #       bytes: 拥有和str相同的操作方式,并且编码为非Unicode
     67 #
     68 # 英文:
     69 #     str:
     70 #         表现形式:'alex'
     71 #         内部编码:unicode
     72 #
     73 #     bytes:
     74 #         表现形式:b'alex'
     75 #         内部编码:非unicode
     76 #
     77 # 中:
     78 #     str:
     79 #         表现形式:'alex'
     80 #         内部编码:unicode
     81 #
     82 #     bytes:
     83 #         表现形式:b'\xe4\xb8\xad\xe5\x9b\xbd'
     84 #         内部编码:非unicode
     85 
     86 #
     87 # 英文
     88 # str1 = 'barry'
     89 # print(str1.upper())
     90 # bytes
     91 # b1 = b'barry'
     92 # print(b1.upper())
     93 #
     94 # 中文
     95 # s1 = '中国'
     96 # b1 = s1.encode('utf-8')
     97 # print(b1)
     98 
     99 # 转换;
    100 # str ---->bytes
    101 # unicode ---> utf-8
    102 # s1 = '中国'
    103 # b1 = s1.encode('utf-8')  # 编码
    104 # print(b1)
    105 # # utf-8 ----> unicode
    106 # s2 = b1.decode('utf-8') # 解码
    107 # print(s2)
    108 
    109 # s1 = '中国'
    110 # b1 = s1.encode('gbk')  # 编码
    111 # print(b1)
    112 # # utf-8 ----> unicode
    113 # s2 = b1.decode('gbk') # 解码
    114 # print(s2)
    115 #
    116 # # gbk ---> utf-8
    117 # b1 = b'\xd6\xd0\xb9\xfa'
    118 # s1 = b1.decode('gbk')  # unicode
    119 # b2 = s1.encode('utf-8')
    120 # print(b2)

     

    06、数据类型转换

     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 '''
     4 bool <---> int
     5 str  <--->  int
     6     s1 = '100'  全部由数字组成的str ---> int
     7     int(s1)
     8     str(100)
     9 str  <--->  bool  非空即True   '' 空即False
    10 
    11 '''
    12 print(int(True))
    13 print(int(False))

     

    07、字符串 str

      1 #!/usr/bin/env python3
      2 #author:Alnk(李成果)
      3 '''
      4 字符串常用知识点
      5 '''
      6 # 索引
      7 #s1 = 'python周末2期'
      8 # s2 = s1[0]
      9 # print(s2,type(s2))
     10 # s3 = s1[2]
     11 # print(s3)
     12 # print(s1[-1])
     13 
     14 # 切片:顾首不顾尾
     15 # s4 = s1[0:6]
     16 # print(s4)
     17 # s8 = s1[:6]
     18 # print(s8)
     19 # s5 = s1[2:6]
     20 # s6 = s1[:]
     21 # print(s6)
     22 
     23 # 切片+步长 [起始索引: 结尾索引: 步长]
     24 # s7 = s1[:5:2]
     25 # print(s7)
     26 
     27 # 反着取值(反向步长)
     28 # print(s1[-1:-4:-1])
     29 #
     30 # print(s1[-3::1])
     31 # print(s1[-3::-1])
     32 
     33 
     34 # 常用操作方法
     35 # 对字符串进行的任何操作都会形成一个新字符串,与原字符串无关。
     36 
     37 # s1 = 'aLEx'
     38 # #upper 全大写
     39 # #lower 全大写
     40 # s2 = s1.lower()
     41 # print(s2)
     42 # s3 = s1.upper()
     43 # print(s3)
     44 # 例子
     45 # username = input('请输入用户名')
     46 # password = input('请输入密码')
     47 # code = 'afREd'.upper()
     48 # your_code = input('请输入验证码:').upper()
     49 # if code == your_code:
     50 #     if username == 'barry' and password == '123':
     51 #         print('登录成功')
     52 # else:
     53 #     print('验证码错误')
     54 
     55 
     56 # strip
     57 # 默认(指定)去除字符串两边的换行符,制表符,空格。
     58 # lstrip  rstrip
     59 # s1 = ' \talex\n'
     60 # print(s1)
     61 # print(s1.strip())
     62 # s2 = 'eareelexqw'
     63 # print(s2.strip('qwer'))
     64 # s3 = '   alex   '
     65 # print(s3.lstrip())
     66 # print(s3.rstrip())
     67 # 例子:
     68 # username = input('请输入用户名').strip()
     69 # password = input('请输入密码').strip()
     70 # if username == 'barry' and password == '123':
     71 #     print('登录成功')
     72 
     73 
     74 # s1 = '周末2期python期'
     75 # find  通过元素找索引,找到用第一个则返回,找不到返回-1
     76 # index 通过元素找索引,找到用第一个则返回,找不到则报错
     77 # i = s1.find('w')
     78 # print(i)
     79 # i = s1.find('期',4,)
     80 # print(i)
     81 # i = s1.index('w')
     82 # print(i)
     83 
     84 
     85 # s1 = '太白barry'
     86 # # startswith 判断以...为开始
     87 # # endswith 判断以...为结尾
     88 # print(s1.startswith("太"))
     89 # print(s1.startswith("b",2,5))
     90 # print(s1.startswith("bb",2,5))
     91 # print(s1.startswith("ba",2,5))
     92 # print(s1.endswith("ba",2,5))
     93 # print(s1.endswith('y'))
     94 
     95 
     96 # # split 默认(可指定分隔符)以空格为分割,返回一个列表。
     97 # # str ----> list
     98 # s1 = 'alex wusir 太白 日天'
     99 # s11 = ':alex:wusir:太白:日天'
    100 # s12 = 'alex:wusir:太白:日天'
    101 # l1 = s1.split()
    102 # print(l1)
    103 # l2 = s11.split(':')
    104 # # 指定分隔次数
    105 # l3 = s12.split(':', 1)
    106 # print(l3)
    107 # print(l2)
    108 
    109 
    110 # join 可以操作任何数据类型(由多个对象组成即可),并不止字符串
    111 # s1 = 'alex'
    112 # s2 = '_'.join(s1)
    113 # print(s2)
    114 # s2 = '*'.join(s1)
    115 # print(s2)
    116 # #前提条件:此列表必须全部是有字符串元素组成
    117 # l1 = ['alex', 'wusir', '太白', '日天']
    118 # s2 = ' '.join(l1)
    119 # print(s2)
    120 
    121 
    122 # s1 = 'python深圳分校 深圳是一个很美丽的城市,我爱深圳'
    123 # #replace  默认全部替换 也可以设置替换次数
    124 # s2 = s1.replace('深圳', '北京')
    125 # print(s2)
    126 # s2 = s1.replace('深圳', '北京', 1)
    127 # print(s2)
    128 
    129 
    130 # s1 = 'barryafdfdafdaaa'
    131 # # count  计算某个元素出现的次数
    132 # i = s1.count('a')
    133 # print(i)
    134 
    135 
    136 # is 系列
    137 # name = 'taibai123'
    138 # print(name.isalnum()) #字符串由字母或数字组成
    139 # print(name.isalpha()) #字符串只由字母组成
    140 # print(name.isdigit()) #字符串只由数字组成
    141 
    142 
    143 # len 内置函数,统计字符串长度
    144 # s1 = 'fjdsklfsjafldsfjskladfjsladfjldksa'
    145 # print(len(s1))

     

    08、for 循环

     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 
     4 # for 循环
     5 s1 = 'python2期'
     6 # while
     7 # print(s1[0])
     8 # print(s1[1])
     9 # print(s1[2])
    10 
    11 # count = 0
    12 # while count < len(s1):
    13 #     print(s1[count])
    14 #     count += 1
    15 
    16 # for 变量 in iterable:
    17 #     pass
    18 # for i in s1:
    19 #     print(i,type(i))
    20 # for i in s1:
    21 #     print(i + 'sb')

     

    09、列表 list

      1 #!/usr/bin/env python3
      2 #author:Alnk(李成果)
      3 '''
      4 列表:list
      5     容器类的数据类型:基础数据类型之一 list
      6     列表可以存储大量的数据,列表是有序的
      7     列表有索引,切片,切片(步长)
      8 
      9 '''
     10 # 索引,切片
     11 # l1 = [100, 'alex', True, [1,2,3]]
     12 # print(l1[0],type(l1[0]))
     13 # print(l1[-2])
     14 # #顾首不顾尾
     15 # print(l1[:3])
     16 
     17 
     18 # 列表的常用操作方法.
     19     # 增删改查其他操作
     20 l1 = ['alex', 'wusir', '太白金星','日天', '女神']
     21 #
     22 # append 追加
     23 # l1.append(666)
     24 # print(ret)
     25 # insert插入
     26 # l1.insert(1,'日天')
     27 # print(l1)
     28 # extend 迭代着追加,增加多个(拆解成最小的元素追加到列表最后)
     29 # l1.extend('abc')
     30 # l1.extend([1,2,3])
     31 # print(l1)
     32 
     33 #
     34 # pop 按照索引删除 有返回值,如果不加索引,默认删除最后一个元素
     35 # ret = l1.pop(-1)
     36 # print(ret)
     37 # remove  按照元素删除
     38 # l1.remove('alex')
     39 # print(l1)
     40 # clear清空列表
     41 # l1.clear()
     42 # print(l1)
     43 # del从内存中删除整个列表,或者按照索引删除单个元素,多个元素
     44 # del l1[0]  # 按照索引删除
     45 # del l1[:2] # 按照切片删除
     46 # del l1
     47 # print(l1)
     48 
     49 #
     50 # 按照索引改值
     51 # l1[1] = 'sb'
     52 # 按照切片改值
     53 # l1[:3] = 'dfsdafsdfsdaf'
     54 # print(l1)
     55 
     56 # 查:可以索引切片 for循环
     57 # for i in l1:
     58 #     print(i)
     59 
     60 # 其他操作方法:
     61 # print(len(l1))  # 元素个数
     62 # count 次数
     63 # ret = l1.count('太白金星')
     64 # print(ret)
     65 
     66 # index 通过元素找索引,找不到报错
     67 # ret = l1.index('日天1')
     68 # print(ret)
     69 
     70 #l2 = [5, 6, 4, 0, 9, 1, 7, 8]
     71 # l2.sort()  # 从小到大
     72 # l2.sort(reverse=True)  # 从大到小
     73 
     74 # l2.reverse()  # 翻转
     75 # print(l2)
     76 
     77 
     78 # 列表的嵌套
     79 # l1 = [1, 2, 'taibai', [1, 'alex', 3, ]]
     80 # '''
     81 # 1, 将l1中的'taibai'变成大写并放回原处。
     82 # 2,给小列表[1,'alex',3,]追加一个元素,'老男孩教育'。
     83 # 3,将列表中的'alex'通过字符串拼接的方式在列表中变成'alexsb'。
     84 # '''
     85 # # print(l1[3][1])
     86 # # new = l1[3][1] + 'sb'
     87 # l1[3][1] = l1[3][1] + 'sb'
     88 # print(l1)
     89 
     90 
     91 
     92 # range 可以视为一个可控的数字范围的列表,多与for循环结合。
     93 # for i in range(1,10):  # [1,2,3,4,...9]
     94 #     print(i)
     95 # for i in range(20):  # [0,1,2,3,4,...19]
     96 #     print(i)
     97 
     98 # for i in range(1,10,2):  # [1,2,3,4,...9]
     99 #     print(i)
    100 # print(range(10))
    101 
    102 #倒着取值。顾首不顾尾
    103 # for i in range(10, 1, -1):
    104 #     print(i)
    105 
    106 # l1 = ['alex', 'wusir', '太白金星','日天', '女神']
    107 # range 打印列表的索引
    108 # for index in range(len(l1)):
    109 #     print(index)

     

    10、元组 tuple

     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 
     4 #  元组: 基础数据类型之一:tuple
     5 # 存储数据,重要的数据。
     6 
     7 # tu1 = (100, True, [1,2,3])
     8 # 元组没有增删改,可以查询
     9 # for i in tu1:
    10 #     print(i)
    11 
    12 # 元组也有索引切片。
    13 # print(tu1[0])
    14 
    15 # tu1 = (100, True, [1,2,3])
    16 # tu1[-1].append(666)
    17 # print(tu1)

     

    11、 字典 dict

      1 #!/usr/bin/env python3
      2 #author:Alnk(李成果)
      3 '''
      4 列表的不足:
      5 1,列表的数据量越大,查询速度越慢。
      6 2,列表存储的数据没有什么关联性。
      7 
      8 基础数据类型之dict
      9 dic = {'name': 'barry'}
     10 字典是以键值对的形式存储的。
     11 数据类型的划分:
     12     可变的(不可哈希)数据类型:list dict set
     13     不可变(可哈希)的数据类型:tuple str int bool
     14 
     15 字典的键key:必须是不可变得数据类型:int str
     16 字典的值:任意数据类型 变量,对象。
     17 字典可以存储大量的数据,而且字典的查询速度非常快。
     18 字典的key 唯一的不重复的。
     19 
     20 字典 3.5之前是无序的。
     21 字典在3.5之后是有序的 初始化时的顺序。
     22 
     23 构建数据类型:大量的关系型数据时,要想到字典。
     24 '''
     25 # dic = {'name': 'alex', 'age': 46, 'sex': 'laddyboy'}
     26 
     27 # 增删改查其他方法
     28 # 增:
     29 # 有则修改,无则增加
     30 # dic['high'] = 175
     31 # print(dic)
     32 # dic['name'] = '日天'
     33 # print(dic)
     34 # 有则不变,无则增加
     35 # dic.setdefault('weight',200)
     36 # print(dic)
     37 # dic.setdefault('age',73)
     38 # print(dic)
     39 
     40 # 删除
     41 # pop 按照key删除键值对,有返回值
     42 # ret = dic.pop('name')
     43 # print(ret)
     44 # clear 清空字典
     45 # dic.clear()
     46 # print(dic)
     47 #
     48 #del 删除整个字典或者某个键值对
     49 # del dic
     50 # del dic['name']
     51 
     52 #
     53 # dic['name'] = '日天'
     54 # dic = {"name":"jin","age":18,"sex":"male"}
     55 # dic2 = {"name":"alex","weight":75}
     56 # dic2.update(dic)   # 将dic的键值对覆盖添加到dic2中,dic不变。
     57 # print(dic2)
     58 # print(dic)
     59 
     60 #
     61 # print(dic['name'])
     62 # print(dic['name1'])
     63 # ret = dic.get('name')
     64 # ret = dic.get('name1', '没有此键')
     65 # print(ret)
     66 #
     67 # for i in dic:
     68 #     print(i,dic[i])
     69 
     70 # 其他方法:
     71 # dic.keys() dic.values() dic.items()
     72 # print(dic.keys(),type(dic.keys()))
     73 #
     74 # for key in dic.keys():
     75 #     print(key,type(key))
     76 #
     77 # for v in dic.values():
     78 #     print(v)
     79 
     80 
     81 # 分别赋值
     82 # a, b = 1, 4
     83 # print(a,b)
     84 # a, b = (1,4)
     85 # a, b = [[1,2,3],'alex']
     86 # print(a,b)
     87 # print(dic.items())  # dict_items([('name', 'alex'), ('age', 46), ('sex', 'laddyboy')])
     88 # for k,v in dic.items():
     89 #     print(k,v)
     90 # print(list(dic.items()))
     91 
     92 # 嵌套
     93 # dic = {
     94 #     'name_list': ['张三', '李四', 'BARRY'],
     95 #     1:{'name':'taibai', 'age': 18}
     96 #     'barry': {}
     97 # }
     98 # 1,给列表追加一个值:'王五'
     99 # dic['name_list'].append('王五')
    100 # print(dic)
    101 # 2,将BARRY 变成全部小写
    102 # print(dic['name_list'][-1].lower())
    103 # dic['name_list'][-1] = dic['name_list'][-1].lower()
    104 # print(dic)
    105 # 3,给{'name':'taibai', 'age': 18} 增加一个键值对 sex: 男。
    106 # print(dic[1])
    107 # dic[1]['sex'] = '男'
    108 # print(dic)
    109 
    110 
    111 #

     

    12 基础数据类型补充

     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 
     4 # int str bool list tuple dict
     5 # 数据类型的转换。
     6 # str ————> list split
     7 # list ————> str join
     8 # #
     9 # list tuple
    10 # print(list((1,2,3,'alex')))
    11 # print(tuple([1, 2, 3, 'alex']))
    12 # dic = {"name": "jin", "age": 18, "sex": "male"}
    13 # #
    14 # # print(list(dic))
    15 
    16 # 补充
    17 # tuple
    18 # 元组中如果只有一个元素且没有逗号(那他不是元组),则与该元素数据类型相同。
    19 # tu1 = (1)
    20 # tu2 = ('alex')
    21 # print(tu1,type(tu1))
    22 # print(tu2,type(tu2))
    23 #
    24 # list
    25 # l1 = [11, 22, 33, 44, 55]
    26 # # 将列表中索引为奇数位的元素删除
    27 # # 正常思路(错误示范):
    28 # # for index in range(len(l1)):
    29 # #     if index % 2 == 1:
    30 # #         del l1[index]
    31 # # print(l1)
    32 #
    33 # # 方法一:
    34 # # del l1[1::2]
    35 # # print(l1)
    36 #
    37 # # 方法二:
    38 # # 倒叙删除
    39 # for index in range(len(l1)-1,-1,-1):
    40 #     if index % 2 == 1:
    41 #         l1.pop(index)
    42 # print(l1)
    43 
    44 #方法三
    45 # count = []
    46 # for index in range(len(l1)):
    47 #     if index % 2 == 1:
    48 #         count.append(index)
    49 #
    50 # for i in count:
    51 #     l1.pop(i)
    52 #
    53 # print(l1)
    54 #
    55 #
    56 # 总结:对于列表来说,你在循环一个列表时,不要改变列表的大小,会影响你的最终结果。
    57 
    58 # 字典:
    59 # dic = {'k1': 'v1', 'k2': 'v2', 'k3': 'v3', 'name': '太白'}
    60 # 将字典的键中含有k元素的所有键值对删除
    61 #错误示范
    62 # for key in dic:
    63 #     if 'k' in key:
    64 #         dic.pop(key)
    65 # print(dic)
    66 #
    67 # 满足条件的key添加到一个新列表中,循环列表删除相应的键值对。
    68 # l1 = []
    69 # for key in dic:
    70 #     if 'k' in key:
    71 #         l1.append(key)
    72 # # print(l1)
    73 # for i in l1:
    74 #     dic.pop(i)
    75 # print(dic)

     

    转载于:https://www.cnblogs.com/lichengguo/p/10239556.html

    展开全文
  • python3周末班2期第3天

    2019-08-02 02:32:22
    今日课程大纲: 01 上周内容回顾 02 赋值运算与深浅拷贝 03 文件操作 简介 04 文件操作 读模式 05 文件操作 写模式 06 文件操作 追加模式 07 文件操作 修改 08 文件操作 其他方法 09 函数的初识 结构 作用 返回值 10...
     
    01 上周内容回顾
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 """
     4 格式化输出:
     5     % s d i f r
     6     format
     7         '{}{}{}'.format('alex',46,'women')
     8         '{0}{1}{2}{0}{0}'.format('alex',46,'women')
     9         '{name}{age}{hobby}'.format(age=46,name='alex',hobby='women')
    10 
    11 str:
    12     s1 = 'alex'
    13     print(s1[0])
    14     print(s1[:2])
    15     print(s1[::2])
    16     upper() lower()
    17     replace
    18     calptlize()
    19     strip()
    20     split() 分割,str----> list
    21     默认按照空格分割。指定分割符,指定分割次数。
    22     find index:通过元素查找索引值。
    23     join:将list ---> str 功能之一
    24      '|'.join(iterable)
    25      starstwith endswith
    26      isdigit()
    27      isalpha()
    28      isalnum()
    29     count()
    30     内置函数:len() 取总长度
    31 
    32 list: 容器型数据类型,按照顺序存值。
    33     索引切片(步长)
    34     增: append() insert() extend()
    35     删:  pop()  remove() clear()
    36          del 索引切片(步长) del l1[1]
    37 38         l1[0] = 'barry'
    39         l1[:3] = 'barry'
    40     查:
    41         索引切片(步长)
    42         for 循环
    43     其他操作方法:
    44         count index sort(reverse=True) 排序 reverse()
    45         len()
    46     range(10)
    47     range(1,12,2)
    48     for i in range(10,0,-1):
    49         print(i)
    50     l1 = [1,2,3,4,5]
    51     for i in range(len(l1)):
    52         print(i)
    53 
    54 tuple (1,2,3,4,[22]) 只读列表
    55 
    56 dict: key:value 大量关系型数据
    57     查询速度快,数据之间的关系性强。
    58     key: 不可变的数据类型。
    59     values: 任意数据类型 对象。
    60 
    61     增:dic['name'] = 'Alex' setdefault
    62     删:pop 按照key删除 del clear popitem(随机删除,但是3.6以后字典有序了)
    63     改:dic[key] = 'value' update
    64     查:
    65         dic['key'] 如果没有此key
    66         dic.get('key','设置返回值') 没有此键不报错,返回None,或者自己设定的值
    67         dic.keys() dic.values() dic.items()
    68         a,b = 1,2       分别赋值 a = 1  b = 2
    69         a,b = (2,3)     a = 2  b = 3
    70         for k,v in dic.items():
    71             pass
    72 
    73 数据类型的补充:
    74     数据类型的转换
    75 编码:
    76     str:python3x 编码是unicode
    77     str ---> bytes
    78     unicode ---> 非Unicode
    79     s1 = 'alex'
    80     b1 = s1.encode('utf-8') 编码
    81     b1.decode('utf-8') 解码
    82 
    83     b1 gbk----->  b2  utf-8
    84     b1.decode('gbk').encode('utf-8')
    85 """

     

    02 赋值运算与深浅拷贝
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # 赋值运算
     4 # 可变的数据类型:由于数据类型可变,修改数据会在原来的数据的基础上进行修改,
     5 # 可变的数据类型有:列表,字典
     6 # l1 = [1,2,3]
     7 # l2 = l1
     8 # l3 = l2
     9 # # l1 = [1,2,3,4]     #注意这不是修改数据,而是在内存中重新创建了一份数据,然后L1 指向了这份新数据
    10 # l1.append(666)       #在原来的数据上修改
    11 # print(l1,l2,l3)
    12 # print(id(l1),id(l2),id(l3))
    13 # 不可变的数据类型:由于数据类型是不可变的,不可修改,会在内存中在重新创建一份新的数据
    14 # 不可变的数据类型有:字符串,数字,bool值,元组等
    15 # l4 = 'abcd'
    16 # l5 = l4
    17 # l4 = [1,2,3,]
    18 # print(l4,l5)
    19 # print(id(l4),id(l5))
    20 
    21 
    22 # 浅copy
    23 # copy一个新列表(dict),列表在内存中是新的,但是列表里面的元素,完全沿用之前的元素。
    24 # l1 = [1, 2, 3,[22,33]]
    25 # l2 = l1.copy()
    26 # print(id(l1),id(l2))
    27 # l1.append(666)
    28 # l1.remove(1)
    29 # print(l1,l2)
    30 # 列表在内存中是新的,但是列表里面的元素,完全沿用之前的元素
    31 # print(id(l1[0]))
    32 # print(id(l2[0]))
    33 # l1[-1].append(666)
    34 # print(l1,l2)
    35 
    36 
    37 # 深copy
    38 import copy
    39 # 总结:深copy则会在内存中开辟新空间,将原列表以及列表里面的可变的数据类型重新创建一份,
    40 # 不可变的数据类型则沿用之前的,指向之前的数据
    41 # l1 = [1, 2, 3, [22, 33]]
    42 # l2 = copy.deepcopy(l1)
    43 # print(id(l1), id(l2))       #id 不一样,是两份数据
    44 # l1[-1].append(666)
    45 # l1.remove(1)                #只是删除了l1列表中和内存中的数据1的指向关系,并不是在内存中删除了数据1
    46 # print(l1,l2)
    47 
    48 
    49 # id 测试对象的内存地址
    50 # == 比较 比较两边的数值是否相等
    51 # print(2 == 2)
    52 # is 判断 判断的两边对象的内存地址是否是同一个。
    53 
    54 
    55 # ****** 切片是浅copy
    56 # l1 = [1, 2, 3, [22, 33]]
    57 # l2 = l1[:]  # 切片是浅copy
    58 # l1[-1].append(666)
    59 # # print(id(l1),id(l2)) 不是赋值关系
    60 # print(l1,l2)

     

     

    03 文件操作 简介
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 """
     4 文件三参数
     5     path:文件的路径
     6     encoding:文件以什么编码方式存储,就以什么编码方式读取
     7     mode:读,写,,读写,写读,追加,改等等
     8 
     9     三个大方向:
    10         带b的模式操作对象都是非文字类的文件:视频,音频,图片。
    11 
    12         读(r rb r+ rb+)
    13             r   读模式
    14             rb
    15             r+  读写模式
    16             rb+
    17 
    18         写 w wb w+ w+b :如果原文件存在,则清空原文件,在写入。这个慎用
    19             w   写模式
    20             wb
    21             w+  写读模式
    22             wb+
    23 
    24         追加(a ab a+ a+b)
    25             a   追加
    26             ab
    27             a+
    28             ab+
    29 
    30     文件操作的其他方法:
    31         f.read()
    32         f.write()
    33         f.readline()
    34         f.readlines()
    35         f.close()
    36         f.seek()
    37         f.tell()
    38 """
    39 
    40 
    41 # f1 = open("文件读写test.txt",encoding='utf-8',mode='r')
    42 # content = f1.read()
    43 # print(content)
    44 # f1.close()
    45 """
    46 f1变量,文件句柄。
    47 open 内置函数,底层调用的操作系统的操作文件功能的接口。
    48 windows: gbk,linux:utf-8  ios: utf-8
    49 操作文件总共3步;
    50     1,打开文件,产生文件句柄。
    51     2,对文件句柄进行操作。
    52     3,关闭文件句柄。
    53 """
    54 
    55 
    56 """
    57 # 错误示范:
    58 # 1,UnicodeDecodeError: 'gbk' codec can't decode...   编解码错误。
    59 # 2,路径错误。路径分隔符 与后面的字符产生特殊的意义。 解决方式 r 或者 \
    60     #f1 = open(r"d:\test\r文件读写test.txt",encoding='utf-8',mode='r')
    61 """

     

    04 文件操作 读模式
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 
     4 # 读模式(r rb r+ rb+)
     5 
     6 # r模式
     7 # 默认就是r模式
     8 # 1.1 read()    #全部读取文件内容,如果文件过大,可能会导致内存不足
     9 # f1 = open('文件读写test.txt', encoding='utf-8',mode='r')
    10 # content = f1.read()
    11 # print(content,type(content))  #对文件进行读取出来的都是字符串类型
    12 
    13 # 1.2 read(n)
    14 # r模式下,  n代表字符。
    15 # f1 = open('文件读写test.txt', encoding='utf-8',mode='r')
    16 # content = f1.read(3)
    17 # print(content)
    18 
    19 # 1.3 readline() 按行读取
    20 # f1 = open('文件读写test.txt', encoding='utf-8',mode='r')
    21 # print(f1.readline().strip())
    22 # print(f1.readline().strip())
    23 # f1.close()
    24 
    25 # 1.4 readlines()  返回一个列表,列表里面的元素是原文件每一行
    26 # f1 = open('文件读写test.txt', encoding='utf-8',mode='r')
    27 # content = f1.readlines()
    28 # print(content)
    29 # f1.close()
    30 
    31 
    32 # 1.5 for循环  **注意平常读取大文件就需要用这种方法
    33 # f1 = open('文件读写test.txt', encoding='utf-8',mode='r')
    34 # for line in f1:
    35 #     print(line.strip())
    36 # f1.close()
    37 
    38 
    39 # rb模式  ---读取一些非文字类的文件,如图片,视频等
    40 # 也有上面说的那5中读的模式, read()  read(n)  readline()  readlines()  for循环读取
    41 #如果是 rb 模式,就不需要规定编码了 encoding不是编码或解码,它是规定你这个文件到底采用哪种编码模式而已
    42 ## f1 = open('文件读写test.txt', encoding='utf-8',mode='rb')
    43 # f1 = open('文件读写test.txt',mode='rb')
    44 # content = f1.read()
    45 # print(content.decode('utf-8'))  #解码 utf-8 ---> unicode
    46 # f1.close()
    47 
    48 # rb 模式读取图片
    49 # f1 = open('time1.jpg', mode='rb')
    50 # content = f1.read()
    51 # print(content)
    52 # f1.close()
    53 
    54 # r+ 读写模式
    55 # f = open('log1',encoding='utf-8',mode='r+')
    56 # print(f.read())
    57 # f.write('666')
    58 # f.close()
    59 
    60 # 先写后读会出问题
    61 # f = open('log1',encoding='utf-8',mode='r+')
    62 # f.write('松岛岛')
    63 # print(f.read())
    64 # f.close()
    65 
    66 #rb+ 模式

     

    05 文件操作 写模式
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # 第二写模式
     4 # w 写模式
     5 # 没有文件,创建文件写入内容
     6 # 有文件,先清空内容后写入
     7 # f = open('log1',encoding='utf-8',mode='w')
     8 # # f.write('林志玲 fjdsklafjsd;flj太白金星')
     9 # f.write('深圳校区 ~~~')
    10 # f.write('深圳校区 ~~~')
    11 # f.write('深圳校区 ~~~')
    12 # f.write('深圳校区 ~~~')
    13 # f.write('深圳校区 ~~~')
    14 # f.close()
    15 
    16 # wb 写模式(以bytes类型写入到文件)
    17 #rb
    18 # f1 = open('time1.jpg', mode='rb')
    19 # content = f1.read()
    20 # print(content)
    21 # f1.close()
    22 # #wb 写入到文件
    23 # #把bytes类型写入到文件 --图片
    24 # f2 = open('time2.jpg',mode='wb')
    25 # f2.write(content)
    26 # f2.close()
    27 
    28 # w+ 写读模式
    29 # f = open('log1',encoding='utf-8',mode='w+')
    30 # f.write('老男孩教育...')
    31 # f.seek(0)   #调整光标(指针)到最开始
    32 # print(f.read())
    33 # f.close()
    34 
    35 #wb+ 写读模式(bytes)

     

    06 文件操作 追加模式
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # a 追加
     4 # 没有文件创建文件追加内容,有文件,在文件最后追加内容。
     5 # f = open('log3',encoding='utf-8',mode='a')
     6 # f.write('老男孩')
     7 # f.close()
     8 #有文件,在文件最后追加内容。
     9 # f = open('log3',encoding='utf-8',mode='a')
    10 # f.write('666')
    11 # f.close()
     
    07 文件操作 修改 
    #!/usr/bin/env python3
    #author:Alnk(李成果)
    '''
    文件的修改:所有的文件编辑器都要经过下面这5步才能对文件进行修改
        1,以读的模式打开原文件
        2,以写的模式打开新文件
        3,读取原文件对源文件内容进行修改形成新内容写入新文件
        4,将原文件删除
        5,将新文件重命名成原文件
    '''
    # 方法一 小文件可以。
    # import os
    #
    # # 1,以读的模式打开原文件。
    # # 2,以写的模式打开新文件。
    # with open('alex个人简历',encoding='utf-8') as f1,\
    #         open('alex个人简历.bak',encoding='utf-8',mode='w') as f2:
    #     # 3,读取原文件对源文件内容进行修改形成新内容写入新文件。
    #     old_content = f1.read()
    #     new_content = old_content.replace('alex','SB')
    #     f2.write(new_content)
    #
    # # 4,将原文件删除。
    # os.remove('alex个人简历')
    # # 5,将新文件重命名成原文件。
    # os.rename('alex个人简历.bak','alex个人简历')
    
    
    # 方法二: 推荐使用这种方法
    import os
    #
    # 1,以读的模式打开原文件。
    # 2,以写的模式打开新文件。
    # with open('alex个人简历',encoding='utf-8') as f1,\
    #         open('alex个人简历.bak',encoding='utf-8',mode='w') as f2:
    #     # 3,读取原文件对源文件内容进行修改形成新内容写入新文件。
    #     for old_line in f1:
    #         new_line = old_line.replace('SB','alex')
    #         f2.write(new_line)
    #
    # # 4,将原文件删除。
    # os.remove('alex个人简历')
    # # 5,将新文件重命名成原文件。
    # os.rename('alex个人简历.bak','alex个人简历')

     

    08 文件操作 其他方法
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # 其他操作方法:
     4 # readable() writable()  ***      文件句柄是否可读,可写
     5 # seek 调整光标位置 按照字节   ***
     6 # seek(0)  将光标移动到开始
     7 # seek(0,2)  将光标移动到最后。
     8 # tell 获取光标位置 按照字节。  ***
     9 # flush 刷新 保存  ***
    10 # truncate 截取原文件,从头开始截,
    11 #
    12 # f = open('log3',encoding='utf-8')
    13 # f.seek(1)
    14 # print(f.read())
    15 # print(f.tell())
    16 # f.seek(0) # 将光标移动到开始
    17 # f.seek(0,2)  # 将光标移动到最后。
    18 
    19 # print(f.read())
    20 # print(f.writable())
    21 # if f.writable():
    22 #     f.write('111')
    23 # f.close()
    24 
    25 # f = open('log3',encoding='utf-8',mode='w')
    26 # f.write('fjdsklafjdfjksa')
    27 # f.flush()
    28 # f.close()
    29 
    30 #截取原文件,从头开始截,需要在可写的模式下,并且不清空原文件
    31 # f = open('log3',encoding='utf-8',mode='r+')
    32 # f.truncate(3)
    33 # f.close()

     

    09 函数的初识 结构 作用 返回值
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # 面向过程编程
     4 # s1 = 'fjksdfjsdklf'
     5 # count = 0
     6 # for i in s1:
     7 #     count += 1
     8 # print(count)
     9 #
    10 # l1 = [1, 2, 3, 4, 5]
    11 # count = 0
    12 # for i in l1:
    13 #     count += 1
    14 # print(count)
    15 #
    16 # 缺点:
    17 # 1,代码重复太多。
    18 # 2,代码的可读性差。
    19 
    20 
    21 #函数的初识
    22 # def my_lf_len(s):
    23 #     count = 0
    24 #     for i in s:
    25 #         count += 1
    26 #     print(count)
    27 #
    28 # s1 = 'fjdsklafsdkalfjsda'
    29 # my_lf_len(s1)
    30 # l1 = [1,2,3,3,5,56,6,6]
    31 # my_lf_len(l1)
    32 #
    33 # 函数是什么?
    34 # 功能体,一个函数封装的一个功能
    35 
    36 
    37 # 结构:
    38 '''
    39 def 函数名():
    40     函数体
    41 '''
    42 
    43 
    44 # 函数什么时候执行?
    45 # 被调用的时候执行。函数名+()
    46 # def my_lf_len(s):
    47 #     count = 0
    48 #     for i in s:
    49 #         count += 1
    50 #     print(count)
    51 #
    52 # my_lf_len('fdskfjskdlf')
    53 
    54 
    55 # 函数的返回值 return
    56 # def Tantan():
    57 #     print('搜索')
    58 #     print('左滑动一下')
    59 #     print('右滑动一下')
    60 #     # return
    61 #     print('发现美女,打招呼')
    62 #     # return '美女一枚'
    63 #     # return ['恐龙一堆']
    64 #     # return '小萝莉', '肯德基', '御姐'
    65 #     return {'name':'alnk','age':18},[1,2,3,4,5,]    #这算多个值
    66 # ret = Tantan()
    67 # print(ret,type(ret))
    68 #
    69 # 调用一次执行一次
    70 # Tantan()
    71 # Tantan()
    72 # Tantan()
    73 #
    74 # 返回值
    75 '''
    76 return: 
    77     1,终止函数。
    78     2,给函数的调用者(执行者)返回值。
    79         return             --->  None 
    80         return   单个值    --->  单个值   --被返回的数据是什么数据类型就是什么类型
    81         return   多个值    ---> (多个值,) --元组
    82 '''

     

    10 函数的参数 
      1 #!/usr/bin/env python3
      2 #author:Alnk(李成果)
      3 # def Tantan(sex):  #函数的定义:sex形式参数,形参
      4 #     print('搜索')
      5 #     print('左滑动一下')
      6 #     print('右滑动一下')
      7 #     print('发现美女,打招呼')
      8 #     return '小萝莉', '肯德基', '御姐'
      9 #
     10 # # 函数的参数
     11 # Tantan('女') # 函数的执行:'女' 实际的数据, 实参。
     12 
     13 
     14 #从两方面将函数的参数:实参 和 形参
     15 # 实参角度
     16 # 1.位置参数。  从左至右,一一对应
     17 # def Tantan(sex,age):
     18 #     print('筛选性别%s,年龄%s左右' %(sex,age))
     19 #     print('搜索')
     20 #     print('左滑动一下')
     21 #     print('右滑动一下')
     22 #     print('发现美女,打招呼')
     23 #
     24 # Tantan('女',28,)
     25 #
     26 # 练习:比大小,返回大的数字
     27 # def max_(a,b): return a if a > b else b
     28 #     # if a > b:
     29 #     #     return a
     30 #     # else:
     31 #     #     return b
     32 #     # return a if a > b else b
     33 # print(max_(100,200))
     34 #
     35 # 扩展:
     36 # 三元运算符
     37 # a = '饼'
     38 # b = '西瓜'
     39 # ret = a if 3 > 2 else b
     40 # print(ret)
     41 
     42 # 2.关键字参数。 一一对应。
     43 # 函数参数较多 记形参顺序较麻烦时,需要关键字参数。
     44 # def Tantan(sex,age,area):
     45 #     print('筛选性别%s, %s附近,年龄%s左右的美女' %(sex,area,age))
     46 #     print('搜索')
     47 #     print('左滑动一下')
     48 #     print('右滑动一下')
     49 #     print('发现美女,打招呼')
     50 # Tantan(sex='女',area='南山区',age='28')
     51 
     52 # 3.混合参数 : 一一对应,关键字参数必须要在位置参数后面。
     53 # def Tantan(sex,age,area):
     54 #     print('筛选性别%s,%s 附近,年龄%s左右的美女' %(sex,area,age))
     55 #     print('搜索')
     56 #     print('左滑动一下')
     57 #     print('右滑动一下')
     58 #     print('发现美女,打招呼')
     59 # Tantan('女',28,area='南山区')
     60 
     61 
     62 # 形参角度
     63 # 1.位置参数。 从左至右,一一对应。
     64 # def Tantan(sex,age):
     65 #     print('筛选性别%s,年龄%s左右' %(sex,age))
     66 #     print('搜索')
     67 #     print('左滑动一下')
     68 #     print('右滑动一下')
     69 #     print('发现美女,打招呼')
     70 # Tantan('女',28,)
     71 
     72 # 2.默认参数  : 使用最多的一般不更改的参数,默认参数一定放在位置参数后面
     73 # def Tantan(area,age,sex='girl'):
     74 #     print('筛选性别%s, %s 附近,年龄%s左右的美女' %(sex,area,age))
     75 #     print('搜索')
     76 #     print('左滑动一下')
     77 #     print('右滑动一下')
     78 #     print('发现美女,打招呼')
     79 # Tantan('南山区',28,'laddboy')
     80 
     81 # 3.万能参数(动态参数) *args, **kwargs
     82 # def Tantan(*args,**kwargs):
     83 #     # 函数的定义: * 代表聚合。
     84 #     # * 将实参角度所有的位置参数放到一个元祖中,并将元组给了args
     85 #     # ** 将实参角度所有的关键字参数放到一个字典中,并将字典给了kwargs
     86 #     # print('筛选性别%s, %s 附近,年龄%s左右的美女' %(sex,area,age))
     87 #     # print(args)
     88 #     # print(kwargs)
     89 #     print('筛选地域:%s,年龄%s' % args)
     90 #     print('搜索')
     91 #     print('左滑动一下')
     92 #     print('右滑动一下')
     93 #     print('发现美女,打招呼')
     94 #
     95 # # Tantan('南山区','28', '性格好','身材好', '家境好')
     96 # Tantan('南山区','28',body='身材好',voice='萝莉音',money='白富美')
     97 #
     98 # def Tantan(*args,**kwargs):
     99 #     # 函数的定义: * 代表聚合。
    100 #     # * 将实参角度所有的位置参数放到一个元祖中,并将元组给了args
    101 #     # ** 将实参角度所有的关键字参数放到一个字典中中,并将字典给了kwargs
    102 #     print(args)
    103 #     print(kwargs)
    104 #
    105 # Tantan('南山区','28',body='身材好',voice='萝莉音',money='白富美')
    106 # l1 = [1,2,3]
    107 # l2 = (4,5,6)
    108 # Tantan(*l1,*l2)  # 函数的执行:*iterable 将l1打散,添加到args
    109 # Tantan(1, 2, 3, 4, 5, 6)
    110 # dic1 = {'name':"alex"}
    111 # dic2 = {'age':46}
    112 # Tantan(**dic1,**dic2)     #**dic1 将dic1打散,将所有的键值对添加到kwargs
    113 
    114 
    115 # 形参的最终顺序
    116 # 位置参数  *args  默认参数 **kwargs
    117 # def func(a,b,*args,sex='女',**kwargs):
    118 #     print(a,b,sex,args,kwargs)
    119 # func(1,2,4,5,6,name='alex',age=73) 
     
    11 函数的命名空间和顺序
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 """
     4 函数里面的变量,在函数外面能直接引用么?
     5 def func1():
     6     m = 1
     7     print(m)
     8 print(m)  #这行报的错
     9 
    10 报错了:
    11 NameError: name 'm' is not defined
    12 
    13 我们首先回忆一下Python代码运行的时候遇到函数是怎么做的,从Python解释器开始执行之后,就在内存中开辟里一个空间。
    14 每当遇到一个变量的时候,就把变量名和值之间对应的关系记录下来,但是当遇到函数定义的时候,解释器只是象征性的将函数名读入内存,
    15 表示知道这个函数存在了,至于函数内部的变量和逻辑,解释器根本不关心。等执行到函数调用的时候,
    16 Python解释器会再开辟一块内存来储存这个函数里面的内容,这个时候,才关注函数里面有哪些变量,
    17 而函数中的变量会储存在新开辟出来的内存中,函数中的变量只能在函数内部使用,并且会随着函数执行完毕,这块内存中的所有内容也会被清空。
    18 
    19 我们给这个‘存放名字与值的关系’的空间起了一个名字 --- 命名空间。
    20 代码在运行伊始,创建存储“变量名与值的关系”的空间叫做全局命名空间;
    21 在函数的运行中开辟的临时的空间叫做局部命名空间。
    22 """
    23 # python的空间分三种:
    24 # 全局名称空间
    25 # 局部名称空间
    26 # 内置名称空间  print() len() 内置函数
    27 #
    28 # 举例
    29 # def func():
    30 #     name = 'alex'
    31 # func()
    32 # print(name)
    33 # func()
    34 # python中的作用域分两种:
    35 # 全局作用域:内置名称空间 全局名称空间
    36 # 局部作用域:局部名称空间
    37 
    38 
    39 # 取值顺序: 就近原则
    40 # 局部变量先到局部寻找,局部没找到才去全局找,全局没找到,去内置找,都没找到,就报错
    41 # 全局变量直接在全局查找,全局没有就去内置空间查找,如果没有,就报错
    42 # 局部名称空间 ———> 全局名称空间 ———> 内置名称空间    (这个顺序不可逆)
    43 # input = 'barry'
    44 # def func():
    45 #     # input = 'alex'
    46 #     print(input)
    47 # func()
    48 # print(input)
    49 
    50 
    51 # 加载顺序
    52 # 所有的东西要加载到内存才运行
    53 # 内置名称空间  --->  全局名称空间 --->  局部名称空间
     
    12 函数的嵌套
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # 函数的嵌套
     4 # 举例1
     5 # def func():
     6 #     print(111)
     7 #
     8 # def func1():
     9 #     print(222)
    10 #     func()
    11 #
    12 # def func3():
    13 #     print(333)
    14 #     func1()
    15 #
    16 # print(444)
    17 # func()
    18 # print(555)
    19 # 444 111 555
    20 #
    21 #举例2
    22 # def func():
    23 #     print(111)
    24 #
    25 # def func1():
    26 #     print(222)
    27 #     func()
    28 #
    29 # def func3():
    30 #     print(333)
    31 #     func1()
    32 #
    33 # print(444)
    34 # func3()
    35 # print(555)
    36 # 444 333 222 111 555
    37 #
    38 # def wrapper():
    39 #     print(222)
    40 #     def inner():
    41 #         print(111)
    42 #     print(444)
    43 #     inner()
    44 #     print(333)
    45 #
    46 # wrapper()
    47 # 222 444 111 333

     

    13 内置函数 globals()  locals()
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 """
     4 本文件主要讲述内置函数globals() locals()
     5 """
     6 # name = 'alex'
     7 # age = 46
     8 #
     9 # def func():
    10 #     sex = '男'
    11 #     hobby = '女'
    12 #     print(globals())  # 返回一个字典:全局作用域的所有内容
    13 #     print(locals())   # 返回一个字典:当前位置的内容
    14 # func()
    15 #
    16 # print(globals())  # 返回一个字典:全局作用域的所有内容
    17 # print(locals())   # 返回一个字典:当前位置的内容

     

    14 函数的名称应用 
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # 函数名的应用
     4 # 1,函数名是一个特殊的变量 函数名() 执行此函数
     5 # def func():
     6 #     print(666)
     7 # func()
     8 
     9 # 2,函数名可以当做变量进行赋值运算。
    10 # def func():
    11 #     print(666)
    12 #
    13 # f = func
    14 # f()
    15 
    16 # 3,函数名可以作为容器型数据的元素 ***
    17 # def func():
    18 #     print(666)
    19 #
    20 # def func1():
    21 #     print(777)
    22 #
    23 # def func2():
    24 #     print(888)
    25 #
    26 # def func3():
    27 #     print(999)
    28 #
    29 # l1 = [func, func1, func2, func3]
    30 # for i in l1:
    31 #     i()
    32 #
    33 # dic = {
    34 #     1: func,
    35 #     2: func1,
    36 #     3: func2,
    37 #     4: func3,
    38 # }
    39 #
    40 # while 1:
    41 #     num = input('请输入序号:').strip()
    42 #     num = int(num)
    43 #     dic[num]()
    44 
    45 # 4,函数名可以作为函数的参数。
    46 # def func1():
    47 #     print(111)
    48 #
    49 # def func2(x):
    50 #     x()
    51 #     print(222)
    52 #
    53 # func2(func1)
    54 
    55 # 5,函数名可以作为函数的返回值。
    56 # def func1():
    57 #     print(111)
    58 #
    59 # def func2(x):
    60 #     print(222)
    61 #     return x
    62 #
    63 # ret = func2(func1)
    64 # ret()

     

    15 关键字 global nonlocal
     1 #!/usr/bin/env python3
     2 #author:Alnk(李成果)
     3 # global nonlocal
     4 #
     5 # global
     6 #1,声明一个全局变量。
     7 # def func():
     8 #     global name
     9 #     name = 'alex'
    10 # func()
    11 # print(name)
    12 
    13 # 2,修改一个全局变量。
    14 # 原因:局部作用域只能引用全局变量而不能改变全局变量。如果改变则报错
    15 # global 可以实现通过局部作用域而去改变全局变量
    16 # count = 1
    17 # def func1():
    18 #     global count
    19 #     count += 1
    20 # print(count)
    21 # func1()
    22 # print(count)
    23 
    24 
    25 # nonlocal: 子级函数可以通过nonlocal修改父级(更高级非全局变量)函数的变量。
    26 # 现象:子级函数可以引用父级函数的变量但是不能修改。
    27 def func():
    28     count = 1
    29     def func1():
    30         def inner():
    31             nonlocal count
    32             count += 1
    33             print(count)  # 2
    34         print(count)      # 1
    35         inner()
    36         print(count)      # 2
    37     func1()
    38 func()
    39 
    40 # 这个不行,会报错
    41 # count = 1
    42 # def func1():
    43 #     def inner():
    44 #         nonlocal count
    45 #         count += 1
    46 #         print(count)
    47 #     inner()
    48 #
    49 # func1()

     

    转载于:https://www.cnblogs.com/lichengguo/p/10272736.html

    展开全文
  • python作业周末班0401

    2018-04-04 21:38:03
    1.字符串最后一个单词的长度 题目描述:计算字符串最后一个单词的长度,单词以空格隔开。 输入描述: 一行字符串,非空,长度小于5000。 输出描述: 整数N,最后一个单词的长度。 ... 输入:hello world ...
  • 因此,利用爬虫,我们可以解决部分数据问题,那么,如何学习Python数据爬虫呢? 1. 学习Python基础知识并实现基本的爬虫过程 一般获取数据的过程都是按照 发送请求-获得页面反馈-解析并且存储...
  • 下载地址: http://yuandonglirw.com/forum.php?mod=viewthread&amp;tid=386&amp;fromuid=1
  • 智普jeapedu教育python周末班课程资料,下载地址:http://uliweb.clkg.org/tutorial/read/21
  • 期望能够在linux上走的更远,学的更多,看了老男孩的课表之后,决心报的全程。 在前几年的困惑,今后发展路线的规划,最终决心还是要走运维路线,随着行业不断的发展,个人面临的困惑,只能不断提高自己,不断掌握...
  • Python实战-学员学习成果展示同样是周末,有些人是闲聊着度过,有些人是学习充电度过。人与人最大的区别,是下班后的时间。看你怎么去利用。周末时,马哥Python实战的学员正在认真上课,他们中的不少人,月薪在...
  • 常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。 但其实import加载的模块分为四个通用类别: 1 使用python编写的代码(.py文件) 2 已被编译为共享库或DLL的C或...
  • 圆满完成Selenium自动化测试周末班培训课程!http://automationqa.com/forum.php?mod=viewthread&tid=2704&fromuid=29
  • 你现在已经学会了写python代码,假如你写了两个python文件a.py和b.py,分别去运行,你就会发现,这两个python的文件分别运行的很好。但是如果这两个程序之间想要传递一个数据,你要怎么做呢? 这个问题以你现在的...
  • Python工程师基础

    2013-06-01 15:51:16
    四个大项目:断点续传下载文件,爬虫,CMS全站(uliweb框架),SNS社交(django框架) 多个小项目:猜数字,计算器,字典查询,... 学习对象:服务器运维工程师,测试工程师,学生 ...Python核心编程 ...Python基础教程:
  • p1 01-02 python fullstack s22 day01 开班介绍 --时长:91.0 p2 01-02 python fullstack s22 day01 ... p3 03 python fullstack s22 day01 python的历史,种类 --时长:52.95 p4 04 python fullstack s22 day01 安装
  • 为此,CSDN特向广大Python爱好者开设了学习,帮助大家在学习Python的道路上少走弯路,事半功倍。上周五,“CSDN博客专家会客厅”走进班级,CSDN博客专家、技术达人刘冬作为受邀专家与大家畅聊Python。 我也
1 2 3 4 5 ... 20
收藏数 1,359
精华内容 543