手撕Python之面向对象

2024-09-07 16:36
文章标签 python 面向对象

本文主要是介绍手撕Python之面向对象,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


1.面向对象

面向过程和面向对象

面向过程:

变量和函数。 “散落” 在文件的各个位置,甚至是不同文件中。

看不出变量与函数的相关性,非常不利于维护,设计模式不清晰。

经常导致程序员,忘记某个变量的相关性,而导致无法检测的错误

面向过程:

相关的变量和函数都“封装” 在对象里,以对象为单位来管理代码。

变量与函数的相关性清晰,利于维护,设计模式清晰。

程序员可以配合“继承” 来提高代码的可重用性,加强合作开发。

区分

面向过程的变成侧重点于编写一系列的步骤来解决问题

面向对象的变成侧重于创建对象和类,这些对象和类封装了数据和处理这些数据的方法

下面我将通过一个简单的例子来展示这两种编程范式的区别。假设我们要编写一个程序,用于计算一个矩形的面积。

面向过程的编程(C语言示例)

在面向过程的编程中,我们通常直接编写函数来执行特定的任务。

#include <stdio.h>// 定义一个函数来计算矩形的面积
int calculateRectangleArea(int length, int width) {return length * width;
}int main() {int length, width;// 获取用户输入printf("请输入矩形的长度: ");scanf("%d", &length);printf("请输入矩形的宽度: ");scanf("%d", &width);// 调用函数计算面积int area = calculateRectangleArea(length, width);// 输出结果printf("矩形的面积是: %d\n", area);return 0;
}

在这个C语言的例子中,我们定义了一个函数calculateRectangleArea来计算面积,然后在main函数中获取用户输入并调用这个函数。

面向对象的编程(Python示例)

在面向对象的编程中,我们创建类来封装数据和行为。

class Rectangle:def __init__(self, length, width):self.length = lengthself.width = widthdef calculate_area(self):return self.length * self.width# 创建矩形对象
rectangle = Rectangle(5, 3)# 调用方法计算面积
area = rectangle.calculate_area()# 输出结果
print(f"矩形的面积是: {area}")

在这个Python的例子中,我们定义了一个Rectangle类,它有两个属性(length和width)和一个方法(calculate_area)。我们创建了一个Rectangle对象,并调用其方法来计算面积。

主要区别

  1. 封装:面向对象编程通过类来封装数据和方法,而面向过程编程通常不封装数据和函数。

  2. 可重用性:面向对象编程通过继承和多态性提高了代码的可重用性。

  3. 维护性:面向对象编程通常更容易维护,因为每个对象都是独立的单元。

  4. 抽象:面向对象编程提供了更好的抽象,通过类和对象隐藏了实现细节。

这些例子展示了两种编程范式的基本思想,但在实际应用中,它们可以更加复杂和多样化。

就我总结:python中我们直接调用函数,不会像是c语言那么复杂一个一个进行定义

面向对象的介绍

借鉴函数的封装思维,再次对函数和变量进行封装,来提高代码的复用性。用关键字 class 定义,为了便于区分,把的代码块称为 类。

我们使用类将函数和变量进行封装 ,我们使用class这个关键字进行封装

变量就是属性,函数就是方法

'''
class 类名:属性\方法
属性:变量()
方法:函数()'''#定义一个人类
class Person:name='小明'   #类属性def work():#类方法print(f'{Person.name}的工作是厨师')
#这里的name 是类属性我们需要通过类名进行调用
#变量是属性,函数是方法#上面我们就实现了一个类的创建了
#类是通过对象进行实现后续的操作的
#那么我们就需要创建一个对象#创建对象:类名()
print(Person)
#<class '__main__.Person'>p1=Person()
print(p1)
#<__main__.Person object at 0x0196F970>
#这个p就是我们创建的对象
#我们每次创建对象编译器就会为其进行空间的分配
#一个类是可以进行多个对象的创建的
p2=Person()
#我们对于类属性和类方法的访问
#通过对象直接对类中的元素进行访问
print(p1.name)#p1.work()
#这么写是会报错的
#对象在调用方法时,会将对象信息传递给方法
#所以我们在利用对象调用类方法的时候是传递了一个类似于指针的东西
#但是我们的函数中并没有进行接收的操作
#所以这个是会进行报错的#所以我们在调用类方法的时候我们是不能直接通过对象进行调用的#综上所述:#想要调用类属性的话
#类名.属性名
print(Person.name)
#想要调用类方法的话
#类名.方法名
print(Person.work())#小明
#小明
#小明的工作是厨师

