# 面向对象

面向对象编程是一种编程范式或编程风格。它以类和对象为核心,将数据(属性)和操作(方法)封装在一起,通过继承和多态等机制实现代码的重用和扩展。

#

类是面向对象编程中的基本概念,它是一种抽象的数据类型,用于定义对象的属性和行为。类可以看作是对象的模板,通过类可以创建多个具有相同属性和行为的对象。

类的定义
在 Python 中,使用 class 关键字来定义一个类。

注意:类名通常使用大写字母开头,遵循驼峰命名法。

# 基本格式:
# class 类名:
#     类的属性和方法
class ClassName:
    # 类的属性
    name = "name"
    # 类的方法
    def method(self):
        print("method id:", self)  # self 是类实例的引用,用于访问类的属性和方法
# 实例化
class_instance = ClassName()
print("class_instance id is:", class_instance)
class_instance.method()
"""
结果:
class_instance id is: <__main__.ClassName object at 0x000001CE2FEA9FD0>
method id: <__main__.ClassName object at 0x000001CE2FEA9FD0>
"""
  1. 构造函数 __init__()
    • 构造函数是类的一个特殊方法,用于初始化对象的状态。当创建对象时,Python 会自动调用构造函数。
    • 构造函数的第一个参数必须是 self ,表示对象本身的引用。
    • 构造函数可以接受任意数量的参数,用于初始化对象的属性。
    • 构造函数可以返回任意类型的值,但通常返回 None
    • 构造函数可以包含任意数量的代码,用于执行初始化操作。
    • 构造函数可以调用其他方法,用于执行初始化操作。
class ClassName:
    def __init__(self, name): #self-- 实例本身,name-- 实例属性(非类属性)
        self.name = name
class_instance = ClassName("name")
print("class_instance.name is:", class_instance.name)
  1. 析构函数 __del__()
    • 析构函数是类的一个特殊方法,用于在对象被销毁时执行清理操作。当对象被销毁时,Python 会自动调用析构函数。
    • 析构函数的第一个参数必须是 self ,表示对象本身的引用。
    • 析构函数可以接受任意数量的参数,用于执行清理操作。
    • 析构函数可以返回任意类型的值,但通常返回 None
    • 析构函数可以包含任意数量的代码,用于执行清理操作。
    • 析构函数可以调用其他方法,用于执行清理操作。
class ClassName:
    def __init__(self, name):
        self.name = name
    def __del__(self):
        print("del ClassName")
class_instance = ClassName("name")
del class_instance # 或者 class_instance = None (删除对象)

# 封装

封装:面向对象编程中的一个重要概念,它指的是将对象的属性和方法封装在一起,形成一个独立的单元。
目的:隐藏对象的内部实现细节,只对外提供必要的接口,从而提高代码的可维护性和安全性。

在 Python 中,可以通过以下方式实现封装:

# 隐藏属性

使用 __ 双下划线开头的属性名或方法名,表示私有,只能在类的内部访问。

class ClassName:
    name = "name"
    __age = 18
class_instance = ClassName()
print("class_instance.name is:", class_instance.name) # √
print("class_instance.__age is:", class_instance.__age) # × AttributeError: 'ClassName' object has no attribute '__age'
## 隐藏属性实际上是将类名修改为: _类名__属性名 
print("class_instance.__age is:", class_instance._ClassName__age) # √

# 私有属性 / 方法

  • xxx :普通属性 / 方法,可以在类的外部访问。
  • _xxx :受保护的属性 / 方法,如果定义在类中,可以外部访问,也可以子类中访问。但是另外的 .py 文件通过 from import * 导入时, _xxx 属性 / 方法不会被导入。
    • 这种一般是为了避免与 python 关键字冲突而采用的命名方法。
  • __xxx :双下划线开头,隐藏的属性 / 方法,只能在类的内部访问,如果定义在类中,子类不会继承。
    • 这种命名一般是 python 中的魔法方法或者属性,都是有特殊含义和功能的,自己不要轻易定义。

私有属性

class ClassName:
    name = "name"
    _age = 18
    __sex = "male"
class_instance = ClassName()
print("class_instance.name is:", class_instance.name) # √
print("class_instance._age is:", class_instance._age) # √
print("class_instance.__sex is:", class_instance.__sex) # × AttributeError: 'ClassName' object has no attribute '__sex'
print("class_instance._ClassName__sex is:", class_instance._ClassName__sex) # √

隐藏方法

class Man:
    def __play(self):
        print("write code")
    def funa(self):
        # 内部调用私有方法
        self.__play() # 类内部调用私有方法
        Man.__play(self) # 类内部调用私有方法(不推荐)
man = Man()
man.funa() # √ 类内部调用私有方法
man.__play() # × AttributeError: 'Man' object has no attribute '__play'
"""
输出:
write code
"""

私有方法

class Gril:
    def _play(self): #私有方法
        print("play game")
gril = Gril()
gril._play() # √

# 继承

继承:面向对象编程中的一个重要概念,它指的是一个类可以继承另一个类的属性和方法,从而实现代码的重用和扩展。

格式: class 子类名(父类名):

# 单继承

class Persion:
    def eat(self):
        print("eat")
    def sing(self):
        print("sing")
class Student(Persion):
    pass # 占位符,表示不添加任何新的属性和方法
student = Student()
student.eat() # √
student.sing() # √
"""
输出:
eat
sing
"""

# 继承的传递(多重继承)

子类可以继承父类的属性和方法,也可以继承父类的父类的属性和方法。

class Persion:
    def eat(self):
        print("eat")
    def sing(self):
        print("sing")
class Student(Persion):
    pass
class Teacher(Student):
    pass
teacher = Teacher()
teacher.eat() # √
teacher.sing() # √
"""
输出:
eat
sing
"""

