Python面向对象编程核心概念

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

核心概念

Python 是一门面向对象的语言(同时也支持面向过程编程)。要掌握面向对象编程的基本语法,需要先理解两个核心概念:类和对象。

1. 类

类是对现实事物的抽象描述。例如定义一个汽车类,包含行驶行为:

# 定义类
class Car:
    # 定义类方法
    def run(self):
        print("汽车正在行驶...")

2. 对象

对象是现实事物的具体实例:

# 创建对象
car = Car()
# 调用方法
car.run()

3. self 关键字

self 是 Python 内置关键字,指向对象实例本身:

class Car:
    def run(self):
        print(f"self 的值: {self}")
        print("汽车正在行驶...")

# 创建对象
car = Car()
print(f"car 对象: {car}")
car.run()

类内部调用方法示例:

class Car:
    def run(self):
        print("汽车正在行驶...")
        
    def work(self):
        self.run()  # 内部调用方法

car = Car()
car.work()

手机类示例:

class Phone:
    def open(self):
        print("手机开机")
    
    def close(self):
        print("手机关机")
    
    def take_photo(self):
        print("拍照功能")

my_phone = Phone()
my_phone.open()
my_phone.take_photo()
my_phone.close()

self 关键字的作用:

  • Python 自动将调用方法的实例作为 self 参数传递
  • 用于区分不同对象调用相同方法
  • my_phone.open() 等价于 Phone.open(my_phone)

4. __init__() 方法

在对象创建时自动调用,用于初始化属性:

class Car:
    def __init__(self, color, number):
        self.color = color
        self.number = number

car = Car("红色", 4)
print(car.number)

5. __str__() 方法

自定义对象打印输出:

class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def __str__(self):
        return f"姓名: {self.name}, 年龄: {self.age}"

p = Person("张三", 25)
print(p)

6. __del__() 方法

对象销毁时自动调用:

class Example:
    def __del__(self):
        print("对象被销毁")

obj = Example()
del obj

魔法方法总结

  1. 魔法方法:由双下划线包围,在特定场景自动调用的方法
  2. __init__():对象创建时初始化属性
  3. __str__():自定义对象打印输出
  4. __del__():对象销毁时释放资源

案例:体重管理

class Student:
    def __init__(self):
        self.current_weight = 100
    
    def run(self):
        self.current_weight -= 0.5
        print(f"跑步一次,体重减少0.5kg,当前体重: {self.current_weight}kg")
    
    def eat(self):
        self.current_weight += 2
        print(f"大吃一顿,体重增加2kg,当前体重: {self.current_weight}kg")

student = Student()
student.run()
student.eat()

类定义语法

class ClassName:
    def __init__(self, parameters):
        self.attribute = parameters
    
    def method(self):
        return self.attribute

python的继承

单继承

class Parent:
    def parent_method(self):
        print("父类方法")

class Child(Parent):
    def child_method(self):
        print("子类方法")

多继承

class Father:
    def method(self):
        print("父亲的方法")

class Mother:
    def method(self):
        print("母亲的方法")

class Child(Father, Mother):
    pass

super() 使用

class Parent:
    def __init__(self):
        self.value = 10

class Child(Parent):
    def __init__(self):
        super().__init__()
        self.value += 5

多层继承

class Grandparent:
    pass

class Parent(Grandparent):
    pass

class Child(Parent):
    pass

python的封装

隐藏内部实现,仅暴露必要接口

私有属性

class BankAccount:
    def __init__(self):
        self.__balance = 0
    
    def get_balance(self):
        return self.__balance

私有方法

class Secure:
    def __private_method(self):
        print("私有操作")

python的多态

1多态是指:多种状态,比如一个函数在不同的状态下有不同的状态

class Animal: # 父类用来确定有哪些方法(父类制定接口标准)
    def speak(self): # 具体的方法实现有子类来实现(子类实现接口标准)
        pass

class Dog(Animal):
    def speak(self):
        return "汪汪!"

class Cat(Animal):
    def speak(self):
        return "喵喵!"
# 这种写法,就叫做抽象类(也可以称之为接口)
# 抽象类:含有抽象方法的类称之为抽象类
# 抽象方法:方法体是空实现的(pass)称之为抽象方法
# 抽象类(接口)
# 抽象类就好比定义一个标准,包含了一些抽象的方法,要求子类必须实现。

多态成立的三个条件

  • 有继承 (定义父类、定义子类,子类继承父类)
  • 函数重写 (子类重写父类的函数)
  • 父类引用指向子类对象 (子类对象传给父类对象调用者)

多态的好处

  • 在不改变框架代码的情况下,通过多态语法轻松的实现模块和模块之间的解耦合;实现了软件系统的可拓展
  • 解耦合的大白话解释:搭建的平台函数def object_play(herofighter:HeroFighter, enemyfighter:EnemyFighter) 相当于任务的调用者;子类、孙子类重写父类的函数,相当于子任务;相当于任务的调用者和任务的编写者进行了解耦合
  • 对可拓展的大白话解释: 搭建的平台函数def object_play(herofighter:HeroFighter, enemyfighter:EnemyFighter),在不做任何修改的情况下,可以调用后来人写的代码
  • 对“继承和多态对比理解”大白话解释:     继承相当于:孩子可以复用老爹的东西。     多态相当于:老爹框架,不做任何修改的情况下,可以可拓展的使用后来人(孩子)写的东西。

