面向对象初识
面向对象是一个编程范式(也就是我们写代码的套路)
满足我们的需求,比如:我现在给你一个商品列表,要你按照价格排序
首先我们要现有一个商品表
commodity_infos = [
{"cid":101,"name":"苹果","price":5},
{"cid":102,"name":"葡萄","price":8},
{"cid":103,"name":"橘子","price":3},
{"cid":104,"name":"猕猴桃","price":6},
{"cid":105,"name":"柚子","price":2}
]
输出商品信息:利用一个for循环便可以将商品的信息输出
for commodity_info in commodity_infos:
print(commodity_info)
我们的需求是:通过价格排序
思路:
1.确定列表里的第一个元素的价格是列表里面最大的
拿列表里的第一个元素和它后边的元素比较一下
如果发现比第一个元素大的就要交换元素的位置
那么我们如何交换位置嘞?
list1 = [1, 2, 3, 4, 5]
list1[1], list1[0] = list1[0], list1[1]
#运行结果:[2, 1, 3, 4, 5]
#回顾一点点知识:给列表里的值重新赋值给变量
a, b, c, d, e = list1
print(a)
#运行结果:2
2.确定列表里的第二个元素的价格是列表里面第二大的
拿列表里的第二个元素和它后边的元素比较一下
如果发现比第二个元素大的就要交换元素的位置
3.确定列表里的第三个元素的价格是列表里面第三大的
拿列表里的第三个元素和它后边的元素比较一下
如果发现比第三个元素大的就要交换元素的位置
4.确定列表里的第四个元素的价格是列表里面第四大的
拿列表里的第四个元素和它后边的元素比较一下
如果发现比第四个元素大的就要交换元素的位置
(第五个就不用啦,因为我们在第四步的时候就已经将整体的排序都做好了)
实现的步骤:
1.取出前几个数据(不要最后一个)
2.拿到数据与后边的元素作比较
3.发现更大的交换位置
commodity_infos = [
{"cid": 101, "name": "苹果", "price": 5},
{"cid": 102, "name": "葡萄", "price": 8},
{"cid": 103, "name": "橘子", "price": 3},
{"cid": 104, "name": "猕猴桃", "price": 6},
{"cid": 105, "name": "柚子", "price": 2}
]
#1.取出前几个数据(不要最后一个)
for r in range(len(commodity_infos) - 1): # r表示第r个数据
for c in range(r + 1, len(commodity_infos)):# c表示r后边的数据
#2.拿到数据与后边的元素作比较
if commodity_infos[r]["price"] < commodity_infos[c]["price"]:
#3.发现更大的交换位置
commodity_infos[r], commodity_infos[c] = commodity_infos[c], commodity_infos[r]
for commodity_info in commodity_infos:
print(commodity_info)
面向过程
总结之前写代码的套路,即分析出来写代码的步骤,然后使用代码逐步实现,如上诉的例子。这就是面向过程 (面向:即是以什么为核心)
面向过程就是把 一个复杂的过程流程化,进而简单化,我们要实现一个功能就要使用这种思维方式。
面向对象:对象再我们程序里边叫object,有物体的意思。
在使用面向对象写代码的时候,首先要想的就是这个程序有哪些部分组成,然后再去想每一步要怎么做。
在面向对象里面,我们想要把现实的事物在我们程序里面表示,需要将现实的事物抽象成我们代码里边的类,然后通过类产生程序对象,通过对象和我们现实生活中的事物对应。
面向对象与面向过程的关系
面向对象和面向过程都是我们写代码的方式,不能把他们对立起来,他们之间是相辅相成的。面向对象是从整体上分析构成,面向过程是注重怎样实现。
小tips❤️:
1.输出快捷方式
用法:变量名称.print + 按下enter键 即可立即变成 print(变量名称) 的形式
2.快速打出for循环的模板
用法:iter + 按下enter键 即可立即出现一个for循环的模板,如下
for in :
3.快速调整代码的的格式:
Ctrl
+Alt
+L
4.快速注释:
Ctrl
+/
5.property的快捷模板:
用法: props + Enter 即可出现模板
@property def (self): return @.setter
6.快速将参数传递的快捷键
Alt
+Enter
+Enter
,效果如下:class Commodity : def __init__(self,cid,name,price): self.price = price self.name = name self.cid = cid
类
就是我们生活当中类别的意思
语法:
class
类的名称
:
1.先定义类
使用class关键字去定义一个类
类名建议使用驼峰体
类与类之间主要是行为不同:人类和动物类有不同的行为
在类中写的函数,我们管它叫做方法,每个方法都会有一个self参数
#1.先定义类
class GirlFriend:
def send_msg(self): #女朋友会给男朋友发消息
print('给男朋友发消息')
def shopping(self): #女朋友会购物
print('买买买')
2.产生对象
类名
+ ()
就是实列话一次,实例化一次就是得到一个对象。实例化两次就得到两个对象。g1 = GirlFriend()
g2 = GirlFriend()
但是每个女朋友都有她独特的地方,我们这时候就会用到一个叫做__init__
的方法
当我们添加了__init__
方法的时候,首先会根据这个类创建一个对象,第二部接着去执行__init__
这个方法,python会自动将创建的对象当成参数传值给self(self就是对象的一个内存空间)\
举个:如下,创建对象时,因为有init,所以会将g1传递给self。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2PrrUPVP-1651233023580)(C:\Users\彭兴娇\AppData\Roaming\Typora\typora-user-images\1645447697005.png)]
class GirlFriend:
def __init__(self,name,face_score,money,boy_fiend):
#实列属性:属于对象的,只能通过对象调用
self.name = name
self.face_score = face_score
self.money = money
self.boy_fiend = boy_fiend
#绑定到对象的方法:可以使用类或者对象调用
#如果我们使用类来调用的话,这个方法就是一个普通的函数
#如果是对象来调用的话,就会把对象当成第一个参数传递
def send_msg(self): #女朋友会给男朋友发消息
print('给男朋友发消息')
def shopping(self): #女朋友会购物
print('买买买')
g1 = GirlFriend("小红",95,88888,"小蓝")
g2 = GirlFriend()
#如果我们想要输出女朋友的名字或者颜值,我们可以像这样子输出:
print(g1.name)
print(g1.face_score)
#如果我们是通过类来调用
print(GirlFriend.send_msg) #这样字就只是一个普通的函数
#如果我们想要调用的话,就必须传入参数
GirlFriend.send_msg
我们在给对象传值得时候会自己传到参数里边去。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MKER7uw-1651233023582)(C:\Users\彭兴娇\AppData\Roaming\Typora\typora-user-images\1644586886286.png)]
好处:把数据对象封装到一个类里边去,然后在方法里边对已经封装的数据进行操作,这样可以将数据对数据的操作相关联,让代码的可读性更高,让我们分析问题的时候更方便。
#如果我们将每一个商品做成一个类
class Commodity :
def __init__(self,cid,name,price):
self.price = price
self.name = name
self.cid = cid
#定义一个功能,用于输出商品的信息
def print_commodity_info(self):
print(f"编号:{self.cid}--商品名称:{self.name}--商品价格:{self.price}")
#如何去产生一个真实的商品呢?我们使用类名往里边传入参数,就产生了一个商品
commodity_info = [
#商品对象
Commodity(1001,"苹果",5)
]
将多个数据打包、封装到一个自定义的类里面,对类外提供必要的功能隐藏实现的细节。
好处:
将多个数据打包、封装到一个自定义的类里面
符合人的思维方式
可以将数据和对数据的操作封装在一起,有利于我们分析解决问题
以后我们拿到一个需求,我们应该将这个需求分解成多个类,类和类之间是行为上的不同,对象和对象之间是数据上的不同。
例如:五子棋可以分为:棋子,棋盘,规则三个类
对类外提供必要的功能隐藏实现的细节,无关紧要的不对外提供
我写了一个类在我们使用的时候,不需要考虑每个功能是怎么实现的
class Atm:
#以下这五个功能,我们在使用的时候不用考虑细节,是怎么来的
def card(self):
print("插卡")
def auth(self):
print("用户认证")
def input(self):
print("输入取款的金额")
def print_bill(self):
print("打印账单")
def take_money(self):
print("取款")
#我们只需要给出一个功能来触发上述五个功能即可
def draw_money(self):
self.card
self.auth
self.input
self.print_bill
self.take_money
a = Atm
a.draw_money()
#运行结果:
#插卡
#用户认证
#输入取款金额
#打印账单
#取款
a.card()#在这一步我们发现card等功能还是暴露出来了,给用户看到了,我们需要将这些隐藏起来不给用户看到
python中我们实现隐藏是通过私有化来做的,在命名的时候使用双下划线开头,就代表我们要把这个功能藏起来
#如何实现隐藏
class A:
def __init__(self,name):
self.__name = name #在前边使用双下划线,代表把name隐藏了,私有化了
def __foo(self):#方法也是可以隐藏的
print('foo')
a = A("孜孜淑淑")
print(a.name) #此时是访问不到name的,因为name已经被隐藏了,执行会报错
print(A.__dict__)#通过这种方式,我们就可以知道类里边有哪些东西,隐藏的也能看到
#如何访问隐藏的方法
A._A__fool(1)#通过该方法就可以访问隐藏的方法或者变量
#特点:
'''
1.在类外我们不是直接通过 对象.属性/方法 访问
2.在类的内部是可以直接使用的
'''
隐藏的使用场景:
实列属性:我们把放在____init____里边的叫做实列属性,也就是每个对象属性
使用场景:
class GirlFriend:
def __init__(self,name,age,weight):
self.weight = weight
self.__age = age
self.__name = name
self.set_age(age)
def get_age(self):
return self.__age
def set_age(self,value): #限定女朋友的年龄
if 21 <= value <=30:
set.__age = value
else:
raise ValueError("我不要") #如果没有达到年龄的要求,就直接报错。这样可以保证数据的安全,比如空调的温度
#实例化就是通过调用一次类名,就进行实例化一次
g1 = GirlFriend("小红",18,100)
公开的实例属性,缺少逻辑验证。私有实例和两个公开的方法结合使用。在这里我们介绍一个类property:
'''
使用了property(放在类里边的)之后,我们在通过 对象.age 就会触发到 get_age的执行
当我们使用 对象.age = 赋值 的时候就会出发到 set.age
property对象可以当作是拦截了我们对age的读写操作,优先级更高
'''
#property使用方法一:
class GirlFriend:
def __init__(self,name,age,weight):
self.weight = weight
self.__age = age
self.__name = name
self.set_age(age)
def get_age(self):
return self.__age
def set_age(self,value): #限定女朋友的年龄
if 21 <= value <=30:
set.__age = value
else:
raise ValueError("我不要")
age = property(get_age,set_age) #会使优先级更高,当涉及到age时,会先找到get_age,set_age这两个方法
#property使用方法二:
class GirlFriend:
def __init__(self,name,age,weight):
self.weight = weight
self.__age = age
self.__name = name
self.set_age(age)
@property #相当于执行了 age = property(age) 创建了一个property对象 只负责拦截读取操作
def age(self):
return self.__age
@age.setter #拦截我们写入的操作
def age(self,value): #限定女朋友的年龄
if 21 <= value <=30:
set.__age = value
else:
raise ValueError("我不要")
'''
使用该方法的快捷方式:`props`+`Enter` 就会自动创建一个该模板
@property
def (self):
return
@.setter
'''
使用面向对象思想实现以下两个要求:
玩家可以攻击敌人,敌人受伤血量减少的同时播放敌人受伤的画面
敌人也可以攻击玩家,玩家血量减少的同时出现碎屏效果
#玩家类
class Player:
def __init__(self,hp,atk): #每一个玩家都会有血量和攻击力
self.atk = atk
self.hp = hp
def attack(self,enemy):
print('玩家攻击敌人')
enemy.enemy_wounded(self.atk)
def player_injured(self,value):
print('碎屏 啊啊啊')
self.hp -= value # 被攻击血量应该减少
print(f'现在玩家的血量是{self.hp}')
#敌人类
class Enemy:
def __init__(self,hp,atk):
self.atk = atk
self.hp = hp
def enemy_wounded(self,value): #value是减少的血量值
print('播放受伤的画面')
self.hp -= value #被攻击血量应该减少
print(f'现在敌人的血量是{self.hp}')
def attack(self,player):
print('敌人攻击玩家')
player.player_injured(self.atk)
p1 = Player(500, 50) #创建一个玩家
e1 = Enemy(100, 10) #创建一个敌人
p1.attack(e1) #玩家攻击敌人
print('-'*20)
e1.attack(p1)
#运行结果:
'''
玩家攻击敌人
播放受伤的画面
现在敌人的血量是50
--------------------
敌人攻击玩家
碎屏 啊啊啊
现在玩家的血量是490
'''
在该案例当中添加“手雷”,手雷爆炸可以炸玩家也可以炸敌人
#手雷
class Grenade:
def __init__(self,atk): # atk攻击力
self.atk = atk
def explode(self, target): #爆炸,target攻击目标
print("拉环")
print("手雷爆炸")
'''
if type(target) == Enemy:#如果攻击对象是敌人
target.enem_wounded(self.atk) #敌人受伤
elif type(target) == Player: #如果攻击的对象是玩家
target.player_injured(self.atk) #玩家受伤
'''
#改良之后,我们就不用判断它是那种属性的人了,直接调用即可
target.damage(self.atk)
#分析:不管是我们的玩家还是敌人,总之有个共同属性,便是可攻击对象,在这里我们就可以创建以个攻击对象的父类
class AttackTarget: #父类
def damage(self,value): #定义一个方法,让他们被攻击
pass
#玩家类
class Player(AttackTarget):
def __init__(self,hp,atk): #每一个玩家都会有血量和攻击力
self.atk = atk
self.hp = hp
def attack(self,enemy):
print('玩家攻击敌人')
enemy.enemy_wounded(self.atk)
def player_injured(self,value):
print('碎屏 啊啊啊')
self.hp -= value # 被攻击血量应该减少
print(f'现在玩家的血量是{self.hp}')
#敌人类
class Enemy(AttackTarget):
def __init__(self,hp,atk):
self.atk = atk
self.hp = hp
def enemy_wounded(self,value): #value是减少的血量值
print('播放受伤的画面')
self.hp -= value #被攻击血量应该减少
print(f'现在敌人的血量是{self.hp}')
def attack(self,player):
print('敌人攻击玩家')
player.player_injured(self.atk)
类属性
对象中有多个相同的数据,这个时候我们可以选择吧这个给数据做成类变量
举个例子:
class Person:#定义一个人的类 country = "中国" #所有的人都是中国人,这个是相同的数据 def __init__(self, name, age): self.name = name self.age = age p1 = Person("小红",18) p2 = Person("小蓝",22) print(p1.name) print(p2.name) #country 是他们的共同属性,都可以用 print(p1.country) print(p2.country)
(1)继承:重用类的功能和概念
‘你要笑死我,然后继承我的花呗’ 父子的关系才会有继承
比如皇位,江山不需要太子打,但太子可以登基
在编程里,父类代码不用子类写,但是子类可以用父类的代码
# **********继承的写法***************
# 父类:动物
class Animal:
def eat(self):
print("吃")
# 子类:狗的父类是动物
class Dog:
def eat(self):
print("吃")
def run(self):
print("跑")
# 子类:鸟的父类是动物
class Bird:
def eat(self):
print("吃")
def fly(self):
print("飞")
# 狗和鸟有共性,我们可以将他们的共性写在父类,这样鸟和狗都可以使用
d1 = Dog()
# 对象本身找,没有的话然后到类里边找,最后到父类里边找
d1.eat = 123
print(d1.eat)
小练习:
以下代码运行结果是什么?
class Foo:
def f1(self):
print("from Foo.f1")
def f2(self):
print("from Foo.f2")
self.f1()
class Bar(Foo):
def f1(self):
print("from Bar.f1")
b = Bar()
b.f2()
#运行结果:
'''
from Foo.f2
from Bar.f1
'''
'''
原因:
首先,b是一个没有实列属性的对象,b这个对象所在的类下边是没有f2这个属性,没有的话就去找父类Foo,那就打印出来from Foo.f2
然后接下来是self.f1,在这里的这个self会在自己(b)的类里边找,就是Bar里边的f1
'''
子类里边什么都没有的话,那这个子类里面的所有东西都是继承父类的
在子类里面定义了自己的,就以自己的为准
问题点是我们在子类里面派生出来的方法里面我想用父类的代码,不是说重新写一遍,我是想在父类的基础上做一点小改动,这个时候我们就需要在子类里边调用父类的方法。
#**********在子类里边调用父类的方法*************
class Animal: #父类
def __init__(self, name, sex, age):
self.age = age
self.sex = sex
self.name = name
def eat(self):
print("吃")
class Dog(Animal): #子类
def __init__(self, name, sex, age, title):
self.title = title
self.age = age
self.sex = sex
self.name = name
d1 = Dog("旺财", "公", 3, "拉布拉多") #在这里,我们最先促发到的还是Dog里边的__init__
print(d1.__dict__)
#方式一:指名道姓
class Dog(Animal): #子类
def __init__(self, name, sex, age, title):
Aniaml.__init__() #执行到此处默认调用父类的__init__
self.title = title
#方法二:super()方法
'''
使用super()会得到一个特殊的对象,这个对象专门用来调用父类的方法
'''
class Dog(Animal): #子类
def __init__(self, name, sex, age, title):
super().__init__(name, sex, age) #执行到此处调用父类的__init__
self.title = title
(2)多态:父类的同一中行为在不同的子类上有不同的实现,可以增强我们程序的可扩展性。
增加新的东西不会影响到我们的类
#子类
def init(self, name, sex, age, title):
self.title = title
self.age = age
self.sex = sex
self.name = name
d1 = Dog(“旺财”, “公”, 3, “拉布拉多”) #在这里,我们最先促发到的还是Dog里边的__init__
print(d1.dict)
#方式一:指名道姓
class Dog(Animal): #子类
def init(self, name, sex, age, title):
Aniaml.init() #执行到此处默认调用父类的__init__
self.title = title
#方法二:super()方法
‘’’
使用super()会得到一个特殊的对象,这个对象专门用来调用父类的方法
‘’’
class Dog(Animal): #子类
def init(self, name, sex, age, title):
super().init(name, sex, age) #执行到此处调用父类的__init__
self.title = title
(2)多态:父类的同一中行为在不同的子类上有不同的实现,可以增强我们程序的可扩展性。
增加新的东西不会影响到我们的类