面向对象和面向过程是两种编程思想,是解决问题的一种手段
面向对象的三大特征:封装、继承、多态
类
将具有共同特征和行为的一组对象抽象定义,就是类的概念
泛指的,指代多个,是抽象出来的
在代码中通过关键字class定义
类的三部分:
类的名称
类的属性:一组数据
类的方法:允许对进行操作的方法(行为)
类的抽象
名词提炼法
所见即所得
类的定义
类名:Dog
属性:
姓名:name
年龄:age
方法:
吃 eat()
玩 play()
......
class Dog(object):
def play(self):
print('小狗玩耍!!!')
pass
对象
dog = Dog()
dog.play()
具体到类中特定的一个
在代码中,对象是由类创建的
创建对象 变量 = 类名()
在类外部添加和获取对象属性
class Dog(object):
def play(self):
print('小狗拆家中。。。。。')
# 创建对象
dog = Dog()
# 调用对象的方法
dog.play()
# 给对象添加属性:对象名.属性名 = 属性值
dog.name = '大黄'
dog.age = 2
# 获取对象的属性值:对象名.属性名
print(dog.name)
# 修改属性值:对象名.属性名 = 属性值
dog.name = '小黄' # 有值就是修改,没有就是添加
封装
在类内部通过self来操作属性
class Dog(object):
# self作为类中调用方法的第一个形参,在通过对象调用方法的时候,不需要手动的传递实参值
# 是python解释器自动将调用该方法的对象传递给self,所以self这个形参代表的是对象
def play(self): # self就是个形参名,只不过默认是self
print('小狗拆家中。。。。。')
print(f'小狗{self.name}在拆家中。。。。。')
dog = Dog()
dog.name = '大黄'
dog.play()
dog.age = 2
魔法方法
在python的类中,有一类方法,这类方法会以两个下划线开头,两个下划线结尾,并且在满足某个特定条件
的情况下,会自动调用,这类方法成为魔法方法。
如何学习魔法方法:
魔法方法在什么情况下会自动调用
这个魔法方法有什么作用
这个魔法方法有什么注意事项
# __init__()
# 调用时机,在创建对象之后,立即调用
# 作用:
# 1.用来给对象添加属性,给对象属性一个初始值(构造函数)
# 2.代码的业务需求,每创建一个对象,都需要执行的代码可以写在'__init'中
# 注意:如果__init__()方法中,有除了self之外的形参,需要在创建对象的时候,需要给额外的形参传递实参值
class Dog(object):
def __init__(self): # self就是个形参名,只不过默认是self
print('我是__init__方法,我被调用了')
self.name = '小狗'
# 创建了对象,直接执行__init__()方法
dog = Dog()
print(dog.name)
# 带参数的__init__()方法
class Dog(object):
def __init__(self, name): # self就是个形参名,只不过默认是self
print('我是__init__方法,我被调用了')
self.name = name
# 创建了对象,直接执行__init__()方法
dog = Dog('大黄')
print(dog.name)
# __str__()
# 调用时机:
# 1.'print(对象)',会自动调用__str__()方法,打印输出的结果是__str__()方法的返回值
# 2.'str(对象)'类型转换,将自定义对象转换为字符串的时候,会自动调用
# 应用:
# 1. 打印对象的时候,输出一些属性信息
# 2. 需要将对象转换为字符串类型的时候
# 注意点:
# ‘方法必须返回一个字符串’,只有self一个参数
class Dog(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
print('我是__str__(),我被调用了')
return f'小狗的名字是{self.name},年龄是{self.age}'
dog = Dog('大黄', 2)
print(dog) # 没有定义__str__()方法的话,print(对象)默认输出对象的引用地址
print(str(dog)) # 没有定义__str__()方法的话,类型转换,赋值的也是引用地址
# __del__()
# 析构函数
# 调用时机:
# 对象在内存中被销毁删除的时候(引用计数为 0)会自动调用__del__()方法
# 1. 程序代码运行结束,在程序运行中创建的所有对象和变量都会被删除销毁
# 2. 使用 'del变量' ,将这个对象的引用计数变为0,会自动调用__del__()方法
# 应用场景
# 对象被销毁的时候要执行的代码,写在'__del__()'中
# 引用计数
# 引用计数:是python内存管理的一种机制,是指一块内存,有多少个变量在引用。
# 1.当一个变量,引用一块内存的时候,引用计数加1
# 2.当删除一个变量,或者这个变量不再引用这块内存,引用计数减1
# 3.当内存的引用计数变为0的时候,这块内存被删除,内存中的数据被销毁
class Dog(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
print('我是__str__(),我被调用了')
return f'小狗的名字是{self.name},年龄是{self.age}'
def __del__(self):
print(f'我是__del__方法,我被调用了')
dog = Dog('大黄', 2) # 大黄引用计数为1
dog2 = dog # 大黄引用计数为2
print("第一次调用之前")
del dog # dog变量不能使用,大黄引用计数为1
print("第一次调用之后")
print("第二次调用之前")
del dog2 # dog2变量不能使用,大黄引用计数为0,会立即调用__del__()方法
print("第二次调用之后")
# __repr__
class Dog(object):
def __init__(self, name, age):
self.name = name
self.age = age
def __str__(self):
return f'小狗的名字是{self.name},年龄是{self.age}'
def __repr__(self): # 适用于:将对象放在容器里面,打印容器的时候
"""repr方法和str方法类似,都必须返回一个字符串"""
return f"{self.name}"
dog_list = [Dog('小黄', 2), Dog('小花', 3), Dog('小白', 2)]
# 没有repr方法的时候输出:[<__main__.Dog object at 0x0000016E62BD7460>, <__main__.Dog object at 0x0000016E62BE6A60>, <__main__.Dog object at 0x0000016E62C851F0>]
# 有repr方法的时候输出:[小黄, 小花, 小白]
print(dog_list)
# 类名.__mro__可以查看当前类的继承顺序链,也叫做方法的调用顺序
print(Hsq.__mro__)
# 实例对象.__dict__ 可以查看对象具有的属性信息,类型是字典,字典的key是属性名,字典的value是属性值
# 类名.__dict__ 查看类对象所具有的属性
案例
# 烤地瓜案例:
# 封装的小套路:
# 1.根据文字的描述信息,确定对象,对象有什么,就是属性
# 2.根据文字的描述信息,对象能干什,就是方法
# 3.根据文字的描述信息,对象的方法怎么写
"""
类名:地瓜类,Potato
属性:
状态 status = '生的'
烧烤总时间 total_time = 0
调料 name_list = []
方法:
def cook(self, 烧烤时间):
计算烧烤的总时间
修改地瓜的状态
pass
# 添加调料
def add()
输出信息 __str__():
定义属性 __init__():
"""
class Potato(object):
def __init__(self):
self.status = '生的'
self.total_time = 0
self.name_list = [] # 保存调料的内容
def cook(self, time):
# 计算总时间:
self.total_time += time
# 修改地瓜的状态
if self.total_time < 3:
self.status = '生的'
elif self.total_time <6:
self.status = '半生不熟'
elif self.total_time <8:
self.status = '熟了'
else:
self.status = '烤糊了'
def __str__(self):
# 字符串。join(列表),将字符串添加到列表的每个元素之间,组成新的字符串
buf = ','.join(self.name_list)
if self.name_list:
return f'地瓜的状态<<{self.status}>>,烧烤的总时间<{self.total_time}>,调料有:<{buf}>'
else:
return f'地瓜的状态<<{self.status}>>,烧烤的总时间<{self.total_time}>,还没有添加调料'
def add(self, name):
self.name_list.append(name)
# 创建对象
potato = Potato()
print(potato)
potato.cook(4)
potato.add('油')
print(potato)
potato.cook(3)
potato.add('辣椒')
potato.add('孜然')
print(potato)
potato.cook(2)
print(potato)
# 搬家具案例
"""
规则:
1.家具分不同的类型,并占用不同的面积
2.输出家具的信息时,显示家具的类型和家具占用的面积
3.房子有自己的地址和占用的面积
4.房子可以添加家具,如果房子的剩余面积可以添加家具,则提示家具添加成功;否则提示添加失败
5.输出房子信息时,可以显示房子的地址、占地面积、剩余面积
"""
"""
类名:家具 Furniture
属性:
类型 name
面积 area
方法:
输出家具信息 __str__
定义属性 __init__
------------------------
类名:房子 House
属性:
地址 address
面积 h_area
家具列表 furniture_list = []
方法:
添加家具 add_furniture()
输出房子信息 __str__
定义属性 __init__
"""
# 定义家具类Furniture类
class Furniture(object):
def __init__(self, name, area):
# 类型
self.name = name
# 面积
self.area = area
def __str__(self):
return f'家具的名字<{self.name}>,家具的面积<{self.area}>平'
# 定义房子类
class House(object):
def __init__(self, address, area):
self.address = address
self.h_area = area
self.furniture_list = []
self.freeArea = area
def __str__(self):
if self.furniture_list:
buf_list = [obj.name for obj in self.furniture_list]
buf = ','.join(buf_list)
return f'房子的地址是<{self.address}>,房子的面积是<{self.h_area}>,房子的剩余面积是<{self.freeArea}>,房子里的家具有:<{buf}>'
else:
return f'房子的地址是<{self.address}>,房子的面积是<{self.h_area}>,房子里面还没有家具'
def addFurniture(self, obj_furniture):
if self.freeArea >= obj_furniture.area:
self.freeArea -= obj_furniture.area
self.furniture_list.append(obj_furniture)
print(f'家具:<{obj_furniture.name}>添加成功')
else:
print('家具添加失败,房间面积不足')
# 创建家具对象
bed = Furniture('床', 10)
print(bed)
house = House('大house', 100)
print(house)
house.addFurniture(bed)
house.addFurniture(bed)
print(house)
继承
基本语法
# 继承:描述的是类与类之间的所属关系
# 基本语法:
class 类B(类A):
pass
"""
称为类B继承类A
特点:B类的对象可以使用A类的属性和方法
优点:代码复用
类A : 父类 基类
类B : 子类 派生类
"""
# 定义一个动物类
class Animal(object):
# 在动物类中书写方法
def play(self):
print("玩耍中")
# 定义一个dog类
class Dog(Animal):
pass
# 创建dog对象,调用animal类的方法
dog = Dog()
dog.play()
单继承和多继承
"""
单继承:一个类只有一个父类
多继承:一个类有多个父类
多层继承:dog --> animal --> object
子类可以使用所有继承链中的类中的方法和属性
"""
```python
# 定义Dog类,定义bark方法和eat方法
class Dog(object):
def bark(self):
print("汪汪汪")
def eat(self):
print("吃东西")
# 定义God类,定义play方法和eat方法
class God(object):
def play(self):
print("在云中飘")
def eat(self):
print("吃蟠桃")
# 定义Hsq类,继承Dog类和God类
# 它有两个父类,这个继承关系称为多继承,它可以继承两个父类中的属性和方法
class Hsq(Dog, God):
def eat(self):
# 调用指定父类中的方法
# 方法一:类名.方法名
Dog.eat(self)
God.eat(self)
# 方法二:super(类A, self).方法名(参数) 类A的继承顺序链中的下一个类中的方法
super(Hsq, self).eat() # 调用Dog类中的方法
super(Dog, self).eat() # 调用God类中的方法
pass
# 创建Hsq类对象
hsq = Hsq()
hsq.bark() # 调用Dog父类中的方法
hsq.play() # 调用God父类中的方法
hsq.eat() # 这时候两个父类中都有这个方法,这时候子类对象默认调用第一个父类中的方法
# 类名.__mro__可以查看当前类的继承顺序链,也叫做方法的调用顺序
print(Hsq.__mro__)
重写
"""
重写:子类定义和父类中同名的方法
为什么重写:父类中的方法不能满足子类的需求
重写后的特点:重写后,子类调用自己的方法,父类调用自己的方法
"""
# 定义dog类,书写bark方法,输出狗类的叫声
class Dog(object):
def bark(self):
print("汪汪汪...")
# 定义hsq类,继承dog类,重写bark方法
class Hsq(Dog):
def bark(self):
print("嗷嗷嗷...")
# 分别实例化dog和hsq,调用bark方法
dog = Dog()
hsq = Hsq()
dog.bark()
hsq.bark()
重写之后子类调用父类的同名方法
class Dog(object):
def bark(self):
print("汪汪汪...")
class Hsq(Dog):
def bark(self):
print("嗷嗷嗷...")
def see_host(self):
# 看见主人后要汪汪叫,也就是调用父类的同名方法
print("看见主人了,汪汪叫")
# 方法1:父类名.方法名(self, 其他参数)
# python解释器会自动将对象作为实参值传递给self形参,如果是通过类名.方法()调用,python解释器就不会自动传递实参值,需要手动给self形参传递实参值
Dog.bark(self)
# 方法2:super(当前类, self).方法名(参数), 会调用当前类的父类中的方法
super(Hsq, self).bark()
# 方法3:是方法2的简写 super().方法名(参数) ⇒ super(当前类, self).方法名(参数)
super().bark()
继承中的init方法
class Dog(object):
def __init__(self, name):
# 添加属性
self.age = 0
self.name = name
def __str__(self):
return f"小狗的年龄为<{self.age}>, 名字为<{self.name}>"
def bark(self):
print("汪汪汪...")
class Hsq(Dog):
# 子类重写父类的__init__方法,默认不再调用父类的init方法,需要手动调用父类的init方法
def __init__(self, name, color): # 子类init方法的形参一般先写父类的形参,再写自己独有的形参
super().__init__(name)
self.color = color
def __str__(self):
return f"小狗的年龄为<{self.age}>, 名字为<{self.name}>, 毛色为<{self.color}>"
def bark(self):
print("嗷嗷嗷...")
hsq = Hsq('小黑', '红色')
私有权限
"""
私有属性和方法
访问权限控制:在什么地方可以使用和操作
私有权限:
定义:在方法和属性前加上两个下划线,就变为私有了
1.不能在类外部通过对象直接访问和使用,只能在类内部访问和使用
2.不能被子类继承
目的:保护数据的相对安全
公有:不是私有的就是公有的
想要使用和访问私有属性:定义一个公有的方法,通过这个方法来使用它
"""
# 定义一个People类,定义属性ICBC_money,钱不能随便被修改,必须是合法的终端才可以操作
class People(object):
def __init__(self):
self.__ICBC_money = 0 # 定义私有属性
# 定义公有的方法,提供接口,修改余额
def get_money(self):
return __ICBC_money
def set_money(self, money):
self.__ICBC_money += money
"""
私有方法:在方法的前面加上两个下划线
不能在类的外部访问
作用:一般作为类内部的方法使用,不让外部调用的时候才定义,保证业务逻辑不被破坏
"""
class Dog(object):
def born(self):
print("生了一个小狗")
self.__sleep()
def __sleep(self):
print("休息30天")
类属性、类方法、静态方法
"""
对象(实例对象):通过class定义的类创建的,即通过类实例化过来的,又称为实例,实例对象
实例对象定义的一些属性称为实例属性,通过实例对象(self)定义的属性都是实例属性
实例属性:每个实例对象中都存在一份,并且值都不一定一样
类(类对象):通过class定义的,又称为类对象,是python解释器在创建类的时候自动创建的
作用:
1.通过类对象去定义实例对象
2.类对象可以保存一些属性信息,称为类属性
类属性的定义:在类内部,方法外部定义的变量就是类属性
类属性:在内存中只有一份
如何确定一个属性是该定义为实例属性还是类属性
先假设这个属性为实例属性,查看这个属性值对于不同的实例对象,属性值是否都一样,并且需要同时变化
如果是,则可以定义为类属性
否则可以定义为实例属性
注意:如果不存在和实例属性名相同的类属性,则可以使用实例对象访问类属性的值
如果存在重名,则使用实例属性访问的一定是实例属性,不是类属性
"""
class Dog(object):
# 定义类属性
class_name = '狗类'
def __init__(self, name, age):
# 定义的都是实例属性
self.name = name
self.age = age
# 访问类属性
# 类名.类属性
print(Dog.class_name)
# 修改类属性
# 类名.类属性 = 属性值
Dog.class_name = 'Dog类'
"""
实例方法:类中默认定义的方法,就是实例方法,第一个参数为self,表示实例对象
类方法:使用@classmethod 修饰的方法,称为类方法,第一个参数是cls,代表的是类对象自己
静态方法:使用@staticmethod装饰的方法,称为静态方法,对参数没有特殊要求,可以有可以没有
什么时候定义实例方法,什么时候定义类方法,什么时候定义静态方法?
1.如果在方法中使用了实例属性,那么该方法必须定义为实例方法
2.不需要使用实例属性,需要使用类属性,可以将这个方法定义为类方法
3.不需要使用实例属性,同时也不需要使用类属性,此时可以将这个方法定义为静态方法
"""
class Dog(object):
class_name = '狗类'
def __init__(self, name, age):
# 定义的都是实例属性
self.name = name
self.age = age
def play(self): # 实例方法
print(f"小狗<{self.name}>在快乐的玩耍")
def get_class_name(self): # 实例方法,但是定义为类方法更好
return Dog.class_name
@classmethod
def get_class_name2(cls): # cls是类方法的默认的形参,在调用的时候,不需要手动传递,python解释器会自动传递
return cls.class_name
@staticmethod # 定义静态方法
def show_info():
print("这是一个Dog类")
# 调用方法1:对象.类方法()
dog = Dog('大黄', 2)
print(dog.get_class_name2())
# 调用方法2:类名.类方法()
print(Dog.get_class_name2())
# 调用静态方法,有两种:
dog.show_info()
Dog.show_info()
多态
"""
多态:在需要使用父类对象的地方,也可以传入子类对象,得到不同的结果
实现步骤:
1.子类继承父类
2.子类重写父类中的重名方法
3.定义一个共同的方法,参数为父类对象,在方法中调用子类和父类同名的方法
"""
class Dog(object):
def __init__(self, name):
self.name = name
def play(self):
print("小狗<{self.name}>在玩耍")
class Hsq(Dog):
def play(self):
print(f"小狗<{self.name}>在拆家")
# 定义一个共同的方法
def play_with_dog(obj_dog):
obj_dog.play()
# 传不同的对象,得到不同的结果
dog = Dog('大黄')
hsq = Hsq('小黑')
play_with_dog(dog)
play_with_dog(hsq)