什么是抽象类(接口)

包含抽象方法的类,称之为抽象类。抽象方法是指:没有具体实现的方法(pass)称之为抽象方法

5抽象类的作用

多用于做顶层设计(设计标准),以便子类做具体实现。 也是对子类的一种软性约束,要求子类必须复写(实现)父类的一些方法 并配合多态使用,获得不同的工作状态。通过多态语法,实现模块和模块之间的解耦合

面向对象的其他特性

属性

类或对象中的属性 都属于属性

class Person:
    def __init__(self, name):
        self.name = name  # 对象属性

person1 = Person("Alice")
print(person1.name)  # 输出: Alice
 

类属性

类属性,指的就是类所拥有的属性,它被共享于整个类中(即都可以直接调用)。

class Person(object):
    # 类属性
    count = 1
# 访问
print(Person.count)  # 推荐
# 创建对象: 开辟一块新的空间
person = Person()
print(person.count)

类方法

所谓类方法,指的是类所拥有的方法,并需要使用装饰器@classmethod来标识其为类方法,同时一定要注意的是对于类方法的第一个参数必须是类对象,通常以cls作为第一个参数名。

class Dog(object):
    @classmethod
    def eat(cls):
        print("小狗都喜欢啃硬骨头...")
Dog.eat()   # 类名直接访问
dog = Dog()
dog.eat()	   # 对象名访问

静态方法

静态方法需要通过装饰器@staticmethod来标识其为静态方法,且静态方法不需要多定义参数。

列如:开发一款游戏要显示初始化操作界面,分别有开始、暂停、退出等按键。

class Game(object):
    @staticmethod
    def show_menu():
        print("="*20)
        print("【1】开始游戏;")
        print("【2】暂停;")
        print("【0】结束.")

Game.show_menu() # 类名直接访问
game = Game()
game.show_menu() # 对象名访问

实例开发,静态类方法使用的多

类方法和静态方法?     

类方法,指的是类所拥有的方法,并需要使用     [装饰器]@classmethod标识其为类方法     

静态方法 需要通过装饰器@staticmethod标识其为静态方法,     且静态方法不需要定义参数

闭包

闭包(Closure)是指一个函数能够访问并记住其外部作用域的变量,即使该函数在外部作用域之外执行

# 外部函数
def 外部函数名(外部参数):
    # 内部函数
    def 内部函数名(内部参数):
        ...[使用外部函数的变量]
    return 内部函数名      # 闭包

# 外部函数
def outer_func(x):  # 外部参数x
    # 内部函数
    def inner_func(y):  # 内部参数y
        return x + y  # 使用外部函数的变量x
    return inner_func  # 闭包形成点
# 闭包调用
closure = outer_func(10)  # x被固定为10
print(closure(5))  # 输出15 (10+5)
print(closure(20))  # 输出30 (10+20)

闭包的构成条件

  • 有嵌套:在函数嵌套(函数里面再定义函数)的前提下;
  • 有引用:内部函数使用了外部函数的变量(还包括外部函数的参数);
  • 有返回:外部函数返回了内部函数名。

闭包特点

  • 闭包可以记住并访问外部函数的变量
  • 即使外部函数已经执行完毕,闭包仍能访问那些变量
  • 每次调用外部函数都会创建新的闭包实例

闭包的好处

数据封装与私有化
闭包可以创建私有变量,避免全局污染。外部无法直接访问闭包内部的变量,只能通过暴露的函数间接操作,增强了代码的安全性和模块化。例如:

class Counter:
    def __init__(self):
        self._count = 0
    
    def increment(self):
        self._count += 1
    
    def get_count(self):
        return self._count

counter = Counter()
counter.increment()
print(counter.get_count())  # 输出1
 

闭包的缺陷

内存泄漏风险
闭包会保留对外部变量的引用,导致垃圾回收器无法释放这些变量。尤其在循环中误用闭包时,可能积累大量无用内存。例如:

def leak_memory():
    data = ['*'] * 1000000  # 创建一个大列表
    return lambda: print(len(data))  # 返回闭包函数,保持对data的引用

leaked = leak_memory()  # data列表因闭包引用无法被回收
 

性能开销
每次创建闭包都会生成新的作用域链,相比普通函数需要更多内存和更慢的访问速度。高频调用的场景下可能影响性能。

调试复杂性
闭包的变量引用链可能较长,调试时难以追踪变量当前值。尤其在嵌套闭包中,错误堆栈信息可能不够直观

闭包的缺陷与使用价值

