02.Python进阶

发布于 2025-07-11  126 次阅读


一、面向对象的基本概念

1.面向过程与面向对象

  • 面向过程:关注事物发展的过程进行编程,程序通过模拟事物发展的过程实现最终效果。
  • 面向对象:关注事物之间的关系进行编程,程序通过模拟事物之间的交互实现最终效果。

2.面向对象的三大特性

  • 封装:隐藏对象的属性和方法,仅对外提供接口供外界访问
    • 使用者只需要通过接口访问,无需了解对象的细节
    • 对象可以设置访问的权限,限制访问的内容
  • 继承:子类继承父类的属性和方法
    • 子类具有父类的特征和行为
    • 子类可以额外拥有自己的特征和行为
  • 多态:同一函数在不同环境下表现出不同的状态

二、类和对象

1.基本概念

  • 类:将事物进行抽象得到的模板(例如:空的个人信息表)
  • 对象:根据模板填充过具体的属性的实体(例如:填写过的个人信息表)

2.定义方法

  • 类的定义
    #类的定义
    #定义方法一
    """
    定义格式:
    class 类名
      (功能列表)
      def 函数1
      def 函数2
      ...
    """
    #例:学生类
    class Student1:
      def getType(self):
          print('这是一个学生')
    #定义方法二
    """
    定义格式:
    class 类名()
      (功能列表)
      def 函数1
      def 函数2
      ...
    """
    #例:学生类
    class Student2():
      def getType(self):
          print('这是一个学生')
    #定义方法三
    """
    定义格式:
    class 类名(object)
      (功能列表)
      def 函数1
      def 函数2
      ...
    """
    #例:学生类
    class Student3(object):
      def getType(self):
          print('这是一个学生')
  • 对象的定义
    #对象的定义
    """
    在定义类的基础上:
    创建对象:对象名=类()
    调用对象:对象名.方法名()
    """
    #例:使用学生类
    xiaoming = Student()
    xiaoming.getType()
    #一个类可以创建多个对象
    xiaohong = Student()
    xiaohong.getType()

3.self关键字

  • 内容
    • 每一个对象拥有一个独特的self值,这个值标识了不同的对象
  • 作用
    • self区分了不同的对象,对象在调用类方法时,会传入self值以便类区分是哪一个对象在调用方法
    • 在类的内部调用类的方法:self.方法名()

4.对象的属性

  • 操作属性
    • 在类的外部操作属性
    #在类的外部操作属性
    """
    设置属性:对象名.属性=属性值
    调用属性:对象名.属性
    """
    class Student:
        def getType(self):
            print('这是一个学生')
    
    xiaoming = Student()
    xiaoming.name = 'xiaoming'
    xiaoming.age = 18
    print(f'小明的年龄是{xiaoming.age}岁')
    • 在类的内部操作属性
    #在类的内部操作属性
    """
    调用属性:self.属性
    """
    class Student:
        def getName(self):
            print(f'学生的姓名是{self.name}')
    
    xiaoming = Student()
    xiaoming.name = '小明'
    xiaoming.getName()

5.魔法方法

  • 概念
    • 魔法方法的命名特点是__方法名__,魔法方法在满足条件时会自动调用
  • __init__
    • 条件:通过类创建对象时,若类中存在__init__函数,则会自动调用
    • 返回值:None
    • 使用
    • 【无参版本】def __init__(self):
      #无参__init__
      class Student:
        def  __init__(self):
            self.name = '未命名'
        def getName(self):
            print(self.name)
      
      xiaoming = Student()
      xiaoming.getName()
      print(xiaoming.name)
    • 【有参版本】def __init__(self,参数1,参数2,...)
      #有参__init__
      class Student:
        def  __init__(self,name):
            self.name = name
        def getName(self):
            print(self.name)
      
      xiaoming = Student('小明')
      xiaoming.getName()
      print(xiaoming.name)
  • __str__
    • 条件:通过print输出对象时,默认打印对象的内存地址,若类中存在__str__函数,则会自动调用
    • 返回值:str类型
    • 使用:def __str__(self)
    #__str__
    class Student:
        def  __init__(self,name):
            self.name = name
        def __str__(self):
            return f'学生姓名:{self.name}'
    
    xiaoming = Student("小明")
    print(xiaoming)
  • __del__
    • 条件:当删除对象时,默认调用__del__函数
    • 使用:def __del__(self)
    #__del__
    class Student:
        def __del__(self):
            print('调用了__del__')
    
    xiaoming = Student()
    del xiaoming
    xiaohong = Student()
    #程序结束默认调用__del__

三、继承

1.基本概念

  • 继承:指子类可以继承父类的属性和方法(站在子类视角)
  • 派生:指从已有的类产生一个新的类(站在父类视角)
  • 注意:继承是类的继承,而并非对象的继承

2.定义方法

#继承的定义
"""
class 类名(父类名):
"""
class Father:
    def __init__(self):
        self._money = 100

    def printHobby(self):
        print("Computer")

class Son(Father):
    pass

xiaoming = Son()
xiaoming.printHobby()

