Python类,面向对象详解

什么是面向对象

面向对象(Object-Oriented Programming OOP)是一种很常见的编程思想

它强调万物皆对象,因此在编程时可以将世界中的事物抽象成程序中的对象,从而更好实现软件的设计与开发

与传统的基于函数的编程不同,面向对象编程注重于将数据与行为封装在一起,即对象既包含数据状态,还包含可调用的行为方法

三大特性:

  • 封装

    将对象的状态和行为进行封装,使其对外只暴露必要的接口,提高安全性和可维护性

  • 继承

    继承指的是某个对象可以继承另一个对象的特性,从而快速构建具有相似属性的对象

  • 多态

    同一种行为在不同的对象上具有不同的表现形式,也就是说在不同的情景下,同一个方法可以被不同的对象进行调用

总之,面向对象编程是一种强大的编程方式,它具有高度封装性、灵活的继承性和强大的多态性,通过使用对象作为程序的基本处理单元,实现了数据和行为的有机结合,可以使程序更加高效、结构清晰,并方便管理和扩展。

下面我们通过学习Python中的类去深入了解面向对象

标题类与对象的关系和定义

类与对象的关系

类是一个模板,对象是根据这个模板创建的具体实例。如果将类比喻为蛋糕模具,那么对象就是蛋糕。一个类可以创建多个对象,它们之间是独立的,互相不影响。

类与对象的定义

类是一种用户自定义的数据类型,它由数据和方法组成

数据表示属性,方法表示行为

一个类可以包含多个属性和方法

属性是类的成员变量,可以存储数据

方法是一组操作数据的代码,它们可以实现某些功能或者改变属性的值

class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")

这就是一个简单的类的定义

解析方法__init__()
class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")

类中的函数称为方法

__init__()是一个特殊的方法,每当我们根据类创建新的实例时,Python都会自动运行它

在这个方法的名称中,开头和末尾各有两个下划线,这是一种约定,旨在避免Python默认方法与普通方法发生名称冲突。务必确保__init__()的两边都有两个下划线

def __init__(self,name,age,gender)

方法__init__()包含三个形参:self,name,age,gender

这个方法中,self是必不可少的,而且必须位于其他形参的前面

为什么必须在方法定义中包含形式参数self

因为:Python调用这个方法创建Person的实例时,会自动地传入实参self

每一个与实例相关联的方法调用都自动传递实参self

self是什么

self可以看作是指向实例本身的一个引用类型,让实例能访问到类中的属性和方法

person = Person("cc",18,"男")

创建Person实例时,Python会调用Person类的方法__init__()

我们会通过实参selfPerson()传递名字、年龄和性别,self会自动传递

所以每当根据Person类创建实例的时候,只需要给最后的形参(name,age,gender)

__init__()方法下的变量都有前缀self,以self为前缀的变量可以供类中的所有方法使用,可以通过类的任何实例来访问。

self.name = name获取与形参name相关联的值,并将其赋给变量name,然后该变量被关联到当前创建的实例。self.age = age的作用与此类似

像这样的,可以通过实例访问的变量称为属性

Person类中还定义了其他的三个方法,这些方法执行不需要额外信息,所以它们只有一个形参self,随后创建的实例可以访问这些方法

给属性指定默认值

有的时候我们的实例中的属性不需要通过形参定义

可以在__init__()中为其指定默认值

class Grildef __init__(self,name):
		self.name = name
		self.ifIsBeautiful = True

比如上面女孩这个类,就意味着所有的女孩都是beautiful的

创建类的实例
class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")
person = Person("cc"18"男")

像我们上面说的一样,利用了__init__()这个方法和self这个实参可以很轻松创建类的实例

我们就创建了一个名字是"cc"年龄18的男性

那么创建实例Python是怎么实现的呢?

person = Person("cc",18,"男") Python进行到这行代码时,利用传进来的实参"cc",18,"男"调用构造方法__init__()创建一个表示特定person的实例,使用提供的参数来设置实例的属性name,age,gender,然后Python会返回一个这个实例,将实例赋给变量person

访问属性

要访问实例的属性,可使用句点表示法

person.name  # cc
调用方法

要调用类定义的方法,同样用句点表示法

person.work()  # 在工作
创建多个实例
person1 = Person("科比布莱恩特",45,"男")
person2 = Person("迈克尔乔丹",55,"男")
person3 = Person("李飞飞",35,"女")
修改属性的值

我们能以两种方式修改属性的值:

  • 直接通过实例修改
  • 通过方法修改
直接修改属性的值(通过实例修改)
class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")
person1 = Person("科比布莱恩特",45,"男")
person1.age = 47

person1.age = 47使用点语法直接访问修改属性

通过方法修改

所谓的通过方法修改本质上还是通过点语法访问属性值来修改