# 重写

  1. 子类重写父类的方法
    子类可以重写父类的方法,即在子类中定义一个与父类方法同名的方法,从而覆盖父类的方法。
class Persion:
    def eat(self):
        print("eat")
class Student(Persion):
    def eat(self):
        print("eat food")
student = Student()
student.eat() # eat food
"""
输出:
eat food
"""
  1. 子类拓展父类的方法 (不会改变父类方法)
    写法 1:子类在需要拓展的父类方法下写 父类名.方法名()
class Persion:
    def eat(self):
        print("eat")
class Student(Persion):
    def eat(self):
        Persion.eat(self)
        print("eat food")
#拓展父类方法
student1 = Student()
student1.eat() # eat food
# 不会改变父类方法
stuent2 = Persion()
stuent2.eat()
"""
输出:
eat
eat food
eat
"""

写法 2:子类在需要拓展的父类方法下写 super().方法名()
注意: super 在 python 里面是一个特殊的类,super () 是使用 super 类创建的一个对象,这个对象是父类,但是不是父类的实例,而是父类的子类,所以 super () 可以调用父类的方法。

class Persion:
    def eat(self):
        print("eat")
class Student(Persion):
    def eat(self):
        super().eat()
        print("eat food")
#拓展父类方法
student1 = Student()
student1.eat() # eat food
# 不会改变父类方法
stuent2 = Persion()
stuent2.eat()
"""
输出:
eat
eat food
eat
"""

# 新式类写法

python 中,类有两种写法:经典类和新式类。

  • class A
    经典类:不由任意内置类型派生的类。
class Animal:
    def eat(self):
        print("eat")
# 经典类:没有新的属性和方法
class Dog(Animal):
    pass
# 派生类
class Cat(Animal):
    name = "cat"
    def play(self):
        print("play")
  • class A()
  • class A(object) 新式类:继承了 object 类或者该类的子类都是新式类。 -- 推荐使用
    object 类是所有类的基类,所有类都继承自 object 类,是 python 为所有对象提供的基类 (顶级父类)。

注意:python3 中如果一个类没有继承任何类,则默认继承 object 类,所以 python3 中所有的类都是新式类。

# 多继承

一个类可以继承多个类,多个类之间用逗号分隔。

class Student:
    def study(self):
        print("study")
class Teacher:
    def teach(self):
        print("teach")
class TeacherStudent(Teacher, Student):
    pass
teacher_student = TeacherStudent()
teacher_student.study() # study
teacher_student.teach() # teach

注意: 多个父类具有同名方法,则调用顺序为: 从左到右,深度优先

方法的搜索顺序:
python 中内置的 __mro__ 属性可以查看类的继承顺序。

class A:
    def funa(self):
        print("A.funa")
class B(A):
    def funb(self):
        print("B.funb")
class C(A):
    def funa(self):
        print("C.funa")
class D(B, C):
    pass
d = D()
d.funa() # C.funa
print(D.__mro__)
"""
输出:(从左到右,深度优先)
(<class '__main__.D'>, <class '__main__.B'>, <class '__main__.C'>, <class '__main__.A'>, <class 'object'>)
"""

# 多态

多态:面向对象编程中的一个重要概念,它指的是一个对象可以有多种形态,即同一个方法可以在不同的对象上调用,从而实现不同的功能。

多态的前提:

  • 继承
  • 重写

多态的体现:

  • 父类引用指向子类对象
class Animal:
    def eat(self):
        print("eat")
class Dog(Animal):
    def eat(self):
        print("dog eat")
class Cat(Animal):
    def eat(self):
        print("cat eat")
animal = Animal()
animal.eat() # eat
dog = Dog()

多态性:

  • 同一个方法,不同的对象调用,产生不同的结果。
class Animal:
    def eat(self):
        print("eat")
class Dog(Animal):
    def eat(self):
        print("dog eat")
class Cat(Animal):
    def eat(self):
        print("cat eat")
def funa(obj):
    obg.eat()
animal = Animal()
funa(animal) # eat
dog = Dog()
funa(dog) # dog eat
cat = Cat()
funa(cat) # cat eat

# 静态方法

静态方法:不需要实例化对象就可以直接调用的方法,使用 @staticmethod 装饰器来定义。

静态方法的特点:

  • 不需要实例化对象就可以直接调用
  • 不需要 self 参数
  • 静态方法可以访问类变量和实例变量
  • 静态方法不能访问实例方法

静态方法的使用场景:当方法不需要访问实例变量和类变量时,可以使用静态方法。

class Animal:
    @staticmethod
    def say_hello():
        print("hello")
# 静态方法既可以使用对象访问,也可以使用类名访问
# 实例对象访问
animal = Animal("dog")
animal.say_hello() # hello
# 类名访问
Animal.say_hello() # hello

注意:类方法可以直接方法时传参数,例如 Animal.say_hello (name)

# 类方法

类方法:使用 @classmethod 装饰器来定义,对于类方法,第一个参数必须是类对象,通常命名为 cls ,表示类本身。

class Animal:
    name = "animal"
    @classmethod
    def say_hello(cls):
        print("hello")
# 类方法既可以使用对象访问,也可以使用类名访问
# 实例对象访问
animal = Animal("dog")
animal.say_hello() # hello
# 类名访问
Animal.say_hello() # hello

当方法中需要使用到类对象(如访问私有类属性时),定义类方法
类方法一般配合类属性使用

总结:

  • 实例方法:方法内部访问实例属性,方法内部可以通过 类名.类属性名 来访问类属性。
  • 静态方法 @staticmethod :方法内部,不需要访问实例属性和类属性。
    • 如果需要访问类属性,方法内部可以通过 类名.类属性名 来访问,不能访问实例属性。
  • 类方法 @classmethod :方法内部只需要访问类属性,方法内部可以通过 cls.类属性名 来访问类属性,不能访问实例属性。