2.单继承与多继承

  • 单继承:子类单独继承一个父类,仅能继承该父类的属性和方法
    #单继承
    class Master:
      def __init__(self):
          self._skill='<降龙十八掌>'
    
      def getSkill(self):
          print(f'使用了{self._skill}')
    
    class Prentice(Master):
      pass
    
    xiaoming = Prentice()
    xiaoming.getSkill()
  • 多继承:子类从多个父类进行继承,可以继承所有父类的属性和方法
    # 多继承
    class Master1:
      def __init__(self):
          self._skill = '<降龙十八掌>'
    
      def getSkill(self):
          print(f'使用了{self._skill}')
    
    class Master2:
      def __init__(self):
          self._skill = '<暴雨梨花针>'
    
      def getSkill(self):
          print(f'使用了{self._skill}')
    
    class Prentice(Master1, Master2):
      pass
    
    xiaoming = Prentice()
    #同名属性和方法按照调用顺序来执行
    print(xiaoming._skill)
    xiaoming.getSkill()
    
    #查看调用顺序的方法
    print(Prentice.__mro__) #返回元组
    print(Prentice.mro()) #返回列表
    """
    运行结果:
    (<class '__main__.Prentice'>, <class '__main__.Master1'>, <class '__main__.Master2'>, <class 'object'>)
    表明:
    1.同名属性和方法按照Prentice,Master1,Master2,object的顺序进行调用
    2.若按照上述顺序未能成功找到,则会报错
    """

3.子类重写父类方法

#子类重写父类同名属性和方法
class Master1:
    def __init__(self):
        self._skill = '<降龙十八掌>'

    def getSkill(self):
        print(f'使用了{self._skill}')

class Master2:
    def __init__(self):
        self._skill = '<暴雨梨花针>'

    def getSkill(self):
        print(f'使用了{self._skill}')

class Prentice(Master1, Master2):
    def __init__(self):
        self._skill='<降龙暴雨掌>'

    def getSkill(self):
        print(f'使用了{self._skill}')

xiaoming=Prentice()
xiaoming.getSkill()

4.子类调用父类方法

  • 显式调用
    #显式调用
    class Master:
      def __init__(self):
          self._skill = '<降龙十八掌>'
    
      def getSkill(self):
          print(f'使用了{self._skill}')
    
    class Prentice(Master):
      def __init__(self):
          self._skill='<降龙暴雨掌>'
    
      def getSkill(self):
          print(f'使用了{self._skill}')
    
      def getMasterSkill(self):
          Master.__init__(self)
          Master.getSkill(self)
    
    xiaoming=Prentice()
    xiaoming.getSkill()
    xiaoming.getMasterSkill()
  • super(子类名,self):可以调用子类的父类
    #super()调用
    #适用于单继承的情况
    class Master:
      def __init__(self):
          self._skill = '<降龙十八掌>'
    
      def getSkill(self):
          print(f'使用了{self._skill}')
    
    class Prentice(Master):
      def __init__(self):
          self._skill='<降龙暴雨掌>'
    
      def getSkill(self):
          print(f'使用了{self._skill}')
    
      def getMasterSkill(self):
          super().__init__()
          super().getSkill()
    
    xiaoming=Prentice()
    xiaoming.getSkill()
    xiaoming.getMasterSkill()

四、封装

1.基本概念

  • 封装:将属性和方法写入类中

2.私有方法与私有属性

  • 私有属性和私有方法不继承给子类
    #私有方法与私有属性
    """
    规则:
    1.只能在类的内部使用
    2.通过公共方法的外部接口在类外使用
    定义:
    __变量名
    """
    class Father:
      def __init__(self):
          self.skill='<武林秘籍>'
          self.__money=50000 #私有属性
    
      def use_money(self,money): #通过公共方法访问私有属性
          self.__money-=money
    
      def check_money(self):
          print(self.__money)
    
    class Son(Father):
      pass
    
    xiaoming=Son()
    xiaoming.check_money()
    xiaoming.use_money(1000)
    xiaoming.check_money()

五、多态

1.基本概念

  • 多态:指同样的函数在不同场景下具有不同的状态

2.使用方法


"""
实现多态的三个条件:
1.有继承(存在父类,子类)
2.函数重写(子类重写父类的函数)
3.父类引用指向子类对象(子类对象传给父类对象调用者)
"""

"""
==在Python下的简单理解,就是将类名作为参数传递==
1.函数列表中存在一个形参,这个形参是“通用类名”,当传入不同的类时,可以简单认为直接将形参出现的位置全部替换为传入的类名
(类似于数学函数中代入未知数x的值,可以认为是将x出现的位置全部替换为该值)。
2.在函数体中,我们用这个形参去调用一个类的函数,例如调用get函数,当这个“通用类名”传入不同的类名时,就会发生替换,指向该类名的get函数
(传入Person就是Person.get,传入Worker就是Worker.get,传入Student就是Student.get)
"""
class Person:
    def get(self):
        print('人类')

class Worker(Person):
    def get(self):
        print('工人')

class Student(Person):
    def get(self):
        print('学生')

def who(person:Person):
    person.get()

people1=Person()
people2=Worker()
people3=Student()
who(people1)
who(people2)
who(people3)

3.抽象类和抽象方法

  • 定义方法:在函数体中使用pass
    """
    抽象类就是定义了一个标准,抽象方法就是标准中的具体条目
    抽象类的子类必须按照标准实现对应的方法
    """
    class Person:
      def get_age(self):
          pass
    
    class Student:
      def __init__(self,age):
          self.__age=age
    
      def get_age(self):
          print(self.__age)

