什么是面向对象
面向对象(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__()
我们会通过实参self
向Person()
传递名字、年龄和性别,self
会自动传递
所以每当根据Person
类创建实例的时候,只需要给最后的形参(name,age,gender)
__init__()
方法下的变量都有前缀self
,以self为前缀的变量可以供类中的所有方法使用,可以通过类的任何实例来访问。
self.name = name获取与形参name相关联的值,并将其赋给变量name,然后该变量被关联到当前创建的实例。self.age = age的作用与此类似
像这样的,可以通过实例访问的变量称为属性
Person
类中还定义了其他的三个方法,这些方法执行不需要额外信息,所以它们只有一个形参self
,随后创建的实例可以访问这些方法
给属性指定默认值
有的时候我们的实例中的属性不需要通过形参定义
可以在__init__()
中为其指定默认值
class Gril:
def __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类,就是一种封装的思想,其实编写类,用类去描述各种各样的现实世界的抽象事物,类是抽象,实例就是具体,从抽象到具体,这就是面向对象做的事
而重写方法、丰富方法、添加各种各样的属性就是多态.
类编码的风格
在编写程序的时候,编码风格或者说规范是及其重要的
类名应该用 驼峰命名法,类名中每个单词首字母大写,不适用下划线
实例名采用小写,在单词之间加上下划线
对于每个类,都应紧跟在类定义后面包含一个文档字符串。这种文档字符串简要地描述类的功能,并遵循编写函数的文档字符串时采用的格式约定。