class Person(object):
    name = "小明" # 类属性
    def __init__(self):
        self.age = 18 # 实例属性:对象私有
    
    def play(self):  # 实例方法
        # 在实例方法中访问类属性
        print(f'{Person.name}在玩游戏')
        # 在实例方法中访问实例属性
        print(f'{self.age}岁')
    @staticmethod
    def eat():  # 静态方法: 类中的函数,形参没有限制,没有 self 指向实例对象的概念
        print(f"{Person.name}eat food") # 静态方法可访问类属性,但是无意义,静态方法既不传递类对象,也不传递实例对象
        # print (f"{self.age} 岁") # × 静态方法不能访问实例属性
    @classmethod # 类方法:针对类存在的方法
    def say_hello(cls):  # 类方法:cls 代表类对象本身,不用类名。属性名 
        print(f'{cls.name}在说话')
        # print (f"{self.age} 岁") # × 不能访问实例属性

# 单例模式

含义:一种常见的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。
优点:节省内存,避免重复创建对象。
弊端:多线程访问容易引发线程安全问题。

方式

  1. 通过 @classmethod 实现
  2. 通过装饰器实现
  3. 通过重写__new__方法实现
  4. 通过模块导入实现

# __new__方法

__new__ 方法是 object基类 提供的内置的 静态方法 ,它是在对象被创建之前调用的,用于创建对象。 __new__ 方法返回一个对象,这个对象会被 __init__ 方法初始化。

作用:

  • 在内存中为对象分配空间
  • 返回对象的引用
class Person(object):
    def __init__(self, name):
        print("Person.__init__")
        self.name = name
    
    def __new__(cls, *args, **kwargs): # 重写 new 不再调用父类的 new 方法
        print("Person.__new__")
        # 对父类方法进行扩展 super (). 方法名 ()
        res = super().__new__(cls) #方法重写,res 里面保存的是实例对象的引用
        return res
person = Person("小明")
"""
输出:
Person.__new__
"""

# 通过重写__new__方法实现单例模式

设计流程:

  1. 定义一个类属性,初始化为 None,用来记录单利对象的引用
  2. 重写__new__方法
  3. 进行判断,如果类属性为 None,把__new__() 返回对象的引用保存进去
  4. 返回类型属性中记录的对象引用
class Singleton(object):
    __instance = None # 类属性
    def __new__(cls, *args, **kwargs):
        print("Singleton.__new__")
        return super().__new__(cls)
    
    def __init__(self):
        print("Singleton.__init__")
s1 = Singleton()
s2 = Singleton()
print(s1 is s2) # True

# 通过导入模块实现单例模式

模块的 __init__.py 文件中的代码在模块第一次被导入时执行,只会执行一次,因此可以利用这个特性来实现单例模式。

# my_singleton.py
class Singleton(object):
    def __init__(self):
        print("Singleton.__init__")
    def __new__(cls, *args, **kwargs):
        print("Singleton.__new__")
        return super().__new__(cls)
singleton = Singleton()
# main.py
from my_singleton import singleton
print(singleton)

# 应用场景

    1. 回收站对象
    1. 音乐播放器
    1. 开发游戏软件 场景管理器
    1. 数据库配置、数据库连接池的设计
    1. 线程池、连接池、缓存、日志对象

# 魔法方法 & 魔法属性

魔法方法:在 Python 中,以双下划线开头和结尾的方法称为魔法方法,也称为特殊方法。魔法方法在特定的情况下自动调用,不需要手动调用。

# __doc__属性

__doc__ : 类、函数的描述信息

class Person(object):
    """这是一个描述信息""" # 只能使用多行注释,单行注释无效
    pass
print(Person.__doc__) # 这是一个描述信息
def fun():
    """这是一个描述信息"""
    pass
print(fun.__doc__) # 这是一个描述信息

# __modeule__属性

__module__ : 类所属的模块名

class Person(object):
    pass
print(Person.__module__) # __main__

# __class__属性

__class__ : 类所属的类名

class Person(object):
    pass
print(Person.__class__) # <class 'type'>

# str () 方法

__str__ : 当使用 print 输出对象时,自动调用 __str__ 方法,返回一个字符串

class Person(object):
    def __str__(self):
        return "这是一个Person对象"
person = Person()
print(person) # 这是一个 Person 对象

# del () 方法

__del__ : 当对象被销毁时,自动调用 __del__ 方法

class Person(object):
    def __del__(self):
        print("Person对象被销毁")
person = Person()
del person # Person 对象被销毁

# call () 方法

__call__ : 当对象被当作函数调用时,自动调用 __call__ 方法
callable: 判断对象是否可调用

class Person(object):
    def __call__(self, *args, **kwargs):
        print("Person对象被当作函数调用")
person = Person()
person() # 调用一个可调用的实例对象,其实就是在调用它的__call__() 方法
# 判断对象是否可调用
print(callable(person)) # True

# __dict__属性

__dict__ : 类或对象的属性字典

class Person(object):
    def __init__(self, name):
        self.name = name
person = Person("小明")
print(person.__dict__) # {'name': ' 小明 '}

# 文件操作

文件:存储在硬盘上的数据

文件操作步骤:

  • 打开文件
  • 读写文件
  • 关闭文件

# 文件对象的方法

  1. open () 函数

open() 创建一个 file 对象,默认是以只读模式打开。

file = open("文件路径", "打开模式")
  1. read(n)

read() 方法用于读取文件内容,n 表示读取的字符个数,如果不指定 n,则表示读取整个文件。

file = open("文件路径", "打开模式")
content = file.read()
file.close()
  1. write()

write() 方法用于向文件中写入内容。

file = open("文件路径", "打开模式")
file.write("要写入的内容")
file.close()
  1. close()