4.类属性与类方法

#类属性与类方法
"""
类方法的定义:
使用装饰器'@classmethod'标识类方法,类方法的第一个参数必须为类对象,通常为cls
类方法的调用:
类名.方法名
对象名.类方法名

静态方法:
使用装饰器@staticmethod标识静态方法
静态方法的调用:
类名.静态方法名
对象名.静态方法名
"""
class Person:
    @classmethod
    def get_money(cls):
        print('赚钱中...')
    @staticmethod
    def get_race():
        print('人类')
    count=1

p1=Person()
p2=Person()
#1.类属性
print(Person.count)
print(p1.count)
print(p2.count)
#对象之间的属性是独立的
p1.count+=1
print(p1.count)
print(p2.count)
#2.类方法
Person.get_money()
p1.get_money()
#3.静态方法
Person.get_race()
p1.get_race()

5.回调

  • 回调:程序员调用框架时,框架回头调用程序员实现的函数

六、赋值与拷贝

1.引用赋值

  • Python中的变量使用引用赋值,变量实际上是对具体数值的引用
  • 也就是说,在Python中变量存储的是地址信息,在传递参数时,传递的也是地址信息

2.浅拷贝与深拷贝

  • 拷贝可变数据类型
    • Python中浅拷贝可变数据类型时,只拷贝第一层数据并开辟空间存储,其它层不拷贝
    • Python中深拷贝可变数据类型时,会拷贝多层数据直到可变数据类型的最深一层,并开辟空间存储
  • 拷贝不可变数据类型
    • Python中拷贝不可变数据类型,无论是深拷贝还是浅拷贝都是进行引用赋值

七、进阶函数使用

1.函数名的作用

  • 函数名存储的是函数所在空间的地址
  • 函数名()执行函数名所存放空间地址中的代码
  • 函数名可以作为函数参数

2.直接调用和间接调用

def func():
    print('func is called')

#1.直接调用
func()
#2.间接调用
def test(function):
    function()

test(func)
"""
将func作为实参传入function,实际上是传入了一个函数地址
再使用()执行地址中的代码
"""

3.闭包

  • 闭包的作用是保存函数中的变量,使其在函数销毁时不会被销毁,可以继续使用
    #闭包的语法
    """
    def 外部函数():
      def 内部函数():
          调用外部函数的变量
      return 内部函数名
    
    闭包的三个条件:
    (1)存在函数嵌套
    (2)内部函数调用外部函数的变量
    (3)返回内部函数的函数名
    """
    def func1(num1):
      #num1=5 #在内部定义变量也可以,但作为形参的好处是可以从实参那里修改变量值
      def func2(num2):
          #nonlocal num1 #通过这种方式可以获得外部函数变量的修改权限
          sum = num1 + num2
          print(f'sum={sum}')
      return func2
    
    #第一次调用返回func2的地址,此时定义了func2,func2中调用的func1的变量数值不变了
    myfunc1 = func1(10)
    #再次调用时会使用func2的函数体
    myfunc1(1)

4.装饰器

  • 装饰器的基本语法
    #装饰器
    #装饰器本质上是一个闭包
    def check(func):
      def inner():
          print("请先验证")
          func()
      return inner
    
    #1.方式一
    """
    装饰器(函数名)
    """
    def func1():
      print('执行功能:func1')
    
    f1 = check(func1)
    f1()
    
    #2.方式二
    """
    @装饰器
    def 函数名()
    """
    @ check
    def func2():
      print('执行功能:func2')
    
    func2()
  • 不同格式函数的装饰方法
    #不同函数的装饰器使用方法
    """
    inner与被装饰函数格式保持一致!!!
    """
    #1.无参无返回值
    def print_info_1(func):
      def inner():
          print('正在加载中')
          func()
      return inner
    
    @ print_info_1
    def func1():
      print('func1被调用')
    
    func1()
    #2.无参有返回值
    def print_info_2(func):
      def inner():
          print('正在加载中')
          return func() #被装饰函数有返回值,这里需要返回
      return inner
    
    @ print_info_2
    def func2():
      print('func2被调用')
      return 0
    
    print(func2())
    #3.有参有返回值
    def print_info_3(func):
      def inner(num): #被装饰函数有参数,这里要加参数
          print('正在加载中')
          return func(num)
      return inner
    
    @ print_info_3
    def func3(num1):
      print('func3被调用')
      return num1
    
    print(func3(5))
    #4.不定长参数函数
    def print_info(func):
      def inner(*args,**kwargs):
          print('正在加载中')
          func(*args,**kwargs)
      return inner
    
    @ print_info
    def function(*args,**kwargs):
      print(*args,**kwargs)
    
    function(1,2,3,4,5,6,7)
  • 多个装饰器装饰同一函数
    #多个装饰器装饰一个函数
    """
    (1)decorator2会先装饰function,此时形成一个整体
    (2)decorator1随后装饰这个整体,形成新的大整体
    (3)运行时,先运行decorator1,随后运行decorator2,最后运行function
    """
    def decorator1(func):
      def inner():
          print('初始化中...')
          func()
      return inner
    
    def decorator2(func):
      def inner():
          print('模块加载中...')
          func()
      return inner
    
    @ decorator1
    @ decorator2
    def function():
      print('程序运行中')
    
    function()
  • 带参数的装饰器
    #带参数的装饰器
    def flag_func(flag):
      def decorator(func):
          def inner(a,b):
              if flag=='+':
                  print('正在进行加法运算')
              elif flag=='-':
                  print('正在进行减法运算')
              return func(a,b)
          return inner
      return decorator
    
    @ flag_func('+')
    def sum(a,b):
      return a+b
    
    print(sum(1,2))
    
    @ flag_func('-')
    def decrease(a,b):
      return a-b
    
    print(decrease(1,2))

