面向对象

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

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

类的定义
在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)