close() 方法用于关闭文件。

file = open("文件路径", "打开模式")
file.close()

# 属性

  1. name
    name : 返回要打开文件的文件名,可以包含具体的路径。
file = open("文件路径", "打开模式")
print(file.name)
  1. mode
    mode : 返回打开文件时使用的模式,如’r’、‘w’、'a’等。
file = open("文件路径", "打开模式")
print(file.mode)
  1. closed
    closed : 返回文件是否已经关闭,True 表示已经关闭,False 表示已经打开。
file = open("文件路径", "打开模式")
print(file.closed)

# 读写操作

  1. 读操作
  • read(size=-1)
    read(size=-1) : 从文件中读取 size 个字符,如果 size 未指定或为负数,则读取剩余的所有字符。
file = open("文件路径", "打开模式")
content = file.read()
file.close()
  • readline(size=-1)
    readline(size=-1) : 从文件中读取一行,如果 size 未指定或为负数,则读取整行。
file = open("文件路径", "打开模式")
content = file.readline()
file.close()
  • readlines(hint=-1)
    readlines(hint=-1) : 从文件中读取所有行,返回一个列表,如果 hint 未指定或为负数,则读取所有行。
file = open("文件路径", "打开模式")
content = file.readlines()
file.close()

# 访问模式

open() 函数的第二个参数,表示打开文件的模式,常用的模式有:

  • ‘r’: 只读模式,如果文件不存在,会抛出异常。
  • ‘w’: 写入模式,如果文件不存在,会创建文件;如果文件存在,会清空文件内容。
  • ‘a’: 追加模式,如果文件不存在,会创建文件;如果文件存在,会在文件末尾追加内容。
  • ‘b’: 二进制模式,可以与上述模式组合使用,如’rb’、‘wb’、'ab’等。
  • ‘+’: 读写模式,可以与上述模式组合使用,如’r+'、‘w+’、'a+' 等。
    • ‘r+’: 读写模式,如果文件不存在,会抛出异常;如果文件存在,可以同时进行读写操作。
    • ‘w+’: 先写再读,文件存在就重新编辑文件,不存在就创建文件。

文件指针

文件指针:文件指针是指向文件中某个位置的指针,文件指针的初始位置为文件的开头,每次读写操作都会改变文件指针的位置。

  • tell()
    tell() : 返回文件指针的当前位置。
file = open("文件路径", "打开模式")
print(file.tell())
  • seek(offset, whence=0)
    seek(offset, whence=0) : 移动文件指针到指定的位置, offset 表示偏移量, whence 表示参考位置,默认为 0 作为起始位置,表示文件开头。
file = open("文件路径", "打开模式")
file.seek(10, 0) # 将文件指针移动到第 10 个字符的位置
  • truncate(size=None)
    truncate(size=None) : 截断文件,从文件指针的位置开始截断,截断后的文件大小为 size,如果 size 未指定,则截断到文件指针的位置。
file = open("文件路径", "打开模式")
file.truncate(10) # 将文件截断到第 10 个字符的位置
  • with open

with open 语句可以自动关闭文件,不需要手动调用 close() 方法。

with open("文件路径", "打开模式") as file:
    content = file.read()

# 编码格式

编码格式:文件的编码格式,常用的编码格式有 UTF-8、GBK 等。

file = open("文件路径", "打开模式", encoding="编码格式")

# 导入模块

os 模块:提供了许多与操作系统交互的函数,如文件操作、目录操作、环境变量操作等。
import os

# 文件重命名

指令: os.rename("旧文件名", "新文件名")

os.rename("旧文件名.txt", "新文件名.txt")

# 删除文件

指令: os.remove("文件名")

os.remove("文件名.txt")

# 创建文件夹

指令: os.mkdir("文件夹名")

os.mkdir("文件夹名")

# 删除文件夹

指令: os.rmdir("文件夹名")

os.rmdir("文件夹名")

# 获取当前工作目录

指令: os.getcwd()

os.getcwd()

# 获取目录列表

指令: os.listdir("目录路径")

os.listdir("目录路径")

# 切换目录

指令: os.chdir("目录路径")

os.chdir("目录路径")

# 判断路径是否存在

指令: os.path.exists("路径")

os.path.exists("路径")

# 判断是否为文件

指令: os.path.isfile("路径")

os.path.isfile("路径")

# 可迭代对象 Iterable

可迭代对象:可以被 for 循环遍历的对象,如列表、元组、字符串、字典、集合等。

  1. 可迭代对象的条件:
  • 对象实现了__iter__() 方法
  • iter () 方法返回迭代对象
  1. for 循环工作原理
  • 先通过__iter__() 方法获取迭代器对象的迭代器
  • 然后通过迭代器对象的__next__() 方法获取下一个元素并赋值给临时变量
  1. isinstance () 方法
  • 判断一个对象是否是可迭代对象
    isinstance(o,t) o: 要判断的对象,t: 要判断的类型
    Iterable: 可迭代对象
from collections.abc import Iterable
# 判断一个对象是否是可迭代对象
print(isinstance([1, 2, 3], Iterable)) # True

可迭代对象 Iterable 和迭代器 Iterator

凡是可以作用于 for 循环的对象都是 可迭代对象 Iterable 类型;
凡是可以作用于 next() 函数的对象都是 迭代器 Iterator 类型。

from collections.abc import Iterable,Iterator
# 判断一个对象是否是可迭代对象
print(isinstance([1, 2, 3], Iterable)) # True
# 判断一个对象是否是迭代器
print(isinstance(iter([1, 2, 3]), Iterator)) # True
# iter () 函数将可迭代对象转换为迭代器
print(isinstance(iter([1, 2, 3]), Iterator)) # True