八、网络编程

1.网络编程三要素

  • IP:网络环境下每一台计算机的唯一标识,可以通过IP地址找到指定的计算机
  • 端口:用于标识进程的逻辑地址,通过端口找到指定的进程
  • 协议:通信的规则,符合协议允许进行通信

2.socket

#导入socket模块
import socket

#创建socket对象
tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
"""
socket.AF_INET:表示使用IPv4协议族
socket.SOCK_STREAM:表示使用面向连接的套接字

两者共同确定使用的协议是IPPROTO_TCP协议
"""

print(tcp_socket)

3.TCP连接

  • 服务端
    import socket
    
    #1.创建套接字
    server_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    # print(server_socket)
    
    #2.绑定端口
    ip = '127.0.0.1'
    port = 8888
    server_socket.bind((ip,port))
    
    #3.监听端口
    print('服务器监听中...')
    server_socket.listen()
    #此时server_socket成为被动套接字用于连接
    
    #4.接收请求
    connect_socket,client_ip = server_socket.accept()
    #此时connect_socket成为传输套接字,用于向客户端传输
    #client_ip为客户端的IP地址
    
    #5.接收数据
    recv_data = connect_socket.recv(1024)
    #接收到的数据是二进制数据,不可见,需要进行转换
    print(recv_data.decode(encoding='utf-8'))
    
    #6.返回数据
    connect_socket.send('服务器已接收'.encode(encoding='utf-8'))
    
    #7.关闭套接字
    connect_socket.close() #关闭连接套接字
    server_socket.close() #关闭监听套接字
  • 客户端
    
    import socket
    
    #1.创建套接字
    client_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    #2.连接服务器
    ip = '127.0.0.1'
    port = 8888
    client_socket.connect((ip,port))
    
    #3.发送数据
    client_socket.send('客户端信息测试'.encode(encoding='utf-8'))
    
    #4.接收数据
    recv_data = client_socket.recv(1024)
    print(recv_data.decode(encoding='utf-8'))
    
    #5.关闭套接字
    client_socket.close() 
  • 端口复用
    #将server.socket套接字的端口可以进行复用
    server_socket.setsockopt(socket.SOL_SOCKET,socket.SO_REUSEADDR,TRUE)

九、多任务开发——进程

1.并发与并行

  • 并发:在一段时间内,交替地执行程序(单个CPU)
  • 并行:在一段时间内,同时地执行程序(多个CPU)

2.进程

  • 进程:进程是CPU分配资源的最小单位,是正在运行的程序
  • 程序:是一段代码,表明了一段业务逻辑,并没有被执行
  • 同一个程序可以产生多个进程,例如同时开启多个QQ

3.多进程

  • 导包
    import multiprocessing
  • 创建进程对象
    multiprocessing.Process(group=None,target=None,name=None,args=(),kwargs={})
    
    """
    group:参数未使用,值始终为None
    target:表示要调用的对象,传入函数地址,生成子任务函数
    args:表示以元组的形式向子任务函数传递参数
    kwargs:表示以字典的形式向子任务函数传递参数
    name:设置子进程的名称
    """
  • 多进程(无参)
    import time
    
    #导入多进程模块
    import multiprocessing
    
    def func1():
      for i in range(100):
          print(f'func1运行中...{i+1}%')
          time.sleep(0.1)#设置休眠便于观察,与多进程无关
    
    def func2():
      for i in range(100):
          print(f'func2运行中...{i+1}%')
          time.sleep(0.1)
    
    #注意:多进程的代码必须写入“if __name__ == '__main__':"中
    if __name__ == '__main__':
      #1.创建子进程对象
      p1 = multiprocessing.Process(target=func1,name='p1')
      p2 = multiprocessing.Process(target=func2,name='p2')
      #2.启动子进程
      p1.start()
      p2.start()
      #3.设置主进程阻塞,等待子进程完成
      p1.join()
      p2.join()
    
      print('主进程结束')
  • 多进程(有参)
    import time
    
    #导入多进程模块
    import multiprocessing
    
    def func1(name):
      for i in range(100):
          print(f'欢迎您{name},func1运行中...{i+1}%')
          time.sleep(0.1)#设置休眠便于观察,与多进程无关
    
    def func2(name):
      for i in range(100):
          print(f'欢迎您{name},func2运行中...{i+1}%')
          time.sleep(0.1)
    
    #注意:多进程的代码必须写入“if __name__ == '__main__':"中
    if __name__ == '__main__':
      #1.创建子进程对象
      p1 = multiprocessing.Process(target=func1,name='p1',args=('小明',))#元组中只有一个元素,需要加','
      p2 = multiprocessing.Process(target=func2,name='p2',kwargs={'name':'小红'})
      #2.启动子进程
      p1.start()
      p2.start()
      #3.设置主进程阻塞,等待子进程完成
      p1.join()
      p2.join()
    
      print('主进程结束')
  • 获取进程编号
    • 获取当前进程编号
    #方式1:使用os模块
    import os
    pid = os.getpid()
    
    #方式2:使用multiprocessing模块
    import multiprocessing
    pid = multiprocessing.current_process().pid
    • 获取父进程编号
    import os
    ppid = os.getppid()