p1.work()

这么写是会报错的

对象在调用方法时,会将对象信息传递给方法

所以我们在利用对象调用类方法的时候是传递了一个类似于指针的东西

但是我们的函数中并没有进行接收的操作

所以这个是会进行报错的

那么我们如何通过这个对象进行类方法的调用呢?

我们在这个类方法中添加一个参数self用来接收传过来的对象信息

那么我们就能进行调用了

这种在类函数中添加self的操作叫做实例方法

哪个对象调用它,它就接收哪个对象的信息

所以self==对象本身

所以我们在类里面我们用self代表对象,在外面就用对象代表对象

类方法和实例方法的区别在于括号中是否存在self

只有调用示例化的方法我们才用创建对象

我们的方法基本上都是用实例方法

实例方法我们只能通过对象名进行调用

对于没有添加self的类方法我们只能够通过类名进行调用

每次通过类名进行类方法的调用这个里面的值都是固定的

但是我们通过这个实例方法进行调用的话,那么我们这个里面的值就随着对象的改变而改变

class Person:name='小明'   #类属性def work():#类方法print(f'{Person.name}的工作是厨师')def job(self):#实例方法print(f'self:{self}')p1=Person()
p1.job()
#<__main__.Person object at 0x03C29AB0> 这个就是对象的地址了#我们只要将类中的方法实例化我们就能通过对象进行类方法的调用了#我们在类方法中定义了self用来接受传过来的对象信息

总结下:类方法只能通过类名进行调用,每次调用的结果都是一样的,而且类方法是不能通过对象进行调用的,因为我们在调用类方法的时候通常是会在函数中传递实参的,但是我们的类函数中并没有形参进行接收,那么就会造成报错的现象,但是我们的实例方法中,我们在原本的类函数的基础上添加了self这个参数用来接受对象的信息的,那么在这个实例方法中我们就有形参进行接收调用函数时传过来的对象信息了,我们在外面通过对象进行对象的访问,但是在这个实例方法中我们通过self进行访问对象,因为self指向的是对应的对象

实例方法是可以根据对象的不同而导致传递的值就不痛,就不会像类方法那么固定了

通过实例方法的话我们函数中的参数随着 对象的变化进行变化

实例化对象的介绍

假如我们现在有三个人,我们分别调用这个实例方法,我们都是不会相互影响的,因为三个人的self都是不同的,不会互相影响,这个就是实例方法的好处

如果想使用类方法的话,那么所有的对象都是一样的东西

学到这里我们就理解这个类方法和实例方法的区别了

class Mode:#模型类#类方法:对于所有实例对象统一的功能def mode():print("这是一个苹果15手机壳模型")#实例方法:应用于对象,对于对象不同,内容不同def types(self0,color,mat):print(f"这是一个{color}颜色{mat}材质的手机壳")#调用类方法
Mode.mode()#类名.类方法进行调用
#这是一个苹果15手机壳模型#调用示例方法:实例对象.实例方法()
#创建实例对象
m1=Mode()
m1.types("红色","金属")
#这是一个红色颜色金属材质的手机壳

类中除了属性和方法还有个占位符pass:

'''
class 类名:属性/方法/pass
属性--变量
方法--函数
pass--占位符
我们在定义类的时候我们对于类中需要写什么我们还没有想好,
我们可以通过pass进行占位的操作的
'''class Pub:pass#进行占位
#这么就不会出现报错,但是我们如果不加pass的话,并且类中是空的,那么就会报错

如果我们没有想好类里面写什么的话,使用pass进行占位置,这样这个类就不会报错了

类的使用---实例化对象:类名

class Student:pass
print(Student)
#<class '__main__.Student'>
s1=Student()
print(s1)
#<__main__.Student object at 0x0184F090>

类方法:用于共享的操作,所有对象是一致的

实例方法:用于不同的对象有不同的数据

定义实例属性:在实例方法中通过self.属性名=值

实例属性一但被定义了,那么在实例方法中都能进行调用的

不存在之前函数内变量的局部域和全局域的说法

只要是实例方法的话就能进行实例属性的运用

class Student:#姓名、年龄、成绩---描述----属性def fun(self,name,age,grade):#接收传入的数据self.name=nameself.age=ageself.grade=grade#睡觉、吃饭、学习---动作、行为---方法  def 方法名()#self接受对象信息,即seif=对象def sleep(self):print(f'{self.name}正在睡觉')#在类里面进行实例属性的调用def eat(self):print(f'{self.name}正在吃东西')def study(self):print(f'{self.name}正在学习')s1=Student()
s1.fun("小明",12,78)
#现在fun函数里面有四个参数,第一个self我们是不用传实参的
#self是可以自动传参的,不需要手动传参的
print(s1.age)s1.sleep()
#小明正在睡觉'''
实例属性一但被定义了,那么在实例方法中都能进行调用的
不存在之前函数内变量的局部域和全局域的说法
只要是实例方法的话就能进行实例属性的运用
'''
s1.eat()
#小明正在吃东西
s1.study()
#小明正在学习
'''
s2=Student()
s2.sleep()
这么写是会报错的,因为我们是没有进行对象内属性的初始化的
'''#生成多个对象,每个对象的值都是不一样的#我们在调用其他的实例方法的之前我们一定要对对象里面的属性进行初始化
#不然后面的方法是调用不了的

我们直接使用对象名.实例方法名()进行类中的实力方法进行调用

对于上面的代码,如果我们又创建了一个对象s2,但是没有调用fun函数进行初始化,那么我们就是不能进行后面的实力函数的调用的

因为fun函数内的实力属性都没有被定义

我们需要将实例属性定义之后才能进行后续的实力属性的调用操作的

2.魔法方法

魔法方法值方法在某种情况下会自动执行

方法是只有调用下我们才会执行,但是魔法方法的话是在某种特殊的情况下会自动进行执行的

下面的析构函数和构造函数都是魔法方法

构造函数---初始化方法

格式:def init(self): 注意这里是两个下划线

作用:在创建对象的时候就会自动执行

class Student:#姓名、年龄、成绩---描述----属性def fun(self,name,age,grade):#接收传入的数据self.name=nameself.age=ageself.grade=grade#睡觉、吃饭、学习---动作、行为---方法  def 方法名()#self接受对象信息,即seif=对象def sleep(self):print(f'{self.name}正在睡觉')#在类里面进行实例属性的调用def eat(self):print(f'{self.name}正在吃东西')def study(self):print(f'{self.name}正在学习')class Test:def __init__(self,name,age):print("这是一个初始化方法")t=Test()
#这是一个初始化方法
#我们在创建对象的时候这个init函数会自动执行的,自动进行初始化操作#如果在__init__中有定义形参,那么创建对象时就要传递实参
#对于对象中都要使用到的通用属性的话,我们就会将这些属性放到__init__函数进行初始化操作
#伴随着对象的创建,那么这个对象的基本属性就初始化好了#像是上面的fun函数我们必须先调用我们才能实现这个对象属性的初始化操作
#但是现在的话我们是不需要进行调用这个init函数
#我们对象一创建好这个init函数就自动进行调用了#将一些必备的属性放在init函数中,创建对象就直接初始化好了,

将一些常用的属性放到init函数中,然后我们在创建对象的时候就能进行对象属性的初始化了

析构函数

格式:def del__(self)

在实例对象被销毁的时候执行

在对象的生命周期快到了的时候就将对象所占的空间进行清空的操作

就是释放内存空间

class Test:#构造函数def __init__(self,name):self.name=name#这个self就是指向的对象print("这是一个初始化方法")#析构函数--对对象进行销毁的操作def __del__(self):print(f'{self.name}进行销毁')t=Test("张三")print("程序执行完毕")
del t
'''
这是一个初始化方法
程序执行完毕
张三进行销毁
'''#我们只有将这个t这个对象进行销毁了,才会进行这个析构函数
#如果这个t没有被回收的话是不会进行析构函数的#我们这里是不会进行这个析构函数内的打印操作的,但是在paycharm中是会进行的
#手动的删除del执行时间是在程序结束之前进行的

py文件所有代码执行完就会自动执行析构函数

我们在创建对象t的时候在这个类括号中输入了张三

然后张三就别传到了init函数进行属性的初始化操作了

方法是只有调用下我们才会执行,但是魔法方法的话是在某种特殊的情况下会自动进行执行的

除了自动删除操作,我们还能使用手动删除操作del进行删除操作

输出魔法方法

格式: ****str(self):设置print输出对象名时显示的内容

class Test:#构造函数def __init__(self,name):self.name=name#这个self就是指向的对象print("这是一个初始化方法")#析构函数--对对象进行销毁的操作def __del__(self):print(f'{self.name}进行销毁')#设置print输出对象名时显示的内容def __str__(self):return "这是一个魔法方法__str__设置的"t1=Test("张三")
print(t1)
#这是一个魔法方法__str__设置的

这个就是可以将我们print函数输出的数据进行改变

我们想print输出啥这个就能改成啥,主要是以return进行返回来实现这个操作的

这个只能设置print输出的内容

如果我们需要改变交互模式输出的话,我们是可以通过这种魔法方法的

__ repr__(self):设置直接输出对象名时的显示内容

class Test:#构造函数def __init__(self,name):self.name=name#这个self就是指向的对象print("这是一个初始化方法")#析构函数--对对象进行销毁的操作def __del__(self):print(f'{self.name}进行销毁')#设置print输出对象名时显示的内容def __str__(self):return "这是一个魔法方法__str__设置的"#__repr__(self):设置直接输出对象名时的显示内容def __repr__(self):return '这是__repr__设置显示的内容't1=Test("张三")
print(t1)
#这是一个魔法方法__str__设置的'''
交互模式:
print(t1)
这是一个魔法方法__str__设置的
t1
这是__repr__设置显示的内容
'''

这个魔法方法可以直接将交互结果进行改变的

这个输出的话感觉用的很少

回顾:

定义实例方法:def 函数名(self)

调用实例方法:self.方法名或对象名.方法名

对象关系方法--isinstance(对象,类)

#isinstance(对象,类)
#判断对象是不是后面的类实例出的对象
print(isinstance(t1,Test))#这里我们前面创建了,只不过这里没有复制过来
#True#就是判断我们第一个参数是不是通过第二个参数(类)实例化出来的
#这个就是进行类型的判断
print(isinstance(1,int))
#True
print(isinstance(1.5,int))
#False

判断这个对象是不是这个类实例话出来的

还能判断1是不是整型

返回值都是bool值

3.继承

单继承

举个例子:就是富二代从他爸那里继承了财产,这个就叫继承,富二代什么都不做

继承发生在两个类之间的

继承的关系是双方是父类和子类

继承可以使子类具有父类的所有属性和方法

class Father:#父亲类def money(self):print('100万')#单继承:class子类名(父类名)
class Son(Father):#子类#重写:在子类中定义一个与父类方法同名的方法就是重写,那么这个子类就不会去执行父亲类了def money(self):print('1000万')#用儿子类创建出来的对象进行父亲类中实例方法的调用
s=Son()
s.money()
#100万    #子类可以通过继承父类,获取父类中的方法、属性

单继承:class子类名(父类名)

重写的操作:如果现在我们不需要使用父亲类中的方法的话,那么我们在子类中再定义一个同名的方法就能进行覆盖的操作

我们可以使用__ mro进行搜索顺序的查询

#__mro__:查询搜索顺序
print(Son.__mro__)
#(<class '__main__.Son'>, <class '__main__.Father'>, <class 'object'>)
#我们在执行这个money的时候我们先看自己类有没有这个方法
#如果自己类有这个方法的话就调用自己的,如果没有的话就看看父亲类有没有
#如果父亲类没有的话就去基类里面找,但是基类的话可能没有,那么就会进行报错的操作#自己有就用自己的,自己没有就用父亲的

(, , )

从自己开始搜索,如果自己没有就用父亲的,父亲没有的话就会报错

但是我们进行重写操作之后又想用父亲类里面的,该怎么做呢?

#但是我们进行重写操作之后又想用父亲类里面的,该怎么做呢?
#我们可以进行扩展:
#扩展可以执行父类中的同名方法
#方法一:父类名().方法名(self)class Father:def money(self):print('100万')class Son(Father):def money(self):print('1000万')
#扩展可以执行父类中的同名方法
#方法一:父类名.方法名(self)Father.money(self)
#方法二:super().方法名()#调用下一个类中的方法#就是我们的搜索顺序是子类到父类到基类#那么调用下一个类中的方法就是去父类里面找super().money()
s=Son()
s.money()
#1000万
#100万
#通过方法一:我们也能调用父亲类里面的money方法了
#100万--第三种方法

方法一:父类名.方法名(self)

方法二:super().方法名()#调用下一个类中的方法

对于方法二来说的话,我们的搜索顺序是子类→父类→基类

那么子类的下一个类中的方法就是父类中的方法

通过这两种方法我们也能去调用父类中的方法了

多继承

一个子类同时继承多个父类

class 子类(父类1,父类2,……)

#一个子类同时继承多个父类就是多继承
class Father:def money(self):print('100')
class Mother:def host(self):print("三室一厅")
class Son(Father,Mother):passs=Son()
s.money()
s.host()
#100
#三室一厅

子类啥都不干就能继承父亲和母亲的

下面的代码介绍了多父类的搜索顺序

class Father:def money(self):print('100')
class Mother:def money(self):print('1000')def host(self):print("三室一厅")
class Son(Father,Mother):#重写操作def money(self):print("10")#扩展#扩展一:super().方法名()---这个扩展的是下一个类里面的对应的方法,就是Father里面的方法super().money()#子类后面的类中第一个有money的类#扩展二   父类名.方法()Father.money(self)Mother.money(self)
s=Son()
s.money()
s.host()#现在父亲类和母亲类都有money
#那么儿子调用money的时候是调用谁的呢?#100
#三室一厅
#为什么是这样的呢?
#我们可以试试看看搜索顺序是如何的print(Son.__mro__)
#(<class '__main__.Son'>, <class '__main__.Father'>, <class '__main__.Mother'>, <class 'object'>)#先是子类,再是父类,再是母类,最后是基类
#这个顺序依照的就是我们在子类继承多类的时候写在括号内的顺序的
#class Son(Father,Mother):
#如果我们先写的是Mother的话,那么这个搜索顺序肯定是Mother排第二个

搜索的顺序取决于这个我们在继承操作时在括号中写的顺序

如果是mother写在前面那就子类过后就搜索mother中是否存在对应的方法

这个上面的代码还介绍了如何进行扩展操作的,访问父类和子类中的money方法

两种方法

建议使用这个第二种方法: 父类名.方法()

这种直接就指定了拓展哪个类里面的哪个方法

这篇关于手撕Python之面向对象的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1145582

相关文章

python: 多模块(.py)中全局变量的导入

文章目录 global关键字可变类型和不可变类型数据的内存地址单模块(单个py文件)的全局变量示例总结 多模块(多个py文件)的全局变量from x import x导入全局变量示例 import x导入全局变量示例 总结 global关键字 global 的作用范围是模块(.py)级别: 当你在一个模块(文件)中使用 global 声明变量时,这个变量只在该模块的全局命名空

【Python编程】Linux创建虚拟环境并配置与notebook相连接

1.创建 使用 venv 创建虚拟环境。例如,在当前目录下创建一个名为 myenv 的虚拟环境: python3 -m venv myenv 2.激活 激活虚拟环境使其成为当前终端会话的活动环境。运行: source myenv/bin/activate 3.与notebook连接 在虚拟环境中,使用 pip 安装 Jupyter 和 ipykernel: pip instal

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

nudepy,一个有趣的 Python 库!

更多资料获取 📚 个人网站:ipengtao.com 大家好,今天为大家分享一个有趣的 Python 库 - nudepy。 Github地址:https://github.com/hhatto/nude.py 在图像处理和计算机视觉应用中,检测图像中的不适当内容(例如裸露图像)是一个重要的任务。nudepy 是一个基于 Python 的库,专门用于检测图像中的不适当内容。该

pip-tools:打造可重复、可控的 Python 开发环境,解决依赖关系,让代码更稳定

在 Python 开发中,管理依赖关系是一项繁琐且容易出错的任务。手动更新依赖版本、处理冲突、确保一致性等等,都可能让开发者感到头疼。而 pip-tools 为开发者提供了一套稳定可靠的解决方案。 什么是 pip-tools? pip-tools 是一组命令行工具,旨在简化 Python 依赖关系的管理,确保项目环境的稳定性和可重复性。它主要包含两个核心工具:pip-compile 和 pip

HTML提交表单给python

python 代码 from flask import Flask, request, render_template, redirect, url_forapp = Flask(__name__)@app.route('/')def form():# 渲染表单页面return render_template('./index.html')@app.route('/submit_form',

Python QT实现A-star寻路算法

目录 1、界面使用方法 2、注意事项 3、补充说明 用Qt5搭建一个图形化测试寻路算法的测试环境。 1、界面使用方法 设定起点: 鼠标左键双击,设定红色的起点。左键双击设定起点,用红色标记。 设定终点: 鼠标右键双击,设定蓝色的终点。右键双击设定终点,用蓝色标记。 设置障碍点: 鼠标左键或者右键按着不放,拖动可以设置黑色的障碍点。按住左键或右键并拖动,设置一系列黑色障碍点

Python:豆瓣电影商业数据分析-爬取全数据【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】

**爬取豆瓣电影信息,分析近年电影行业的发展情况** 本文是完整的数据分析展现,代码有完整版,包含豆瓣电影爬取的具体方式【附带爬虫豆瓣,数据处理过程,数据分析,可视化,以及完整PPT报告】   最近MBA在学习《商业数据分析》,大实训作业给了数据要进行数据分析,所以先拿豆瓣电影练练手,网络上爬取豆瓣电影TOP250较多,但对于豆瓣电影全数据的爬取教程很少,所以我自己做一版。 目

【Python报错已解决】AttributeError: ‘list‘ object has no attribute ‘text‘

🎬 鸽芷咕:个人主页  🔥 个人专栏: 《C++干货基地》《粉丝福利》 ⛺️生活的理想,就是为了理想的生活! 文章目录 前言一、问题描述1.1 报错示例1.2 报错分析1.3 解决思路 二、解决方法2.1 方法一:检查属性名2.2 步骤二:访问列表元素的属性 三、其他解决方法四、总结 前言 在Python编程中,属性错误(At