class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")
		
	def changeAge(self):
		self.age += 2
    
    def changeName(self,newValue):
		self.name = newValue
person1.changeAge()
# 还可以实现传入值
person3.changeName("Ai教母李飞飞")

继承

编写类时,并非总是要从空白开始。如果要编写的类是另一个现成类的特殊版本,可使用继承。一个类继承另一个类时,将自动获得另一个类的所有属性和方法。原有的类称为父类,而新类称为子类。子类继承了父类的所有属性和方法,同时还可以定义自己的属性和方法

子类的 __init__()

我们怎么实现子类继承父类的所有属性和方法

还是我们的老熟人__init__()

在既有类的基础上编写新类时,通常要调用父类的方法__init__()。这将初始化在父类__init__()方法中定义的所有属性,从而让子类包含这些属性

只需要一行代码super().__init()

还记得上面我们创建了美丽的女孩类么,我们让他可以继承Person这个类

class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")
		
	def changeAge(self):
		self.age += 2
    
    def changeName(self,newValue):
		self.name = newValue
class Girl(Persn)
	def __init__(self,name,age):
		# 初始化父类属性
		super().__init__(name,age)
		self.ifIsBeautiful = True
		# 默认性别
		self.gender = "女"
为子类定义属性和方法

其实上面已经实现过了为子类添加自己独有的属性了

self.ifIsBeautiful = True

class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")
		
	def changeAge(self):
		self.age += 2
    
    def changeName(self,newValue):
		self.name = newValue
class Girl(Persn)
	def __init__(self,name,age):
		# 初始化父类属性
		super().__init__(name,age)
		self.ifIsBeautiful = True
		# 默认性别
		self.gender = "女"
	
	# 添加子类方法
	def showBeauty(self):
		print("美丽")

添加方法无非是在类里面写一个方法罢了

重写父类方法

对于父类的方法,只要不符合子类就可以重写

比如上面的,我们不想让女孩变老,可以重写changeAge方法

class Person:
	def __init__(self,name,age,gender):
		self.name = name
		self.age = age
		self.gender = gender
	
	def ear(self):
		print("吃饭")
	
	def sleep(self):
		print("睡觉")
	
	def work(self):
		print("在工作")
		
	def changeAge(self):
		self.age += 2
    
    def changeName(self,newValue):
		self.name = newValue
class Girl(Persn)
	def __init__(self,name,age):
		# 初始化父类属性
		super().__init__(name,age)
		self.ifIsBeautiful = True
		# 默认性别
		self.gender = "女"
	
	# 添加子类方法
	def showBeauty(self):
		print("美丽")
	
	# 重写父类方法
	def changeAge(self):
		self.age -= 1

这样女孩只能年轻,不会变老了,🫤

将实例作为属性

类和实例就是不同程度的抽象

当我们要写代码解决实际问题时,会发现类的细节越来越多

这时候我们就要将类的一部分提取出来,作为一个独立的类

也就是把大型类拆分为多个协同工作的小类

比如Gril就是一个很宏大的类,一个女孩有身体部位,工作,学习,化妆品等等细节要填写,如果直接在它上面写,结果难以想象,你可能会写1000行py代码,来描述一个女孩的抽象

'''
	以化妆品为例子
'''
class Person:
	....
class Girl(Persn)
	def __init__(self,name,age):
		# 初始化父类属性
		super().__init__(name,age)
		self.ifIsBeautiful = True
		# 默认性别
		self.gender = "女"
		# 用品相关的属性
		self.lipstick = "Dior"
		self.shoes = "回力"
		self.hat = "遮阳帽"
        ....
	# 添加子类方法
	def showBeauty(self):
		print("美丽")
	
	# 重写父类方法
	def changeAge(self):
		self.age -= 1

假如说,我们一直写下去,那么用品有关的属性太多了都放到一个类里面是及其吓人的,维护极不方便

这时候我们就要利用解耦的思想,把这个用品当作一个类去写,然后把用品的类实例当作Gril类的属性

class Supply:
	def __init(self):
		# 用品相关的属性
		self.lipstick = "Dior"
		self.shoes = "回力"
		self.hat = "遮阳帽"

class Girl(Persn)
	def __init__(self,name,age):
		# 初始化父类属性
		super().__init__(name,age)
		self.ifIsBeautiful = True
		# 默认性别
		self.gender = "女"
		# 实例化Supply类作为属性
		self.supply = Supply()
	# 添加子类方法
	def showBeauty(self):
		print("美丽")
	
	# 重写父类方法
	def changeAge(self):
		self.age -= 1
# 我们构建一个实例
gril1 = Gril("王菲",40)
# 访问王菲的口红
lipstick = gril1.supply.lipstick

类方法和静态方法