4.注意点

import multiprocessing

my_list = []

print('这是一个全局打印')

def func1():
    for i in range(10):
        my_list.append(i)
        print(my_list)

def func2():
    for i in range(10):
        my_list.append(i*10)
        print(my_list)

if __name__ == '__main__':
    p1 = multiprocessing.Process(target=func1)
    p2 = multiprocessing.Process(target=func2)

    p1.start()
    p2.start()

    p1.join()
    p2.join()

    #主进程输出my_list,结果为空表
    print(my_list)

"""
操作系统在创建子进程时,会将主进程的资源拷贝给子进程
子进程中的修改时对自身拷贝的资源进行修改,对主进程的资源没有影响

子进程会拷贝主进程的所有代码
但因为子进程中的__name__ != '__main__',因此不会执行其中的内容

如果在主进程中不使用”if __name__ == '__main__':"
那么子进程也会创建子进程,陷入死循环
"""

5.改变子进程退出方式

  • 主进程默认会等待所有子进程结束之后再销毁
  • 不让主进程等待子进程
    • 方式一:设置守候进程
    import time
    import multiprocessing
    
    def func1():
        for i in range(100):
            print(f'func1运行中...{i+1}%')
            time.sleep(0.1)
    
    def func2():
        for i in range(100):
            print(f'func2运行中...{i+1}%')
            time.sleep(0.1)
    
    if __name__ == '__main__':
        p1 = multiprocessing.Process(target=func1,name='p1',daemon=True)#将daemon设置为True
        p2 = multiprocessing.Process(target=func2,name='p2',daemon=True)
        p1.start()
        p2.start()
    
        time.sleep(2)
        print('主进程结束')
    
    """
    设置守候进程时:
    主进程结束之后,子进程会被系统接管
    """
    • 方式二:暴力直接结束子进程
    import time
    import multiprocessing
    
    def func1():
        for i in range(100):
            print(f'func1运行中...{i+1}%')
            time.sleep(0.1)
    
    def func2():
        for i in range(100):
            print(f'func2运行中...{i+1}%')
            time.sleep(0.1)
    
    if __name__ == '__main__':
        p1 = multiprocessing.Process(target=func1,name='p1')
        p2 = multiprocessing.Process(target=func2,name='p2')
        p1.start()
        p2.start()
    
        time.sleep(2)
        p1.terminate() #直接结束
        p2.terminate()
        print('主进程结束')
    
    """
    直接结束子进程,不建议使用!!!
    此时子进程占用的资源没有被回收,成为了僵尸进程!!!
    """

十、多任务开发——线程

1.线程

  • 线程是CPU调度资源的最小单位

2.多线程

  • 导入线程模块
    import threading
  • 创建线程对象
    线程对象 = threading.Thread(target=任务名)
  • 启动线程
    线程对象.start()
  • 多进程(无参)
    import time
    
    #导入线程模块
    import threading
    
    def func1():
      for i in range(100):
          print(f'func1运行中...{i+1}%')
          time.sleep(0.1)
    
    def func2():
      for i in range(100):
          print(f'func2运行中...{i+1}%')
          time.sleep(0.1)
    
    if __name__ == '__main__':
      #1.创建线程对象
      th1 = threading.Thread(target=func1)
      th2 = threading.Thread(target=func2)
    
      #2.启动线程
      th1.start()
      th2.start()
    
      #3.主线程阻塞等待
      th1.join()
      th2.join()
    
      print('主线程结束')
  • 多进程(带参)
    import time
    
    #导入线程模块
    import threading
    
    def func1(name):
      for i in range(100):
          print(f'当前用户{name}:func1运行中...{i+1}%')
          time.sleep(0.1)
    
    def func2(name):
      for i in range(100):
          print(f'当前用户{name}:func2运行中...{i+1}%')
          time.sleep(0.1)
    
    if __name__ == '__main__':
      #1.创建线程对象
      th1 = threading.Thread(target=func1,args=('Jack',))
      th2 = threading.Thread(target=func2,kwargs={'name':'Tom'})
    
      #2.启动线程
      th1.start()
      th2.start()
    
      #3.主线程阻塞等待
      th1.join()
      th2.join()
    
      print('主线程结束')

3.线程间执行顺序是无序的

import time

#导入线程模块
import threading

def func1(name):
    for i in range(100):
        print(f'当前用户{name}:func1运行中...{i+1}%')
        time.sleep(0.1)

def func2(name):
    for i in range(100):
        print(f'当前用户{name}:func2运行中...{i+1}%')
        time.sleep(0.1)