总结:

  • 可迭代对象不一定是迭代器,但迭代器一定是可迭代对象。
  • iter () 函数可以将可迭代对象转换为迭代器。
  • 如果一个对象拥有 __iter__() 方法,那么它就是可迭代对象
  • 如果一个对象拥有 __next__() 方法,那么它就是迭代器
  • dir() 函数可以查看一个对象的所有属性和方法

# 迭代器协议

迭代器协议:迭代器协议是指迭代器对象必须实现 __next__() 方法, __next__() 方法返回下一个元素,如果没有元素了,会抛出 StopIteration 异常。

# 自定义迭代器类

两个特性: __iter__()__next__() 方法

class MyIterator:
    def __init__(self, data):
        self.data = data
        self.index = 0
    def __iter__(self):
        return self # 返回迭代器对象本身
    def __next__(self):
        if self.index < len(self.data):
            result = self.data[self.index]
            self.index += 1
            return result
        else:
            raise StopIteration # 抛出 StopIteration 异常,表示迭代结束
# 使用自定义迭代器类
my_iter = MyIterator([1, 2, 3])
for i in my_iter:
    print(i)

# 生成器 generator

python 中一边循环一边计算的机制,称为生成器(generator)。

  1. 生成器表达式
for i in range(5)
    print(i*5)
# 等价于
li = [i*5 for i in range(5)]
gen = (i*5 for i in range(5)) # 列表推导式的 [] 改为 () 就是生成器表达式
print(li)
print(gen)
# 取值
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))
print(next(gen))

# 生成器函数

python 中,只要函数体内部包含 yield 关键字,那么这个函数就变成一个生成器函数,调用生成器函数会返回一个生成器对象。
yield 关键字的作用是:

  • 类似于 return, 将指定值或者多个值返回给调用者
  • yield 语句一次返回一个结果,在每个结果中间,挂起函数,执行 next (), 再重新从挂起处开始执行
def my_generator():
    yield 1
    yield 2
    yield 3
gen = my_generator()
print(next(gen))
print(next(gen))
print(next(gen))
"""
输出:
1
2
3
"""
def my_generator(n):
    li = []
    a = 0
    while a<n:
        li.append(a)
        a+=1
        yield a
    print("生成器结束li:",li)
print(my_generator(5))
for i in my_generator(5):
    print(i)
"""
输出:
生成器结束li: [0, 1, 2, 3, 4]
0
1
2
3
4
"""

三者关系:

  • 可迭代对象:指实现了 python 迭代协议,可以通过 for…in… 循环遍历的对象,比如列表 list、元组 tuple、字典 dict、集合 set、字符串 str、迭代器、生成器等。
  • 迭代器:可以记住自己遍历位置的对象,直观体现就是可以 next () 函数返回值,迭代器只能往前,不能后退。(遍历完之后,再次调用 next () 会抛出 StopIteration 异常)
  • 生成器:一种特殊的迭代器,迭代器不一定是生成器,它是 python 提供的通过简单方法写出的迭代器的一种手段
    包含关系:可迭代对象(Iterable)-> 迭代器(Iterator)-> 生成器(Generator)

# 正则表达式

正则表达式:用于匹配字符串的规则,可以用来查找、替换、验证字符串等操作。

步骤:

  1. 导入 re 模块
  2. 使用 match () 函数进行匹配操作
    • re.match (pattern, string, flags=0), pattern: 正则表达式,string: 要匹配的字符串,flags: 标志位,用于控制正则表达式的匹配方式
    • 如果起始位置没有匹配成功,返回 None
  3. 如果上一步匹配成功,可以使用 Match 对象的 group () 方法获取匹配到的字符串

使用:

import re
# 匹配字符串
result = re.match("a", "abc")
if result:
    print(result.group())
else:
    print("匹配失败")

# 匹配单个字符

符号 含义
. 匹配除换行符以外的任意字符
\ 转义字符,用于匹配特殊字符
[ ] 匹配括号中的任意一个字符,例如 [a-z] 匹配任意一个小写字母
\w 匹配字母、数字、下划线
\W 匹配非字母、数字、下划线
\s 匹配任意空白字符,包括空格、制表符、换页符等
\S 匹配非空白字符
\d 匹配数字
\D 匹配非数字
\b 匹配单词边界
\B 匹配非单词边界

匹配任意字符

import re
re1 = re.match(".", "abc")
print(re1.group()) # a
re2 = re.match("1.", "123")
print(re2.group()) # 12

[] 匹配括号中的任意一个字符

import re
re1 = re.match("[abc]", "abc")
print(re1.group()) # abc
re2 = re.match("[a-z]", "abc")
print(re2.group()) # none

** \d 匹配数字

import re
re1 = re.match("\d", "123")
print(re1.group()) # 1
re2 = re.match("\d", "abc")
print(re2.group()) # none

# 匹配多个字符

符号 含义
* 匹配前面的字符 0 次或多次
+ 匹配前面的字符 1 次或多次
? 匹配前面的字符 0 次或 1 次
匹配前面的字符 m 次
匹配前面的字符 m 到 n 次

* 匹配前面的字符 0 次或多次

import re
re1 = re.match("a*", "aaa")
print(re1.group()) # aaa
re2 = re.match("a*", "b")
print(re2.group()) # b
re3 = re.match("a*", "")
print(re3.group()) # ""

+ 匹配前面的字符 1 次或多次

import re
re1 = re.match("a+", "aaa")
print(re1.group()) # aaa
re2 = re.match("a+", "b")
print(re2.group()) # none
re3 = re.match("a+", "")
print(re3.group()) # none

? 匹配前面的字符 0 次或 1 次

import re
re1 = re.match("a?", "aaa")
print(re1.group()) # a

# 匹配开头和结尾

符号 含义
^ 匹配字符串的开头
$ 匹配字符串的结尾