Python中,类方法和静态方法都是用来处理类的一些数据和行为的,它们的作用是为类的实例或类本身提供特定的功能。

两种方法都是通过类名进行调用,而不是通过实例对象进行调用

类方法

类方法是什么?

类方法顾名思义就是定义在类上的方法,可以通过类或类的实例来调用

定义类方法需要使用@classmethod装饰器

类方法的第一个参数必须是cls表示类本身,可以通过cls来调用类属性和类方法

类方法可以使用类的属性和方法,但是不能访问实例的属性和方法

类方法的使用场景:

  • 当需要在类的级别上执行操作时,而不是在实例级别上执行操作时
  • 当需要使用类的属性和方法时,而不需要访问实例的属性和方法时
class Person:
	total_persons = 0
	
	def __init__(self,name):
		self.name = name
		Person.total_persons += 1
	
	@classmethod
	def show_total_person(cls):
		print(cls.total_persons)


p1 = Person("Tom")
p2 = Person("Jerry")
Person.show_total_persons()

在上面的代码中,我们创建了一个Person类来记录创建的总人数。我们定义一个类方法show_total_persons()来显示总人数。在实例化两个Person对象后,我们使用Person.show_total_persons()来显示人数。实现了记录类的实例化个数并且提供了类方法去打印

静态方法

静态方法是定义在类上的方法,可以通过类或类的实例来调用

静态方法使用@staticmethod装饰器来标识,它不需要类或者实例作为一个参数

与类方法的区别是:静态方法不能访问类或者实例的属性和方法

静态方法没有参数限制,它只是一个普通函数,涉及到类和对象的问题,都需要在函数内部进行处理。

静态方法的使用场景:

  • 在类中实现一些与类相关的操作,但是不需要访问类的属性和方法
  • 定义一个与类无关的辅助函数,但是又想把它定义到类之内
class Calculator:
	
	@staticmethod
	def add(x,y)return x + y
Calculator.add(5,5)

类方法和静态方法的应用

# 日期计算的类似代码
from datetime import datetime

now = datetime.now()
current_year = now.year

但是这种代码重复了很多次,我们可以定义一个类方法来处理它

class DateCalculator:
 
    @classmethod
    def current_year(cls):
        now = datetime.now()
        return now.year
 
print("Current year: ", DateCalculator.current_year())

关于封装,继承,多态

其实我们在讲解类的时候,各种各样的特性已经将封装继承多态的思想贯穿到底了
比如,我们抽象出来supply类,就是一种封装的思想,其实编写类,用类去描述各种各样的现实世界的抽象事物,类是抽象,实例就是具体从抽象到具体,这就是面向对象做的事
而重写方法、丰富方法、添加各种各样的属性就是多态.

类编码的风格

在编写程序的时候,编码风格或者说规范是及其重要的

类名应该用 驼峰命名法,类名中每个单词首字母大写,不适用下划线

实例名采用小写,在单词之间加上下划线

对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。

相关推荐

  1. Python,面向对象详解

    2024-06-14 10:58:02       6 阅读
  2. Python面向对象-对象

    2024-06-14 10:58:02       39 阅读
  3. 面向对象编程中的详解

    2024-06-14 10:58:02       7 阅读
  4. python--面向对象-3

    2024-06-14 10:58:02       15 阅读
  5. python面向对象详解(一)

    2024-06-14 10:58:02       7 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-06-14 10:58:02       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-14 10:58:02       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-14 10:58:02       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-14 10:58:02       20 阅读

热门阅读

  1. windows命令帮助大全

    2024-06-14 10:58:02       7 阅读
  2. Codeforces Round 952 (Div. 4)

    2024-06-14 10:58:02       8 阅读
  3. 2024.6.13 刷题总结

    2024-06-14 10:58:02       8 阅读
  4. MySql几十万条数据,同时新增或者修改

    2024-06-14 10:58:02       6 阅读
  5. ELasticSearch数据迁移方案-elasticdump

    2024-06-14 10:58:02       7 阅读
  6. 前端针对需要递增的固定数据

    2024-06-14 10:58:02       5 阅读
  7. 如果用户访问的是没有页面的路由跳转到404

    2024-06-14 10:58:02       6 阅读
  8. Spring源码学习-Resource

    2024-06-14 10:58:02       8 阅读
  9. 行列视(RCV)能解决哪些问题?

    2024-06-14 10:58:02       8 阅读
  10. easyExcel导入日期LocalDateTime等类型不匹配问题

    2024-06-14 10:58:02       8 阅读
  11. milvus的磁盘索引

    2024-06-14 10:58:02       8 阅读
  12. npm发布自己的插件包

    2024-06-14 10:58:02       8 阅读
  13. redis的分布式session和本地的session有啥区别

    2024-06-14 10:58:02       9 阅读