if __name__ == '__main__':
    #1.创建线程对象
    th1 = threading.Thread(target=func1,args=('Jack',))
    th2 = threading.Thread(target=func2,kwargs={'name':'Tom'})
    th3 = threading.Thread(target=func1,args=('Mike',))
    th4 = threading.Thread(target=func2,kwargs={'name':'Lisa'})
    th5 = threading.Thread(target=func1,args=('Lily',))
    th6 = threading.Thread(target=func2,kwargs={'name':'Jhon'})

    #2.启动线程
    th1.start()
    th2.start()
    th3.start()
    th4.start()
    th5.start()
    th6.start()

    #3.主线程阻塞等待
    th1.join()
    th2.join()
    th3.join()
    th4.join()
    th5.join()
    th6.join()

    print('主线程结束')

"""
多次运行,观察输出结果
"""

4.改变子线程退出方式

  • 创建守候子线程
    import time
    
    #导入线程模块
    import threading
    
    def func1():
      for i in range(100):
          print(f'func1运行中...{i+1}%')
          time.sleep(0.1)
    
    def func2():
      for i in range(100):
          print(f'func2运行中...{i+1}%')
          time.sleep(0.1)
    
    if __name__ == '__main__':
      #1.创建线程对象
      th1 = threading.Thread(target=func1,daemon=True) #主线程结束,子线程结束
      th2 = threading.Thread(target=func2,daemon=True)
    
      #2.启动线程
      th1.start()
      th2.start()
    
      print('主线程结束')

5.子线程间共享全局变量

import time

#导入线程模块
import threading

list = []

def func1():
    for i in range(10):
        list.append(i)
        print(list)
        time.sleep(0.2)

def func2():
    for i in range(10):
        list.append(i*10)
        print(list)
        time.sleep(0.2)

if __name__ == '__main__':
    #1.创建线程对象
    th1 = threading.Thread(target=func1)
    th2 = threading.Thread(target=func2)

    #2.启动线程
    th1.start()
    th2.start()

    th1.join()
    th2.join()

    print(list)

    print('主线程结束')

6.子线程共享全局变量导致的数据不安全

import time

#导入线程模块
import threading

a=0

def func1():
    for i in range(1000000):
        global a
        a += 1
    print('func1:',a)

def func2():
    for i in range(1000000):
        global a
        a += 1
    print('func2:',a)

if __name__ == '__main__':
    #1.创建线程对象
    th1 = threading.Thread(target=func1)
    th2 = threading.Thread(target=func2)

    #2.启动线程
    th1.start()
    th2.start()

    th1.join()
    th2.join()

    print('main:',a)

    print('主线程结束')

"""
当线程1读取全局变量,却还未进行修改时,发生CPU调度
此时线程2读取全局变量,进行修改

例如:
线程1读取到a=0,发生CPU调度
线程2读取到a=0,修改a=1,发生CPU调度
线程1修改a=1
但我们预期的结果是a在线程1中+1,在线程2中+1,结果为2
也就是说,这种情况下,a少加了一个1
"""

7.互斥锁

  • 作用:对共享数据进行锁定,保证同一时刻只有一个线程去操作
  • 互斥锁的使用流程
    • 创建互斥锁
    互斥锁对象 = threading.Lock()
    • 使用互斥锁
    #上锁
    互斥锁对象.acquire()
    
    #解锁
    互斥锁对象.release()
    • 示例
    import time
    
    #导入线程模块
    import threading
    
    #实例化线程锁
    mutex = threading.Lock()
    
    a=0
    
    def func1():
        for i in range(1000000):
            #上锁
            mutex.acquire()
    
            global a
            a += 1
    
            #解锁
            mutex.release()
        print('func1:',a)
    
    def func2():
        for i in range(1000000):
            #上锁
            mutex.acquire()
    
            global a
            a += 1
    
            # 解锁
            mutex.release()
        print('func2:',a)
    
    if __name__ == '__main__':
        #1.创建线程对象
        th1 = threading.Thread(target=func1)
        th2 = threading.Thread(target=func2)
    
        #2.启动线程
        th1.start()
        th2.start()
    
        th1.join()
        th2.join()
    
        print('main:',a)
    
        print('主线程结束')
  • 死锁:获得互斥锁的线程没有及时释放锁,导致其他线程无法获得锁

十一、其他Python语法

1.with语句

  • with:用于简单高效地处理资源和异常
  • with的使用
    #使用with语句
    #1.正常运行时
    with open('1.txt','w') as fp:
      fp.write('hello world!')
    #2.出现异常时
    with open('1.txt','r') as fp:
      fp.write('hello world!')
    """
    使用with出现异常时,尽管还是会报错
    但已经打开的文件fp会被关闭,占用的资源会被回收
    """
  • 上下文管理器
    • 定义:一个类只要实现了两个魔法方法__enter__()__exit__(),通过该类创建的对象就可以称之为上下文管理器
    • with管理的对象是上下文管理器
    • 自定义上下文管理器
    class MyFile:
        def __init__(self,file_name,file_mode):
            self.file_name = file_name
            self.file_mode = file_mode
            self.file = None
    
        #重写__enter__
        def __enter__(self):
            print('此处是上文')
            self.file = open(self.file_name,self.file_mode)
            return self.file #这个返回的是文件对象,不能调用类中的方法
            #return self #这个返回值可以调用类中的方法,但在下面使用时要注意使用f.file.write()
    
        #重写__exit__
        def __exit__(self,exc_type,exc_val,exc_tb):#这个参数一般是固定的
            print('此处是下文')
            self.file.close()
    
    if __name__ == '__main__':
        with MyFile('1.txt','w') as f:
            f.write('hello world!!!')

