py学习6——面向对象

面向对象和面向过程是两种编程思想,是解决问题的一种手段

面向对象的三大特征:封装、继承、多态

将具有共同特征和行为的一组对象抽象定义,就是类的概念
泛指的,指代多个,是抽象出来的
在代码中通过关键字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)
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值