面向对象
面向对象编程是一种编程范式或编程风格。它以类和对象为核心,将数据(属性)和操作(方法)封装在一起,通过继承和多态等机制实现代码的重用和扩展。
类
类是面向对象编程中的基本概念,它是一种抽象的数据类型,用于定义对象的属性和行为。类可以看作是对象的模板,通过类可以创建多个具有相同属性和行为的对象。
类的定义
在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>
"""
- 构造函数
__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)
- 析构函数
__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
"""
重写
- 子类重写父类的方法
子类可以重写父类的方法,即在子类中定义一个与父类方法同名的方法,从而覆盖父类的方法。
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:子类在需要拓展的父类方法下写父类名.方法名()
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}岁") # × 不能访问实例属性
单例模式
含义:一种常见的设计模式,用于确保一个类只有一个实例,并提供一个全局访问点。
优点:节省内存,避免重复创建对象。
弊端:多线程访问容易引发线程安全问题。
方式
- 通过@classmethod实现
- 通过装饰器实现
- 通过重写__new__方法实现
- 通过模块导入实现
__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__方法实现单例模式
设计流程:
- 定义一个类属性,初始化为None,用来记录单利对象的引用
- 重写__new__方法
- 进行判断,如果类属性为None,把__new__()返回对象的引用保存进去
- 返回类型属性中记录的对象引用
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)
应用场景
-
- 回收站对象
-
- 音乐播放器
-
- 开发游戏软件 场景管理器
-
- 数据库配置、数据库连接池的设计
-
- 线程池、连接池、缓存、日志对象
魔法方法&魔法属性
魔法方法:在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': '小明'}
文件操作
文件:存储在硬盘上的数据
文件操作步骤:
- 打开文件
- 读写文件
- 关闭文件
文件对象的方法
- open()函数
open()
创建一个file对象,默认是以只读模式打开。
file = open("文件路径", "打开模式")
- read(n)
read()
方法用于读取文件内容,n表示读取的字符个数,如果不指定n,则表示读取整个文件。
file = open("文件路径", "打开模式")
content = file.read()
file.close()
- write()
write()
方法用于向文件中写入内容。
file = open("文件路径", "打开模式")
file.write("要写入的内容")
file.close()
- close()
close()
方法用于关闭文件。
file = open("文件路径", "打开模式")
file.close()
属性
- name
name
: 返回要打开文件的文件名,可以包含具体的路径。
file = open("文件路径", "打开模式")
print(file.name)
- mode
mode
: 返回打开文件时使用的模式,如’r’、‘w’、'a’等。
file = open("文件路径", "打开模式")
print(file.mode)
- closed
closed
: 返回文件是否已经关闭,True表示已经关闭,False表示已经打开。
file = open("文件路径", "打开模式")
print(file.closed)