^ 匹配字符串的开头
注意: ^ 符号在 [] 中是取反的意思

import re
re1 = re.match("^a", "abc")
print(re1.group()) # a

$ 匹配字符串的结尾

import re
re1 = re.match("c$", "abc")
print(re1.group()) # c

# 匹配分组

符号 含义
| 匹配左右任意一个表达式
(ab) 将括号中的字符作为一个分组
\num 引用分组 num 匹配到的字符串
(?P…) 分组起别名
(?P=name) 引用别名为 name 分组匹配到的字符串

| 匹配左右任意一个表达式
注意:优先从左到右匹配

import re
re1 = re.match("a|b", "abc")
print(re1.group()) # a
re2 = re.match("a|b", "b")
print(re2.group()) # b

(ab) 将括号中的字符作为一个分组

import re
re1 = re.match('\w*@(qq|163|126).com', "123@163.com")
print(re1.group()) # 123@163.com

\num 引用分组 num 匹配到的字符串
应用:常在匹配标签时使用,例如匹配 <html> 标签

import re
re1 = re.match('<(\w*)>\w*</\\1>', '<html>login</html>')
print(re1.group()) # <html>login</html>
re2 = re.match('<(\w*)><(\w*)>.*</\2></\1>', '<html><body>login</body></html>')
print(re2.group()) #

(?P<name>...) 分组起别名
(?P=name) 引用别名为 name 分组匹配到的字符串
使用:

import re
re2 = re.match('<(?P<l1>\w*)><(?P<l2>\w*)>.*</(?P=l2)></(?P=l1)>', '<html><body>login</body></html>')
print(re2.group()) #

示例:匹配网址

import re
# res = re.match('www(\.)\w*\\1(com|cn|org)', 'www.baidu.com')
# print(res.group()) 
li = ['www.baidu.com', 'www.google.com', 'www.sina.com', 'www.163.com', 'www.qq.com']
for i in li:
    res = re.match('www(\.)\w*\\1(com|cn|org)', i)
    if res:
        print(res.group())

# 高级用法

search() 函数:
在字符串中搜索匹配的子串,返回第一个匹配的子串,如果没有匹配的子串,则返回 None。
示例:

import re
res = re.search('\d', 'abc123')
print(res.group()) # 1

findall() 函数:
在字符串中搜索匹配的子串,返回所有匹配的子串,以列表形式返回。
示例:

import re
res = re.findall('\d', 'abc123')
print(res) # ['1', '2', '3']

sub() 函数:
在字符串中搜索匹配的子串,并替换为指定的字符串。
参数格式:
sub(pattern, repl, string, count=0)

  • pattern: 正则表达式,用于匹配子串
  • repl: 替换字符串
  • string: 要替换的字符串
  • count: 最大替换次数,0 表示无限制

示例:

import re
res = re.sub('\d', '0', 'abc123')
print(res) # abc000

split() 函数:
在字符串中搜索匹配的子串,并按照匹配的子串将字符串分割成多个子串,以列表形式返回。
参数格式:
split(pattern, string, maxsplit=0, flags=0)

  • pattern: 正则表达式,用于匹配子串
  • string: 要替换的字符串
  • maxsplit: 最大分割次数,0 表示无限制
  • flags: 标志位,用于控制正则表达式的匹配方式,0 表示默认值
    示例:
import re
res = re.split('\d', 'abc123')
print(res) # ['abc', '']

compile() 函数:
将正则表达式编译成一个正则表达式对象,可以用于多次匹配操作。
参数格式:
compile(pattern, flags=0)
示例:

import re
pattern = re.compile('\d')
res = pattern.findall('abc123')
print(res) # ['1', '2', '3']

# 贪婪匹配和懒惰匹配

贪婪匹配:尽可能多的匹配字符
懒惰匹配:尽可能少的匹配字符

  1. 贪婪匹配 (默认)
import re
res = re.search('\d+', '123456')
print(res.group()) # 123456
  1. 懒惰匹配
    使用 ? 符号,表示懒惰匹配
    示例:
import re
res = re.search('\d+?', '123456')
print(res.group()) # 1

# 原生字符串

在正则表达式中, \ 符号用于转义特殊字符,例如 \d 表示匹配数字, \w 表示匹配字母或数字。
但是在 Python 中, \ 符号也是转义字符,例如 \n 表示换行符, \t 表示制表符。因此,在正则表达式中使用 \ 符号时,需要使用双反斜杠 \\ 来转义。
例如, \d 需要写成 \\d
为了避免这种麻烦,可以使用原生字符串,即在字符串前加上 rR ,例如 r'\d'
示例:

import re
res = re.search(r'\d+', '123456')
print(res.group()) # 123456

注意:在正则表达式中,匹配字符串中的字符 \ 时,需要 \\\\ , 加入原生字符串, \\ 代表 \

# os 模块

# os 模块介绍

os 模块是 Python 标准库中的一个模块,提供了许多与 操作系统交互 的函数。使用 os 模块,可以执行文件和目录操作,如创建、删除、重命名文件和目录,获取文件和目录的属性等。

# os 模块常用函数

  1. os.name :获取操作系统的名称,例如 posix 表示 LinuxMac OS Xnt 表示 Windows。
import os
print(os.name) # posix
  1. os.getenv(key) :获取环境变量的值 (环境变量名称)。
import os
print(os.getenv('PATH')) # /usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/opt/X11/bin
  1. os.path.split(path) :将路径分割成目录和文件名,以元组的形式接收,第一参数为目录,第二参数为文件名。
import os
print(os.path.split('/home/user/test.txt')) # ('/home/user', 'test.txt')
  1. os.path.dirname(path) :获取路径中的目录名。
import os
print(os.path.dirname('/home/user/test.txt')) # /home/user
  1. os.path.basename(path) :获取路径中的文件名。