2.Python生成器

  • 定义:Python生成器根据程序员指定的规则生成数据,条件不成立时生成结束
  • 特点:生成器生成的数据是多次的,当数据被使用才会生成下一个,节省内存空间
  • 创建生成器
    • Python生成器并没有直接生成所有元素,其存储的只是生成数据的规则,当使用数据时才会生成
    • 使用生成器推导式
    #1.生成器推导式
    my_generator1 = (i for i in range(10))
    my_generator2 = (i*10 for i in range(10))
    • 使用yield关键字
    #2.yield
    def generator(num):
        for i in range(num):
            print('生成开始')
            yield i
            print('生成结束')
    
    my_generator2 = generator(10)
    print(next(my_generator2))
    print(next(my_generator2))
    print(next(my_generator2))
    print(next(my_generator2))
    
    """
    通过单步调试可以发现:
    当程序运行到yield时,会产生阻塞,将i值返回
    直到下一次迭代时,阻塞结束继续向下运行,直到再次遇到yield阻塞
    """
  • 遍历生成器
    • 使用next单步迭代
    #next:可以单步迭代生成器
    next(my_generator2)
    • 使用for循环
    #1.通过for循环
    #使用for循环时,无需进行异常处理
    for i in my_generator1:
        print(i)
    • 使用while循环
    #2.通过while循环
    #使用while循环时,需要进行异常处理
    while True:
        try:
            print(next(my_generator2))
        except:
            break

十二、正则表达式

1.概念

正则表达式描述了一种字符串的格式用于字符串的匹配

2.Python中使用正则表达式

  • 导入模块
    import re
  • 使用正则
    import re
    
    #1.使用match方法进行匹配操作
    result1 = re.match('.e.','hello world!')
    """
    match:
    第一个参数是正则表达式
    第二个参数是要进行匹配的原字符串
    
    match会从左向右严格进行匹配
    """
    #2.使用search方法进行扫描操作
    result2 = re.search('d.*','圆周率大约是3.14,因此我们可以粗略地求圆的面积')
    """
    search:
    第一个参数是正则表达式
    第二个参数是要进行匹配的原字符串
    """
    #3.替换字符
    sentence = '订购热线:xxx-xxx-xxxx!添加微信:WeChat_xxx,获取《20%折扣券》#限时优惠#'
    p = r'x|#|-|_' #要去除的字符的集合
    r = re.compile(pattern=p) #
    str1 = r.sub('',sentence) #将第二个参数中符合条件的字符替换为第一个参数
    print(str1)
    #4.replace方法
    str2 = sentence.replace(':',':') #将sentence中符合第一个参数的字符替换为第二个参数
    print(str2)
    
    #使用group方法进行提取数据
    if result1:
      print(result1.group())
    else:
      print('不存在这样的字符串')
    
    if result2:
      print(result2.group())
    else:
      print('不存在这样的字符串')
    

3.正则匹配

  • 单个字符匹配
    匹配符 功能
    . 匹配任意1个字符(n除外)
    [] 匹配[]中列举的字符
    [^指定字符] 匹配除指定字符之外的字符
    d 匹配数字
    D 匹配非数字
    s 匹配空白
    S 匹配非空白
    w 匹配普通字符(a-z,A-Z,0-9,_,汉字)
    W 匹配特殊字符
  • 多个字符匹配
    匹配符 功能
    * 匹配前一个字符出现0次或无限次
    + 匹配前一个字符出现1次或无限次
    ? 匹配前一个字符出现1次或0次
    {m} 匹配前一个字符出现m次
    {m,n} 匹配前一个字符出现的次数属于区间[m,n]
  • 头尾匹配
    匹配符 功能
    ^ 匹配字符串开头
    $ 匹配字符串结尾
  • 匹配分组
    匹配符 功能
    | 或者
    () 将括号内的字符作为一个分组
    转义字符

4.分组引用

  • 如果在正则表达式中分组了,那么使用group函数提取时,可以使用数字提取对应的组(0表示全部,从1开始对应自左向右的分组)
    import re
    
    sentence = '订购热线:123-456-7890!添加微信:WeChat_xxx,获取《20%折扣券》#限时优惠#'
    
    result = re.match('w*:(d*-d*-d*)Ww*:(w*)',sentence)
    if result:
      print(result.group(0))
      print(result.group(1))
      print(result.group(2))
  • 在正则表达式中可以通过\数字引用对应的分组
    import re
    
    sentence = '<html>www.xxx.com</html>'
    
    result = re.match('<([a-z]{4})>.*</\1>',sentence) #此处的‘\1'表示引用第一个分组
    if result:
      print(result.group())
  • 可以多次使用分组引用
    import re
    
    sentence = '<html><h1>www.xxx.com</h1></html>'
    
    result = re.match('<([a-z]{4})><(wd)>.*</\2></\1>',sentence)
    if result:
      print(result.group())
  • 可以为分组起别名以便调用,语法为:起名(?P<name>)调用(?P=name)
    import re
    
    sentence = '<html><h1>www.xxx.com</h1></html>'
    
    result = re.match('<(?P<html>[a-z]{4})><(?P<h1>wd)>.*</(?P=h1)></(?P=html)>',sentence)
    if result:
      print(result.group())