闭包可能导致内存泄漏(因变量被长期引用无法释放),但它的核心价值在于:

  • 封装私有变量:隐藏内部状态,避免全局污染,实现数据隔离。
  • 延长变量生命周期:函数外部能访问函数内部的变量,适合缓存或持久化场景。
  • 函数式编程基础:实现柯里化、高阶函数等模式,提升代码灵活性和复用性。

    nonlocal关键字介绍

    nonlocal: 它是Python内置的关键字, 可以实现 在内部函数中 修改外部函数的 变量值.

    nonlocal的使用场景

    当需要在嵌套函数中修改外层函数中的变量时,可以使用nonlocal。这与global关键字类似,但nonlocal用于嵌套函数中的外层函数变量,而global用于全局变量。

    def outer_function():
        x = 10
        def inner_function():
            nonlocal x
            x = 20
        inner_function()
        print(x)  # 输出20
     
    

    nonlocal与global的区别

    nonlocal用于访问嵌套函数外层函数的变量,而global用于访问模块级别的全局变量。nonlocal不能用于全局变量,否则会引发语法错误。

    nonlocal的注意事项

    使用nonlocal时,变量必须在外层函数中已定义,否则会引发语法错误。nonlocal不能用于全局变量,只能用于嵌套函数的外层函数变量。

    nonlocal的实际应用

    nonlocal常用于闭包或装饰器中,用于保存和修改外层函数的状态。例如,在计数器或状态机中,nonlocal可以方便地修改外层函数的变量。

    def counter():
        count = 0
        def increment():
            nonlocal count
            count += 1
            return count
        return increment
    
    c = counter()
    print(c())  # 输出1
    print(c())  # 输出2
     
    

     装饰器

    装饰器的作用是不改变原有函数的基础上,给原有函数增加额外功能。 装饰器本质上就是一个闭包函数。

    代码案例:例如,发表评论前,都是需要先登录的。 先定义有发表评论的功能函数,然后在不改变原有函数的基础上,需要提示用户要先登录。

    # 1定义一个装饰器(装饰器的本质是闭包)
    def check(fn):
        def inner():
        print("登陆验证。。。")
        fn()
    
     return inner
    
    # 2.需要被装饰的函数
    def comment():
        print("发表评论")
    # 3.使用装饰器装饰函数(增加一个登陆功能)
    comment = check(comment)
    comment()
    

    装饰器的构成条件

    • 有嵌套:在函数嵌套(函数里面再定义函数)的前提下;
    • 有引用:内部函数使用了外部函数的变量(还包括外部函数的参数);
    • 有返回:外部函数返回了内部函数名;
    • 有额外功能:给需要装饰的原有函数增加额外功能。

    语法糖:方式2: 语法糖: @装饰器名   (这种方式最常见也最常用) 

    # 1. 定义装饰器
    def check(fn):
        def inner():
            print("登陆验证。。。")
            fn()
        return inner
    
    # 2. 使用语法糖装饰函数
    @check
    def comment():
        print("发表评论")
    
    # 3. 调用被装饰的函数
    comment()
     
    

    单个装饰器的使用

    def decorator(func):
        def wrapper(*args, **kwargs):
            print("装饰器前置操作")
            result = func(*args, **kwargs)
            print("装饰器后置操作")
            return result
        return wrapper
    
    @decorator
    def target_function():
        print("目标函数执行")
    
    target_function()
     
    

    多个装饰器的叠加使用

    def decorator1(func):
        def wrapper(*args, **kwargs):
            print("装饰器1前置操作")
            result = func(*args, **kwargs)
            print("装饰器1后置操作")
            return result
        return wrapper
    
    def decorator2(func):
        def wrapper(*args, **kwargs):
            print("装饰器2前置操作")
            result = func(*args, **kwargs)
            print("装饰器2后置操作")
            return result
        return wrapper
    
    @decorator1
    @decorator2
    def target_function():
        print("目标函数执行")
    
    target_function()
     
    

    多层装饰器的工作原理

    当使用多个装饰器时,最靠近函数定义的装饰器最先被应用,但最后执行:

    1. @decorator2 最先被应用到原始函数上,返回包装后的函数。
    2. @decorator1 再被应用到已经被 decorator2 装饰过的函数上。
    3. 调用函数时,decorator1 的 wrapper 最先执行,然后才是 decorator2 的 wrapper。

    Python 深浅拷贝的区别

    1所谓的深浅拷贝分别指的是:

    • 浅拷贝:copy模块的copy()
    • 深拷贝:拷贝deepcopy()函数

    2大白话介绍深浅拷贝:深拷贝考的多,浅拷贝考的少       

    3深浅拷贝主要是针对可变类型来讲的,深浅拷贝拷贝所有层(可变),浅拷贝只拷贝第一层(可变)如果针对不可变类型,则用法一致,则用法和普通赋值一样,并无区别

    浅拷贝(Shallow Copy)

    浅拷贝仅复制对象的顶层结构,不会复制嵌套对象。修改嵌套对象时,原对象和拷贝对象会同时受影响。
    使用 copy.copy() 或某些对象的自带方法(如列表的 list.copy())实现:

    import copy
    lst1 = [1, [2, 3], 4]
    lst2 = copy.copy(lst1)
    lst1[1][0] = 99  # 修改嵌套对象
    print(lst2)  # 输出 [1, [99, 3], 4](受影响)
     
    

    深拷贝(Deep Copy)
    深拷贝会递归复制所有嵌套对象,生成完全独立的副本。修改任何部分均不会影响原对象。
    使用 copy.deepcopy() 实现:

    import copy
    lst1 = [1, [2, 3], 4]
    lst2 = copy.deepcopy(lst1)
    lst1[1][0] = 99  # 修改嵌套对象
    print(lst2)  # 输出 [1, [2, 3], 4](不受影响)
     
    

    适用场景

    浅拷贝适用情况

    • 对象无嵌套结构或无需独立修改嵌套内容。
    • 需要快速复制且内存占用较小。

    深拷贝适用情况

    • 对象包含多层嵌套且需完全独立修改。
    • 避免共享可变嵌套对象引发的意外副作用。

     注意事项

    • 深拷贝可能因递归复制导致性能开销,尤其对复杂数据结构。
    • 自定义类可通过实现 __copy__() 和 __deepcopy__() 方法控制拷贝行为。

    Python高级与正则表达式

    Python高级语法        

    1、什么是迭代器

    迭代器(Iterator)是 Python 中的一种对象,用于在数据集合中逐个访问元素,而不需要暴露数据集合的底层实现。它提供了一种遍历集合元素的标准方式,适用于任何支持迭代的数据结构,如列表、元组等,range()就是一个迭代器

    迭代器是一个实现了 __iter__() 和 __next__() 方法的对象,使得可以逐步遍历它的元素。

    特点: 手动管理:需要显式地实现 __iter__() 和 __next__() 方法。 状态管理:迭代器需要自己管理迭代的状态,包括当前位置和结束条件。 内存使用:内存使用取决于迭代器的实现,通常是惰性计算(即按需生成数据)。

    2、迭代器底层实现

    class MyIterator:
        def __init__(self, start, end):
            # 初始化迭代器,设置起始值和结束值
            self.current = start  # 当前值初始化为start
            self.end = end        # 结束值设置为end
    
        def __iter__(self):
            # 返回迭代器对象自身,使其成为可迭代对象
            return self
    
        def __next__(self):
            # 获取下一个值,实现迭代逻辑
            if self.current > self.end:
                # 如果当前值超过结束值,抛出StopIteration异常终止迭代
                raise StopIteration
            value = self.current  # 保存当前值
            self.current += 1    # 当前值递增
            return value         # 返回当前值
    
    # 使用迭代器
    iterator = MyIterator(1, 5)  # 创建迭代器实例,范围1到5
    for num in iterator:         # 遍历迭代器
        print(num)               # 打印每个值
     
    

    定义:迭代器是一个对象,提供了一个访问集合元素的方式,不暴露集合的底层实现。

    协议:需要实现 __iter__() 和 __next__() 方法

    使用:可以通过 for 循环或 next() 函数进行遍历。

    优势:支持惰性计算和高效的内存使用,适合处理大型数据集合或流数据

     2、什么是生成器

    根据程序员制定的规则循环生成数据,当条件不成立时则生成数据结束。数据不是一次性全部生成出来,而是使用一个,再生成一个,可以节约大量的内存。

    创建生成器的方式:① 生成器推导式  ② yield 关键字

    # 使用生成器推导式创建一个生成器对象
    # 生成器推导式使用圆括号 () 而非方括号 [](列表推导式)或花括号 {}(集合/字典推导式)
    # 生成器不会立即计算所有值,而是按需生成,节省内存
    my_generator = (i * 2 for i in range(5))  
    # 生成器规则:对 range(5) 的每个元素 i,计算 i * 2
    
    # 打印生成器对象本身(输出的是对象内存地址,而非数据内容)
    print(my_generator)  # 输出类似:<generator object <genexpr> at 0x...>
    
    # 使用 next() 函数手动获取生成器的下一个值(示例被注释)
    # value = next(my_generator)  # 首次调用返回 0 * 2 = 0
    # print(value)  # 输出 0
    
    # 通过 for 循环遍历生成器
    # 每次迭代自动调用 next(),直到生成器耗尽(StopIteration)
    for value in my_generator:
        print(value)  # 依次输出 0, 2, 4, 6, 8
     
    
    3、yield生成器

    只要在def函数里面看到有 yield 关键字那么就是生成器

    def mygenerater(n):
        for i in range(n):
            print('开始生成...')
            yield i
            print('完成一次...')
    
    if __name__ == '__main__':
        g = mygenerater(2)
        # 获取生成器中下一个值
        # result = next(g)
        # print(result)
    
        # for遍历生成器, for 循环内部自动处理了停止迭代异常,使用起来更加方便
        for i in g:
            print(i)
     
    
    4、生成器的应用场景 – 数据迭代器dataloader的封装
    import math
    
    def dataset_loader(batch_size):
        # 1 读歌词
        with open('./jaychou_lyrics.txt', 'r') as file:
            lines = file.readlines()
    
        # 2 统计共有多少条歌词
        lyrics_number = len(lines)
    
        # 3 计算共有多少个批次 math.ceil向上取整 math.floor向下取整
        batch_number = math.ceil(lyrics_number / batch_size)
    
        # 4 遍历每一个 batch
        for idx in range(batch_number):
            yield lines[idx * batch_size : idx * batch_size + batch_size]
    
    if __name__ == '__main__':
        dataloader = dataset_loader(8)
        for data in dataloader:
            print(data)
    
        print('创建生成器(数据加载器) 为AI专业课做准备 End')
     
    

    1 生成器的概念? 如何使用?

    • 根据一定规则生成数据的一种机制,每次调用生成器只生成一个值,可以节省大量内存。
    • next(generater) next 函数获取生成器中的下一个值
    •  for  循环遍历生成器中的每一个值

    2 生成器的创建有两种方式?

    • 生成器推导式
    • yield 关键字

    3 yield关键字的作用(特性)

    • 1 将yield后面的值返回
    • 2 在yield这个地方卡着(阻塞)

    生成器和迭代器区别?

    1实现方式

    迭代器:需要实现 __iter__() 和 __next__() 方法,手动管理迭代状态。 生成器:通过 yield 关键字简化实现,自动管理迭代状态。

    2代码复杂度

    迭代器:通常需要更多的代码来管理状态和迭代逻辑。 生成器:代码更简洁,更容易理解和维护。

    3.  性能与内存

    迭代器:性能和内存使用取决于实现,通常也是惰性计算。 生成器:由于使用了 yield,内存使用和性能优化自动管理,适合处理大数据或流数。

    4. 使用场景

    迭代器:适合需要对迭代过程进行高度控制,或者需要自定义复杂的迭代逻辑时使用 生成器:适合需要简洁地生成序列数据,尤其是在处理大数据或需要按需生成数据时,能够节省内存和提高性能

    5Property属性

    负责把一个方法当做属性进行使用,这样做可以简化代码使用。

    定义property属性有两种方式:① 装饰器方式 ② 类属性方式

    装饰器方式

    class Person(object):
        def __init__(self):
            self.__age = 0
    
        # 获取属性
        @property
        def age(self):
            return self.__age
    
        # 修改属性
        @age.setter
        def age(self, new_age):
            self.__age = new_age
    
    if __name__ == '__main__':
        p1 = Person()
        print(p1.age)
    
        p1.age = 100
        print(p1.age)
     
    

    类属性方式

    class Person(object):
        def __init__(self):
            self.__age = 0
    
        def get_age(self):
            """当获取age属性的时候会执行该方法"""
            return self.__age
    
        def set_age(self, new_age):
            """当设置age属性的时候会执行该方法"""
            self.__age = new_age
    
        # 类属性方式的property属性
        age = property(get_age, set_age)
    
    
    if __name__ == '__main__':
        p1 = Person()
        print(p1.age)
    
        p1.age = 100
        print(p1.age)
     
    

    1 property属性的作用?

    把一个方法当做属性进行使用,这样做可以简化代码使用

    2 定义property属性的两种方式,各自的语法?

    装饰器方式:

    1@property装饰取值方法

    2@方法名.setter装饰设置值方法

    类属性

    property属性 = property(获取值方法,设置值方法)

    正则表达式

    1正则表达式(regular expression)描述了一种字符串匹配的模式,

    • 比如:检索一个串是否含有某种子串(检索)
    • 比如:匹配的子串做替换(替换)
    • 比如:从一个串中取出符合某个条件的子串(提取)

    模式:一种特定的字符串模式,这个模式是通过一些特殊的符号组成的。

    正则表达式并不是Python所特有的,在Java、PHP、Go以及JavaScript等语言中都是支持正则表达式的。

    2在Python中需要通过正则表达式对字符串进行匹配时,可使用re模块

    # 第一步:导入re模块 import re

    # 第二步:使用match方法进行匹配操作 result = re.match(pattern正则表达式, string要匹配的字符串, flags=0)

    #flags : 可选,表示匹配模式,比如忽略大小写,多行模式等 # 第三步:如果数据匹配成功,使用group方法来提取数据 result.group()

    3、举个栗子 

          

    def dm01_match匹配字符():
        """匹配字符: 从大字符串中, 按照规则, 匹配符合条件的子串"""
        # 导入re模块
        import re
    
        # 使用match方法进行匹配操作
        # 在大的字符串中, 按照规则:“任意1个字符”+“it”+“任意1个字符”, 提取符合要求的子串
        # 注意: 提取出来的子串一定要符合规则
        result = re.match(".it.", "aitcast")
    
        # 从左到右的匹配(不能跳, 不能从中间匹配), 一个字符一个字符的匹配
        # result = re.match(".it.", "iloveitcast")
    
        # 使用group方法来提取数据
        if result:
            info = result.group()
            print(info)
        else:
            print("没有找到符合规则的子串")
     
    

    1、正则表达式编写

    代码

    功能

    .

    匹配任意1个字符(除了\n

    [ ]

    匹配[ ]中列举的字符

    [^指定字符]

      匹配除了指定字符以外的所有字符

    \d

    匹配数字,即0-9

    \D

    匹配非数字,即不是数字

    \s

    匹配空白,即 空格,tab

    \S

    匹配非空白

    \w

    匹配非特殊字符,即a-zA-Z0-9_、汉字

    \W

    匹配特殊字符,即非字母、非数字、非汉字

    举个例子

    # 匹配任意1个字符(除了\n)
    # 匹配数据: 从左向右匹配,一个字符接着一个字符的匹配
    result = re.match("itcast.", "itcast2")
    
    # 获取数据
    if result:
        info = result.group()
        print(info)
    else:
        print("没有匹配到")
     
    

     2、能够使用re模块匹配多个字符

    1、能够使用re模块匹配多个字符

    代码

    功能

    *

    匹配前一个字符出现0次或者无限次,即可有可无

    +

    匹配前一个字符出现1次或者无限次,即至少有1

    ?

    匹配前一个字符出现1次或者0次,即要么有1次,要么没有

    {m}

    匹配前一个字符出现m

    {m,n}

    匹配前一个字符出现从mn

    # * 匹配前一个字符出现0次或者无限次,即可有可无
    result = re.match("itcast1*", "itcast111123333itcast")  # 1出现0次或者多次
    # result = re.match("itcast\d*", "itcast23333itcast")    # 数字出现0次或者多次
    # result = re.match("itcast\d*itcast", "itcast123333itcast")  # 数字出现0次或者多次 itcast开始 itcast结束
    # result = re.match("itcast\d*itcast", "itcastitcast")    # 数字出现0次或者多次 itcast开始 itcast结束
    
    # 获取数据
    if result:
        info = result.group()
        print(info)
    else:
        print("没有匹配到")
     
    

    网络编程

    网络:将具有独立功能的多台计算机通过通信线路和通信设备连接起来,在网络管理软件及网络通信协议下,实现资源共享和信息传递的虚拟平台。

    基本概念:要使用编程语言实现多台计算机的网络通信,需要具备网络编程三个要素

    (1)IP地址:这是网络环境下每一台计算机的唯一标识,通过IP地址来找到指定的计算机;

    (2)端口:用于标识进程的逻辑地址,通过端口来找到指定的进程;

    (3)协议:定义通信规则,符合协议则可以通信,否则无法正常通信。

    IP地址的概念

    IP地址就是标识网络中设备的一个地址,好比现实生活中的家庭地址。

    IP地址的表现形式

    IP地址分为两类: IPv4和IPv6

    IPv4:是目前大家使用的IP地址;

    IPv6:作为了解,IPv6是未来使用的IP地址。

    Linux 和 mac OS 使用 ifconfig 这个命令 Windows 使用 ipconfig 这个命令

    检查网络是否正常使用 ping 命令 ping www.baidu.com 检查是否能上公网 ping 当前局域网的ip地址 检查是否在同一个局域网内 ping 127.0.0.1 检查本地网卡是否正常

    端口和端口号:其实,每运行一个程序都会有一个端口,想要给对应的程序发送数据,找到对应的端口即可。

    端口是传输数据的通道,好比教室的门,是数据传输必经之路(注:给已经运行起来的程序分配端口号;没有运行的程序)

    TCP的概念 TCP的英文全拼(Transmission Control Protocol)简称传输控制协议,它是一种面向连接的、可靠的、基于字节流的传输层通信协议。

    TCP 通信步骤: 创建连接 传输数据 关闭连接 说明: TCP通信模型相当于生活中的’打电话‘,在通信开始之前,一定要先建立好连接,才能发送数据,通信结束要关闭连接

    TCP协议创建连接:3次握手

    三次握手(Three-Way Handshake)就是指建立一个TCP连接时,需要客户端和服务端总共发送3个包以确认连接的建立。

    • 第一次握手:客户端向服务端发送请求,等待服务端确认。
    • 第二次握手:服务端收到请求后知道客户端请求建立连接,回复给客户端以确认连接请求。
    • 第三次握手:客户端收到确认后,再次发送请求确认服务端,服务端收到正确请求后,如果正确则连接建立成功,完成三次握手,随后客户端与服务端之间可以开始传输数据了。

    TCP协议断开连接:4次挥手

    • 四次挥手说TCP断开链接的时候需要经过4次确认。TCP连接是双向,A连接B、B连接A都要断开第一次挥手: 当主机A(可以是客户端也可以是服务端)完成数据传输后, 提出停止TCP 连接的请求
    • 第二次挥手: 主机B收到请求后对其作出响应,确认这一方向上的TCP连接将关闭
    • 第三次挥手: 主机B 端再提出反方向的连接关闭请求
    • 第四次挥手: 主机A对主机B 的请求进行确认,双方向的关闭结束

            

    socket

    知道网络编程的三要素,数据是如何完成传输的呢?此时,就可以使用 socket来完成。 socket(简称 套接字) 是进程之间通信一个工具,好比现实生活中的插座,所有的家用电器要想工作都是基于插座进行,而进程之间想要进行网络通信需要基于这个 socket。

    # 导入模块
    import socket
    
    # 创建socket对象
    tcp_socket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
    
    # 输出内容
    

    进程

    进程(Process)是CPU资源分配的最小单位,它是操作系统进行资源分配和调度运行的基本单位 通俗理解:一个正在运行的程序就是一个进程 . 例如:正在运行的qq , 微信等他们都是一个进程

     注意:一个程序运行后至少有一个进程

    进程的创建步骤
    
    1. 导入进程工具包
                 import multiprocessing
    2. 通过进程类 实例化进程 对象 
                 子进程对象 = multiprocessing.Process() 
    3. 启动进程执行任务
                 进程对象.start()
    

    案例

    使用多进程来模拟一边编写代码,一边听音乐功能实现。(任务函数没有参数)

    import multiprocessing
    import time
    
    def coding():
        for i in range(3):
            print("coding...")
            time.sleep(0.2)
    
    def music():
        for i in range(3):
            print("music...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        coding_process = multiprocessing.Process(target=coding)
        music_process = multiprocessing.Process(target=music)
        
        coding_process.start()
        music_process.start()
        
        coding_process.join()
        music_process.join()
     
    

    使用多进程来模拟小明一边编写num行代码,一边听count首音乐功能实现。(任务函数有参数)

    import multiprocessing
    import time
    
    def coding(num, name):
        for i in range(1, num+1):
            print(f"{name}正在编写第{i}行代码...")
            time.sleep(0.2)
    
    def music(count, name):
        for i in range(1, count+1):
            print(f"{name}正在听第{i}首音乐...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        coding_process = multiprocessing.Process(
            target=coding,
            args=(3, "小明")
        )
        music_process = multiprocessing.Process(
            target=music,
            kwargs={"count": 2, "name": "小明"}
        )
        
        coding_process.start()
        music_process.start()
        
        coding_process.join()
        music_process.join()
     
    

    总结:

    说出使用多进程完成多任务步骤?

    • a.导入进程包     import multiprocessing
    • b.创建子进程并指定执行的任务     sub_process = multiprocessing.Process (target=任务名)
    • c.启动进程执行任务     sub_process.start()

    2.进程传参的两种方式是什么?

    • a.元组方式传参 :元组方式传参一定要和任务函数的参数顺序保持一致。
    • b.字典方式传参:字典方式传参字典中的key一定要和任务函数的参数保持一致

    进程编号的作用

    进程编号唯一标识一个进程, 方便管理进程。          

    在一个操作系统中,一个进程拥有的进程号是唯一的,进程号可以反复使用。 获取进程编号的目的是验证主进程和子进程的关系,可以得知子进程是由那个主进程创建出来的

    获取进程编号的两种操作 获取当前进程编号 获取当前父进程编号
     

    import os
    
    # 获取当前进程编号
    
    pid = os.getpid() print(pid) 或者
    
    import multiprocessing pid = multiprocessing.current_process().pid
    
    print(pid)
    
    # 获取父进程的编号
    ppid = os.getppid()
    print(pid)
    
    进程的注意点

    1.进程之间不共享全局变量

    2. 主进程会等待所有的子进程执行结束再结束

    import multiprocessing
    import time
    
    # 全局变量
    my_list = []
    # 写入数据
    def write_data():
        for i in range(3):
            my_list.append(i)
            print("add:", i)
        print("write_data:", my_list)
    # 读取数据
    
    def read_data():
        print("read_data:", my_list)
    
    if __name__ == '__main__':
       # 创建写入数据进程
        write_process = multiprocessing.Process(target=write_data)
       # 创建读取数据进程
        read_process = multiprocessing.Process(target=read_data)
        # 启动进程执行相应任务
        write_process.start()
        time.sleep(1)
        read_process.start()
     
    

    创建子进程会对主进程资源进行拷贝,也就是说子进程是主进程的一个副本,好比是一对双胞胎,之所以进程之间不共享全局变量,是因为操作的不是同一个进程里面的全局变量,只不过不同进程里面的全局变量名字相同而已。

    1. 为了保证子进程能够正常的运行,主进程会等所有的子进程执行完成以后再销毁,设置守护主进程的目的是主进程退出子进程销毁,不让主进程再等待子进程去执行。
    2. 设置守护主进程方式: 子进程对象.daemon = True
    3. 销毁子进程方式: 子进程对象.terminate()

    线程

    进程是分配资源的基本单位, 一旦创建一个进程就会分配一定的资源;

    线程是cpu调度的基本单位,每个进程至少都有一个线程,而这个线程就是我们通常说的主线程。

    1、什么是线程?线程依附于进程执行,是CPU调度的基本单元

    2、线程的作用是什么?线程用来实现多任务编程

    线程创建的步骤

    1. 导入线程模块     import threading

    2. 通过线程类创建线程对象     线程对象 = threading.Thread(target=任务名)

    3. 启动线程执行任务     线程对象.start()

    import threading
    import time
    
    def coding():
        for i in range(3):
            print("正在写代码...")
            time.sleep(0.2)
    
    def music():
        for i in range(3):
            print("正在听音乐...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        t1 = threading.Thread(target=coding)
        t2 = threading.Thread(target=music)
        
        t1.start()
        t2.start()
        
        t1.join()
        t2.join()
        
        print("所有任务完成")
     
    

    使用多线程来模拟小明一边编写num行代码,一边听count首音乐功能实现。

    import threading
    import time
    
    def coding(num, name):
        for i in range(num):
            print(f"{name}正在编写第{i+1}行代码...")
            time.sleep(0.2)
    
    def music(count, name):
        for i in range(count):
            print(f"{name}正在听第{i+1}首歌...")
            time.sleep(0.2)
    
    if __name__ == '__main__':
        coding_thread = threading.Thread(target=coding, args=(3, '小明'))
        music_thread = threading.Thread(target=music, kwargs={'name':'小明', "count":3})
        
        coding_thread.start()
        music_thread.start()
        
        coding_thread.join()
        music_thread.join()
     
    

    1.创建线程的流程是什么?        

    • a.导入线程模块        import threading        
    • b.创建子线程并指定执行的任务         sub_thread = threading.Thread(target=任务名)        
    • c.启动线程执行任务         sub_thread.start()

    2.线程传参的两种方式是什么?          

    • a .元组方式传参 :元组方式传参一定要和参数的顺序保持一致。          
    • b.字典方式传参:字典方式传参字典中的key一定要和参数名保持一致

    线程之间执行是无序的

    线程之间执行是无序的,它是由cpu调度决定的 ,cpu调度哪个线程,哪个线程就执行,没有调度的线程是不能执行的。

    创建多个线程,多次运行,观察各次线程的执行顺序

    import threading
    import time
    
    def get_info():
        time.sleep(0.5)
        current_thread = threading.current_thread()
        print(current_thread)
    
    if __name__ == '__main__':
        for i in range(10):
            sub_thread = threading.Thread(target=get_info)
            sub_thread.start()
     
    

    线程之间共享全局变量

    import threading
    import time
    
    my_list = []
    lock = threading.Lock()  # 创建锁对象
    
    def write_data():
        lock.acquire()  # 获取锁
        for i in range(3):
            print("add:", i)
            my_list.append(i)
        print("write:", my_list)
        lock.release()  # 释放锁
    
    def read_data():
        lock.acquire()  # 获取锁
        print("read:", my_list)
        lock.release()  # 释放锁
    
    if __name__ == '__main__':
        write_thread = threading.Thread(target=write_data)
        read_thread = threading.Thread(target=read_data)
        write_thread.start()
        time.sleep(1)  # 仍保留延时确保执行顺序演示
        read_thread.start()
     
    

    1.线程执行执行是()? A 有序 B无序

    2.主线程默认会等待所有子线程执行结束再结束。设置()的目的是主线程退出子线程销毁。         守护主进程,threading.Thread(target=show_info,           daemon=True)或 线程对象.setDaemon(True)

    3.线程之间()全局变量?。A 共享 B 不共享

    4.线程之间共享全局变量可能会导致数据出现错误问题,可以使用()来解决这个问题。

    互斥锁

    对共享数据进行锁定,保证同一时刻只有一个线程去操作。

    互斥锁是多个线程一起去抢,抢到锁的线程先执行,没有抢到锁的线程进行等待,等锁使用完释放后,其它等待的线程再去抢这个锁。

    互斥锁的使用

    1. 互斥锁的创建

    mutex = threading.Lock()

    2上锁

    mutex.acquire()

    3. 释放锁

    mutex.release()

    使用互斥锁保证线程间的数据安全

    import threading
    
    # 全局变量初始化
    g_num = 0
    
    # 创建互斥锁
    mutex = threading.Lock()
    
    # 第一个累加函数
    def sum_num1():
        mutex.acquire()  # 获取锁
        for _ in range(1000000):
            global g_num
            g_num += 1
        mutex.release()  # 释放锁
        print("g_num1:", g_num)
    
    # 第二个累加函数
    def sum_num2():
        mutex.acquire()  # 获取锁
        for _ in range(1000000):
            global g_num
            g_num += 1
        mutex.release()  # 释放锁
        print("g_num2:", g_num)
    
    if __name__ == '__main__':
        # 创建线程
        sum1_thread = threading.Thread(target=sum_num1)
        sum2_thread = threading.Thread(target=sum_num2)
    
        # 启动线程
        sum1_thread.start()
        sum2_thread.start()
    
        # 等待线程结束
        sum1_thread.join()
        sum2_thread.join()
    
        # 最终结果
        print("Final g_num:", g_num)
     
    
    死锁

    一直等待对方释放锁的情景就是死锁。

    死锁的原因:     使用互斥锁的时候需要注意死锁的问题,     未在合适的地方注意释放锁

    死锁的结果:     会造成应用程序的停止响应,     应用程序无法再继续往下执行了

    import threading
    
    # 定义全局变量
    g_num = 0
    # 创建互斥锁
    mutex = threading.Lock()
    
    def sum_num1():
        global g_num
        for _ in range(1000000):
            mutex.acquire()  # 上锁
            g_num += 1
            mutex.release()  # 释放锁
        print("g_num1:", g_num)
    
    def sum_num2():
        global g_num
        for _ in range(1000000):
            mutex.acquire()  # 上锁
            g_num += 1
            mutex.release()  # 释放锁
        print("g_num2:", g_num)
    
    if __name__ == '__main__':
        sum1_thread = threading.Thread(target=sum_num1)
        sum2_thread = threading.Thread(target=sum_num2)
        sum1_thread.start()
        sum2_thread.start()
        sum1_thread.join()
        sum2_thread.join()
        print("Final g_num:", g_num)
     
    

    1.互斥锁是什么?          

    对共享数据进行锁定,保证同一时刻只有一个线程去操作。

    2.互斥锁的使用步骤 ?          

    互斥锁的创建threading.Lock() ,上锁mutex.acquire(),释放锁release

    3.死锁是什么?产生的原因?效果是什么?        

    一直等待对方释放锁的情景就是死锁。

    原因: 使用互斥锁的时候需要注意死锁的问题,未在合适的地方注意释放锁

    结果: 会造成应用程序的停止响应,应用程序无法再继续往下执行了

    进程和线程对比

    1. 进程之间不共享全局变量

    2. 线程之间共享全局变量,但是要注意资源竞争的问题,解决办法: 互斥锁

    3. 创建进程的资源开销要比创建线程的资源开销要大

    4. 进程是操作系统资源分配的基本单位,线程是CPU调度的基本单位

    5. 线程不能够独立执行,必须依存在进程中

    6. Python中多进程开发比单进程多线程开发稳定性要强

    进程优缺点:

    优点:可以用多核

    缺点:资源开销大

    线程优缺点:         

    优点:资源开销小

    缺点:不能使用多核

    您可能感兴趣的与本文相关的镜像

    Python3.8

    Python3.8

    Conda
    Python

    Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值