import os
print(os.path.basename('/home/user/test.txt')) # test.txt
  1. os.path.exists(path) :判断路径 (文件或目录) 是否存在。
import os
print(os.path.exists('/home/user/test.txt')) # True
print(os.path.exists('/home/user/test1.txt')) # False
  1. os.path.isfile(path) :判断路径是否为文件。
import os
print(os.path.isfile('/home/user/test.txt')) # True
print(os.path.isfile('/home/user')) # False
  1. os.path.isdir(path) :判断路径是否为目录。
import os
print(os.path.isdir('/home/user')) # True
print(os.path.isdir('/home/user/test.txt')) # False
  1. os.path.abspath(path) :获取绝对路径。
import os
print(os.path.abspath('test.txt')) # /home/user/test.txt
  1. os.path.isabs(path) :判断路径是否为绝对路径。
import os
print(os.path.isabs('/home/user/test.txt')) # True
print(os.path.isabs('test.txt')) # False
  1. os.path.join(path, *paths) :将多个路径组合成一个路径。
import os
print(os.path.join('/home/user/', 'test.txt')) # /home/user/test.txt
  1. os.path.getsize(path) :获取文件大小,单位为字节。
import os
print(os.path.getsize('/home/user/test.txt')) # 12345

# sys 模块

sys 模块是 Python 标准库中的一个模块,提供了许多与 Python解释器交互 的函数。使用 sys 模块,可以获取 Python 解释器的信息,如版本号、命令行参数等,还可以控制 Python 解释器的行为,如退出程序、改变标准输入输出等。

# sys 模块常用函数

  1. sys.getdefaultencoding() :获取 Python 解释器的默认编码。
import sys
print(sys.getdefaultencoding()) # utf-8
  1. sys.path() :获取 Python 解释器的命令行参数。
import sys
print(sys.path) # ['/home/user', '/usr/lib/python38.zip', '/usr/lib/python3.8', '/usr/lib/python3.8/lib-dynload', '/usr/local/lib/python3.8/dist-packages']
  1. sys.platform() :获取 Python 解释器的平台信息。
import sys
print(sys.platform) # linux
  1. sys.version() :获取 Python 解释器的版本信息。
import sys
print(sys.version) # 3.8.10 (default, Jun  4 2021, 15:09:15)

# time 模块

time 模块是 Python 标准库中的一个模块,提供了许多与 时间 相关的函数。使用 time 模块,可以获取当前时间、格式化时间、时间戳等。

时间表示方式:

  1. 时间戳:从 1970 年 1 月 1 日 00:00:00 开始计算的秒数。
  2. 时间元组:包含年、月、日、时、分、秒、星期、年中的第几天等信息的元组。
  3. 字符串时间:按照指定格式表示时间的字符串。

# time 模块常用函数

  1. time.time() :获取当前时间的时间戳。
import time
print(time.time()) # 1634567890.123456
  1. time.localtime([secs]) :将时间戳转换为本地时间的时间元组。
    返回元组元素:
索引 元素 说明
0 tm_year
1 tm_mon
2 tm_mday
3 tm_hour
4 tm_min
5 tm_sec
6 tm_wday 星期几 (0-6,0 表示周一)
7 tm_yday 一年中的第几天 (1-366)
8 tm_isdst 是否为夏令时 (0-1,0 表示不是)
import time
print(time.localtime()) # 2021-10-15 10:30:45
# time.struct_time(tm_year=2021, tm_mon=10, tm_mday=15, tm_hour=10, tm_min=30, tm_sec=45, tm_wday=4, tm_yday=288, tm_isdst=0)
  1. time.sleep(secs) :暂停程序执行 secs 秒。
import time
print('start')
time.sleep(5)
print('end')
  1. time.asctime(time_tuple) :将时间元组转换为字符串时间。
import time
print(time.asctime()) # Fri Oct 15 10:30:45 2021
  1. time.ctime() :获取当前时间的时间戳。
import time
print(time.ctime()) # Fri Oct 15 10:30:45 2021
  1. time.strftime(format, time_tuple) :将时间元组转换为指定格式的字符串时间。
    格式化符号:
符号 说明
%Y 年 (4 位)
%m 月 (2 位)
%d 日 (2 位)
%H 时 (24 小时制,2 位)
%M 分 (2 位)
%S 秒 (2 位)
%w 星期几 (0-6,0 表示周一)
%j 一年中的第几天 (1-366)
%a 星期几的简写 (例如:Mon)
%A 星期几的全称 (例如:Monday)
%b 月份的简写 (例如:Jan)
%B 月份的全称 (例如:January)
%z 时区 (例如:+0800)
%Z 时区名称 (例如:CST)
%x 日期的字符串表示 (例如:10/15/21)
%X 时间的字符串表示 (例如:10:30:45)
%c 日期和时间的字符串表示 (例如:Fri Oct 15 10:30:45 2021)
%U 一年中的第几周 (以周日为一周的第一天)
%W 一年中的第几周 (以周一为一周的第一天)
%V 一年中的第几周 (以 ISO 8601 标准定义的一周为一周的第一天)
%j 一年中的第几天 (1-366)
%U 一年中的第几周 (以周日为一周的第一天)
%W 一年中的第几周 (以周一为一周的第一天)
%V 一年中的第几周 (以 ISO 8601 标准定义的一周为一周的第一天)
import time
print(time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())) # 2021-10-15 10:30:45
  1. time.strptime(string, format) :将字符串时间转换为时间元组。
import time
print(time.strptime('2021-10-15 10:30:45', '%Y-%m-%d %H:%M:%S')) # time.struct_time(tm_year=2021, tm_mon=10, tm_mday=15, tm_hour=10, tm_min=30, tm_sec=45, tm_wday=4, tm_yday=288, tm_isdst=-1)