十三、Python中的数据结构

1.定义

  • 数据结构:是指存储时组织数据的方式,对于同样的数据采取不同的方式进行存储(例如用列表存储和用字典存储),就是采用了不同的数据结构

2.顺序表

  • 一体式存储:存储的数据类型一致
  • 分离式存储:由于存储的数据类型不一定相同,分离式存储会存储数据的首地址

3.链表

  • 链表的存储不需要连续的存储空间
#结点的定义
class LinkListNode:
    def __init__(self,item=None):
        self.item = item #结点的数据域
        self.next = None #结点的链接域

#单链表的定义
class LinkList:
    def __init__(self,node=None):
        self.head = node

    #1.判空
    def is_empty(self):
        if self.head is None:
            return True
        else:
            return False

    #2.求长度
    def length(self):
        cur = self.head
        length = 0
        while cur is not None:
            length += 1
            cur = cur.next
        return length

    #3.遍历
    def travel(self):
        cur = self.head
        while cur is not None:
            print(cur.item,end=' ')
            cur = cur.next
        print('')

    #4.头插
    def add(self,item):
        new_node = LinkListNode(item)
        new_node.next = self.head
        self.head = new_node

    #5.尾插
    def append(self,item):
        new_node = LinkListNode(item)
        if self.is_empty():
            self.head = new_node
        else:
            cur = self.head
            while cur.next is not None:
                cur = cur.next
            new_node.next = None
            cur.next = new_node

    #6.按下标插入
    def insert(self,pos,item):
        if pos <= 0:
            self.add(item)
        elif pos > self.length()+1:
            self.append(item)
        else:
            new_node = LinkListNode(item)
            cur = self.head
            for i in range(pos-1):
                cur = cur.next
            new_node.next = cur.next
            cur.next = new_node

    #7.删除
    def delete(self,item):
        cur = self.head
        pre = None
        while cur is not None:
            if cur.item == item:
                if cur == self.head:
                    self.head = cur.next
                else:
                    pre.next = cur.next
                return
            else:
                pre = cur
                cur = cur.next

    #8.查找
    def search(self,item):
        cur = self.head
        pos = 0
        while cur is not None:
            if cur.item == item:
                return pos
            else:
                cur = cur.next
                pos += 1
        return -1

十四、Python中的排序算法

1.排序算法

  • 算法:是指为了实现业务目标而采用的思路和方法,类似于数学题中的不同解法,不同的算法可能有不同的效率
  • 稳定性:排序算法的稳定性是指相同的元素,在经过排序算法之后,其相对位置不发生改变

2.冒泡排序

def bubble_sort(list,len):
    """
    :param list:传入要排序的列表
    :param len: 传入列表的长度
    :return:None
    """
    #外层控制循环次数,总次数=列表长度-1
    for i in range(len-1):
        flag = 0
        #内层控制遍历,遍历的长度是还未排序的列表长度-1
        for j in range(len-i-1):
            if list[j] > list [j+1]:
                list[j],list[j+1] = list[j+1],list[j]
                flag = 1
        if flag == 0: #说明内部循环没有发生交换,列表已经有序,可以提前退出
            break
    #上面循环次数中都-1,是因为遍历到未排序列表的倒数第二个元素时,就已经和最后一个元素比较了

3.选择排序

def select_sort(list,len):
    """
    :param list: 传入要排序的列表
    :param len: 传入要排序列表的长度
    :return: None
    """
    #选择排序是找到元素的最终位置,将列表中的len-1个元素确定位置之后,最后一个元素的位置就自然确定了
    for i in range(len-1):
        min = i #假设第i个元素是最小的
        for j in range(i+1,len): #因为已经假设第i位最小,所以不需要比较第i位和第i位
            if list[j] < list[min]: #找到更小的则替换min
                min = j
        list[i],list[min] = list[min],list[i]

4.快速排序

def quick_sort(list,left,right):
    """
    :param list:传入要排序的列表
    :param left: 要排序子列表的左侧下标
    :param right: 要排序子列表的右侧下标
    :return: None
    """
    #递归出口
    if left >= right:
        return
    #记录分割元素
    mid = list[left]
    #定义左右指针
    left_point = left
    right_point = right
    #当左指针在右指针左侧时,重复循环
    while left_point < right_point:
        #当右侧大于等于分割元素,使右指针向左移动
        while list[right_point] >= mid and left_point < right_point:
            right_point -= 1
        #当右侧小于分割元素,将右指针的元素放入左指针的位置
        list[left_point] = list[right_point]
        #当左侧元素小于分割元素,使左指针向右移动
        while list[left_point] <= mid and left_point < right_point:
            left_point += 1
        #当左侧大于分割元素,将左指针的元素放入右指针的位置
        list[right_point] = list[left_point]
    #此时左指针与右指针相遇,说明左指针指向的位置,左侧元素全部小于分割元素,右侧元素全部大于分割元素,将分割元素放入即可
    list[left_point] = mid
    #递归,将分割元素左右视为两个要排序的子列表
    quick_sort(list,left,left_point-1)
    quick_sort(list,left_point+1,right)

学习是一段漫长的旅途