# logging 模块

logging 模块是 Python 标准库中的一个模块,提供了许多与 日志 相关的函数。使用 logging 模块,可以记录程序的运行信息,如错误、警告、调试信息等,方便程序调试和问题排查。
等级:

level 等级 说明
NOTSET 0 不设置级别,按照父 looger 的级别显示日志,如果 root logger, 那么会显示所有的
DEBUG 10 调试信息,一般用于开发阶段
INFO 20 一般信息,用于记录程序正常运行的信息
WARNING 30 警告信息,用于记录可能的问题,但不影响程序的正常运行
ERROR 40 错误信息,用于记录程序运行中出现的错误,但不影响程序的继续运行
CRITICAL(FATAL) 50 严重错误信息,用于记录程序运行中出现的严重错误,可能导致程序崩溃

排序:NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL

import logging
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
"""
输出:
DEBUG:root:debug message
INFO:root:info message
WARNING:root:warning message
ERROR:root:error message
CRITICAL:root:critical message
"""
# logging 默认的级别是 WARNING,所以只有 WARNING 级别以上的日志才会被输出

# logging 模块常用函数

  1. logging.basicConfig(**kwargs) :配置 root logger 的参数,设置日志级别、日志格式、日志输出位置等。

参数:

参数 说明
level 设置日志级别,默认为 WARNING 及以上
format 设置日志格式,默认为’%(asctime) s - %(name) s - %(levelname) s - %(message) s’
datefmt 设置日期格式,默认为’% Y-% m-% d % H:% M:% S’
filename 设置日志输出文件名,默认为 None,表示输出到控制台
filemode 设置日志输出文件模式,默认为’a’,表示追加;'w’表示覆盖
stream 设置日志输出流,默认为 sys.stderr,表示输出到控制台
import logging
logging.basicConfig(filename='log.log')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

# random 模块

random 模块是 Python 标准库中的一个模块,提供了许多与 随机数 相关的函数。使用 random 模块,可以生成随机数、随机选择、随机打乱等操作。

# random 模块常用函数

  1. random.random() :生成一个 0 到 1 之间的随机浮点数。
import random
print(random.random()) # 0.123456789
  1. random.randint(a, b) :生成一个 a 到 b 之间的随机整数。
import random
print(random.randint(1, 10)) # 5
  1. random.choice(seq) :从序列 seq 中随机选择一个元素。
import random
print(random.choice(['apple', 'banana', 'orange'])) # apple
  1. random.sample(population, k) :从 population 中随机选择 k 个不重复的元素,返回一个列表。
import random
print(random.sample([1, 2, 3, 4, 5], 3)) # [1, 2, 3]
  1. random.shuffle(x[, random]) :将序列 x 中的元素随机打乱。
import random
x = [1, 2, 3, 4, 5]
random.shuffle(x)
print(x) # [3, 1, 5, 2, 4]
  1. random.uniform(a, b) :生成一个 a 到 b 之间的随机浮点数。
import random
print(random.uniform(1, 10)) # 5.123456789
  1. random.randrange(start, stop[, step]) :生成一个从 start 到 stop 之间的随机整数,步长为 step。
import random
print(random.randrange(1, 10, 2)) # 3

# format 方法

format 方法是一种字符串格式化方法,用于将字符串中的占位符替换为指定的值。format 方法可以接受多个参数,也可以接受关键字参数。

# format 方法语法

str.format(*args, **kwargs)

参数:

  • *args :位置参数,可以接受任意数量的位置参数,按照顺序替换占位符。
  • **kwargs :关键字参数,可以接受任意数量的关键字参数,按照关键字替换占位符。

# 位置参数

name = 'Alice'
age = 20
print('My name is {0}, I am {1} years old.'.format(name, age)) # My name is Alice, I am 20 years old.

# 关键字参数

name = 'Alice'
age = 20
print('My name is {name}, I am {age} years old.'.format(name=name, age=age)) # My name is Alice, I am 20 years old.

# 占位符

占位符的语法为 {} ,可以包含以下内容:

  • : :指定格式化方式,例如 {0:.2f} 表示将第一个参数格式化为保留两位小数的浮点数。
  • ! :指定转换方式,例如 {0!r} 表示将第一个参数转换为字符串表示。
  • # :指定进制,例如 {0:#x} 表示将第一个参数格式化为十六进制表示。
  • * :指定宽度,例如 {0:*<10} 表示将第一个参数格式化为宽度为 10 的字符串,左对齐。
  • + :指定符号,例如 {0:+} 表示将第一个参数格式化为带符号的整数。
  • - :指定对齐方式,例如 {0:-<10} 表示将第一个参数格式化为宽度为 10 的字符串,左对齐。
  • = :指定填充字符,例如 {0:=^10} 表示将第一个参数格式化为宽度为 10 的字符串,居中对齐,填充字符为 =
  • 0 :指定填充字符,例如 {0:0>10} 表示将第一个参数格式化为宽度为 10 的字符串,右对齐,填充字符为 0
  • , :指定千位分隔符,例如 {0:,} 表示将第一个参数格式化为带千位分隔符的整数。
  • b :将整数格式化为二进制表示。
num = 1234567890
print('The number is {0:,}'.format(num)) # The number is 1,234,567,890
print('The number is {0:b}'.format(num)) # The number is 111010110111100110100010010
print('The number is {0:#x}'.format(num)) # The number is 0x499602d2
print('The number is {0:0>10}'.format(num)) # The number is 0000004996
print('The number is {0:*<10}'.format(num)) # The number is 499602d2****
print('The number is {0:+}'.format(num)) # The number is +1234567890
print('The number is {0:-<10}'.format(num)) # The number is 1234567890-----
print('The number is {0:=^10}'.format(num)) # The number is =1234567890==