DAY-06课堂笔记
面向对象编程介绍
面向对象和面向过程的基本概念
⾯向对象(oop) 是⼀种编程⽅法, 编程思想(即指导如何写代码), 适⽤于 中⼤型项⽬
⾯向过程也是⼀种编程思想, 适⽤于⼩型项⽬
⾯向过程 和 ⾯向对象 都可以实现某个编程的⽬的.
⾯向过程 考虑的是 实现的细节
⾯向对象 考虑的是 结果(谁能做这件事)
面向对象的核心内容 类 和 对象
类和对象的概念
- 类:是对具有相同特征或者行为的事物的一个统称, 是抽象的,不能直接使用
- 指代多个事物---很多事物的统称
- 代码中 类 是由关键字
class
定义的
- 对象:是由类创建出来的一个具体存在的事物, 可以 直接使用
- 指代一个事物
- 代码中 使用类去创建(实例化)
苹果 ----> 类
红苹果 ----> 类
⼩明⼿⾥的那个红苹果 ----> 对象
狗 ----> 类
⼤⻩狗 -----> 类
李四家的那条 ⼤⻩狗 -----> 对象
类 :就是模具。拥有一定的整体特征。
对象:符合模具的要求,有具体的实现。
当我们把一大堆拥有共同特征的对象的静态特征(属性)和动态特征(行为)都抽离出来后,就可以定义出一个叫做“类”的东西。
类的构成
想要设计一个类, 通常需要满足以下三个要素:
- 类名: 同类事物的名字, 命名要满足大驼峰命名法
- 属性: 同类事物具备的特征
- 方法: 同类事物具备的行为
⾯向对象代码的步骤
1, 设计类 (找类的三要素)
2, 定义类
3, 创建对象(实例化对象)
4, 由对象调⽤类中的⽅法
类的设计
类的设计 就是找三要素. 属性和⽅法 可能会有很多,我们只需要
找到关注的即可
类名的提取:使⽤名词提炼法 分析整个业务流程, 得出的名词,
通 常就是类名
⚠:对于需求中没有提到的属性和方法, 在设计类的时候不要添加进来!
类的设计-案例演练1
需求:
• 小明 今年 18 岁,身高 1.75,每天早上 跑 完步,会去 吃 东西
• 小美 今年 17 岁,身高 1.65,小美不跑步,小美喜欢 吃 东西
类名 :Person
属性 :name , age ,height
方法 :run , eat
类的设计-练习1
需求:
• 一只 黄颜色 的 狗狗 叫 大黄
• 看见生人 汪汪叫
• 看见家人 摇尾巴
类名 :Dog
属性 :color , name
方法 :汪汪叫(bark) , 摇尾巴(shake)
类的设计-案例演练2
需求:
• 进入某 Web 项目登录页面,输入用户名、密码、验证码之后,点击登录按钮可以登录系统
类名 :LoginPage
属性 :username , paasword ,code ,登录按钮
方法 :登录方法
类的设计-练习2
需求:
进入某APP项目的下订单页面,输入用户收货地址后,点击提交订单按钮,可以实现下单操作
类名 :Page
属性 :用户收货地址 , 提交订单按钮
方法 :下单方法
面向对象基本语法
定义类
在Python 中定义类使⽤关键字 class
# 类名: 遵循大驼峰命名法
class 类名:
# 方法: 定义方式与函数基本相同, 区别是方法的第一个参数必须为 self
def 方法1(self, 参数列表):
pass
def 方法2(self, 参数列表):
pass
创建对象
在代码中, 对象是由类对象.
类名() # 就是创建对象
# ⼀般使⽤变量将创建的对象保存起来
变量 = 类名() # ⼀般将这个变量 称为是对象, 本质-->变量中保存的是对象的引⽤地址
案例一
需求:小猫 爱 吃鱼,小猫 要 喝水
类名 :Cat
属性 :/
方法 :吃鱼eat ; 喝水drink
class Cat:
def eat(self):
"""吃⻥的⽅法"""
print("吃鱼")
def drink(self):
"""喝⽔的⽅法"""
print("喝水")
tom = Cat()
tom.drink()
tom.eat()
self 参数
1, 参函数的语法上来看, self 是形参, 名字可以任意的变量
名, 只是我们习惯性叫 self
2, 特殊点: self 是⼀个普通的参数, 按照函数的语法,在调⽤的时候,必须传递实参值, 原因, 是 Python 解释器⾃动的将 调⽤这个⽅法的对象作为参数传递给 self 所以 self 就是调⽤这个⽅法对象
class Cat:
def eat(self): # self 是调⽤这个⽅法的对象
"""吃⻥的⽅法"""
print(f'self:{id(self)}')
print('⼩猫爱吃⻥...')
# 创建对象
tom = Cat()
# 通过对象 调⽤类中的⽅法
print(f"tom :{id(tom)}")
tom.eat() # tom 调⽤ ,self 就是 Tom
blue_cat = Cat()
print(f'blue:{id(blue_cat)}')
blue_cat.eat() # blue_cat 调⽤, self 就是 blue_cat
属性
属性表示事物的特征.
可以给对象添加属性 或者获取对象的属性值.
给对象添加属性:
对象.属性名 = 属性值 # 添加或者修改
获取对象的属性值:
对象.属性名
在⽅法中操作属性(self 是对象):
self.属性名 = 属性值
self.属性名
class Cat:
def eat(self): # self 是调⽤这个⽅法的对象
"""吃⻥的⽅法"""
print(f'self:{id(self)}')
print(f'⼩猫{self.name}爱吃⻥...')
# 创建对象
tom = Cat()
tom.name = '汤姆' #------->在类的外面定义属性
print(tom.name)
tom.eat()
blue_cat = Cat()
print(f'blue:{id(blue_cat)}')
blue_cat.name = '蓝猫'
blue_cat.eat()
练习:
Cat 添加属性 姓名, 年龄
eat ⽅法中打印输出 ⼩猫 xx, xx 岁 爱吃⻥
class Cat:
def eat(self): # self 是调⽤这个⽅法的对象
"""吃⻥的⽅法"""
print(f'self:{id(self)}')
print(f'⼩猫{self.name},{self.age}岁 爱吃⻥...')
tom=Cat()
tom.name="xx"
tom.age="xx"
tom.eat()
在类的内部定义属性👇
魔法方法:
在 python 中存在一类方法,以两个下划线开头,两个下划线结尾,在满足某个条件的情况下,会自动调用,这一类方法 称为魔法方法
学习:
1:什么情况下会自动调用(自动调用的时机)
2:应用场景
3:注意事项
初始化方法:__init__
1, 调⽤时机
在创建对象之后,会⾃动调⽤.
2, 应⽤场景
初始化对象, 给对象添加属性
3, 注意事项
- 不要写错
- 如果 属性是会变化的, 则可以将这个属性的值作为参数传递, 在创建对象的时候,必须传递实参值
class Cat:
def __init__(self, name, age):
'''添加属性'''
self.name = name
self.age = age
def eat(self):
"""吃⻥的⽅法"""
print(f'self:{id(self)}')
print(f'⼩猫{self.name},{self.age}岁 爱吃⻥...')
tom=Cat("tom","18")
tom.eat() #⼩猫tom,18岁 爱吃⻥...
# init ⽅法 创建对象之后 会⾃动调⽤
# 1 会 2 不会
# Cat # 2 不是创建对象
# Cat() # 1 因为是创建对象
# tom = Cat # 2 不是创建对象, 即 tom 也是类
#
# blue = Cat() # 1 创建对象
# b = blue # 2 不是创建对象, 只是引⽤的传递
#
# t = tom() # 1, tom 已经是类, 类名() 就是创建对象
__str__
方法
1:调⽤时机
使⽤ print(对象) 打印对象的时候, 会⾃动调⽤
1, 如果没有定义 __str__ ⽅法, 默认打印的是 对象的引⽤地址
2, 如果定义 __str__ ⽅法,打印的是 ⽅法的返回值
2:应⽤场景
使⽤ print(对象) 打印输出对象的属性信息
3:注意事项
必须返回⼀个 字符串
class Cat:
def __init__(self, name, age):
'''添加属性'''
self.name = name
self.age = age
def __str__(self): # ⼀般不使⽤ print,直接返回
return f"姓名: {self.name}, 年龄: {self.age}"
tom=Cat("tom","18")
print(tom)
dir()函数
dir(对象变量)
作用:可以查看对象内的 所有属性及方法
注意:dir()函数不具备输出功能, 需要和print()函数配合使用
面向对象的三大特征
封装
封装: 根据要求将属性和⽅法 定义到类中(定义类)
封装案例一:小明爱跑步
需求:
• 小明 体重 75.0 公斤
• 小明每次 跑步 会减肥 0.5 公斤
• 小明每次 吃东西 体重增加 1 公斤
class Person:
def __init__(self,name,weight):
self.name=name
self.weight=weight
def __str__(self):
return f"{self.name}现在的体重为{self.weight}kg"
def run(self):
self.weight=self.weight-0.5
print(f"{self.name}跑步后体重为{self.weight}kg")
def eat(self):
self.weight=self.weight+1
print(f"{self.name}吃东西后体重为{self.weight}kg")
xm=Person("小明",85)
print(xm) #小明现在的体重为85kg
xm.run() #小明跑步后体重为84.5kg
xm.eat() #小明吃东西后体重为85.5kg
同一个类 创建的 多个对象 之间,属性 互不干扰!
if __name__ == '__main__':
xm=Person("小明",85)
print(xm)
xm.run()
xm.eat()
xiaomei = Person("小美", 45)
xiaomei.run()
xiaomei.run()
xiaomei.eat()
print(xiaomei)
封装案例二:登录
需求:
• 进入某 Web 项目登录页面,输入用户名、密码、验证码之后登录系统
class Login:
def __init__(self, username, password, code):
self.username = username
self.password = password
self.code = code
if code == "8888":
if username == "张三" and password == "123456":
print("登录成功!")
else:
print("账号或密码错误!")
else:
print("验证码错误!")
if __name__ == '__main__':
zq = Login("张三", "123456", "8888")
mm = Login("张三", "123476", "8888")
yzm = Login("张三", "123456", "8877")
yhm = Login("王五", "123456", "8888")
class LoginPage:
def __init__(self, username, password, code):
self.username = username
self.password = password
self.code = code
def Login(self):
print(f"1.输入用户名:{self.username}")
print(f"2.输入密码:{self.password}")
print(f"3.输入验证码:{self.code}")
print(f"4.点击登录")
if self.code == "8888":
if self.username == "张三" and self.password == "123456":
print("登录成功!")
else:
print("账号或密码错误!")
else:
print("验证码错误!")
if __name__ == '__main__':
admin = LoginPage("张三", "123456", "8888")
admin.Login()
hm = LoginPage("XX", "123456", "8888")
hm.Login()
mm = LoginPage("张三", "XXXXX", "8888")
mm.Login()
yzm = LoginPage("张三", "123456", "XXXX")
yzm.Login()
封装案例三:摆放家具
需求:
1. 房子(House) 有 户型、总面积 和 家具名称列表
– 新房子没有任何的家具
2. 家具(HouseItem) 有 名字 和 占地面积,其中
– 席梦思(bed) 占地 4 平米
– 衣柜(chest) 占地 2 平米
– 餐桌(table) 占地 1.5 平米
3. 将以上三件 家具 添加 到 房子 中
4. 打印房子时,要求输出:户型、总面积、剩余面积、家具名称列表
class HouseItem:
def __init__(self, name, area):
self.name = name
self.area = area
def __str__(self):
return f"{self.name} 占地 {self.area}平方"
if __name__ == '__main__':
bed = HouseItem('席梦思', 4)
chest = HouseItem('⾐柜', 2)
table = HouseItem('餐桌', 1.5)
print(bed)
print(chest)
print(table)
class House:
def __init__(self, house_type, area):
self.house_type = house_type
self.area = area
self.free_area=area
self.item_name_list=[]
def __str__(self):
return f"户型:{self.house_type}、面积:{self.area}、剩余面积{self.free_area}、家具名称列表{self.item_name_list}"
def add_item(self,item):
if self.free_area > item.area:
print(f'添加家具: {item.name}')
self.item_name_list.append(item.name)
# 修改剩余⾯积
self.free_area -= item.area
else:
print(f"房⼦剩余⾯积不⾜,换个⼤房⼦ 吧.....")
# 创建房⼦
house = House('三室⼀厅', 100)
print(house)
house.add_item(bed)
print(house)
house.add_item(chest)
print(house)
house.add_item(table)
print(house)
继承
1, 继承描述的是类与类之间的关系 is ... a
2, 继承的好处: 减少代码冗余,重复代码不需要多次书写, 提高编程效率
语法:
# class 类A(object):
# class 类A():
class 类A: # 默认继承 object 类, object 类 Python 中最原始的类
pass
class 类B(类A): # 就是继承, 类 B 继承 类 A
pass
# 类 A: 父类 或 基类
# 类 B: 子类 或 派生类
子类继承父类之后, 子类对象可以直接使用父类中的属性和方法
继承的传递性
说明:
• C 类从 B 类继承, B 类又从 A 类继承
• 那么 C 类就拥有 B 类和 A 类的所有属性和方法
结论: 子类拥有父类以及父类的父类中封装的所有属性和方法
对象调用方法的顺序: 对象.方法名()
1, 会现在自己的类中查找, 找到直接使用
2, 没有找到 去父类中查找, 找到直接使用
3, 没有找到, 在父类的父类中查找, 找到直接使用
4, 没有找到, ...
5, 直到 object 类, 找到直接使用, 没有找到,报错
继承案例
需求:
1. 定义动物类,动物有姓名和年龄属性,具有吃和睡的行为
2. 定义猫类,猫类具有动物类的所有属性和方法,并且具有抓老鼠的特殊行为
3. 定义狗类,狗类具有动物类的所有属性和方法,并且具有看门的特殊行为
4. 定义哮天犬类,哮天犬类具有狗类的所有属性和方法,并且具有飞的特殊行为
class Animal:
def __init__(self, name, age):
self.name = name
self.age = age
def eat(self):
print(f"{self.name}吃吃吃")
def sleep(self):
print(f"{self.name}睡睡睡")
class Cat(Animal):
def catch(self):
print(f"{self.name}抓老鼠")
class Dog(Animal):
def door(self):
print(f"{self.name}看门")
class xtq(Dog):
def fly(self):
print(f"{self.name}起飞")
if __name__ == '__main__':
tom = Cat("汤姆", 2)
tom.eat()
tom.sleep()
tom.catch()
dh = Dog("大黄", 3)
dh.eat()
dh.sleep()
dh.door()
xtq = xtq("神犬", 100)
xtq.eat()
xtq.sleep()
xtq.door()
xtq.fly()
方法的重写(override)
1, 什么是重写?
重写是在子类中定义了和父类中名字一样的方法.
2, 重写的原因? 为什么重写?
父类中的代码不能满足子类对象的需要
3, 重写的方式
3.1 覆盖式重写
3.2 扩展式重写
覆盖式重写
父类中的功能全部不要.
直接在子类中定义和父类中方法名字一样的方法接口, 直接书写新的代码.
class Dog:
def bark(self):
print('汪汪汪叫......')
class XTQ(Dog): # 需要哮天犬 嗷嗷嗷叫, 父类中的 bark 方法,不能满足子类对象的需要, 覆盖式重写
def bark(self):
print('嗷嗷嗷叫.....')
pass
if __name__ == '__main__':
xtq = XTQ()
xtq.bark()
扩展式重写
父类中的功能还需要,只是添加了新的功能
方法:
1. 先在子类中定义和父类中名字相同的方法
2. 在子类的代码中 使用 super().方法名() 调用父类中的功能
3. 书写新的功能
class Dog:
def bark(self):
print('汪汪汪叫......')
class XTQ(Dog): # 需要哮天犬 嗷嗷嗷叫, 父类中的 bark 方法,不能满足子类对象的需要, 覆盖式重写
def bark(self):
super().bark()
print('嗷嗷嗷叫.....')
pass
if __name__ == '__main__':
xtq = XTQ()
xtq.bark()
多态
多态: 调用代码的技巧
---就是通过定义方法,重写方法,造成多个状态,引用不同状态得到不同结果
多态:不同的子类对象调用相同的父类方法,产生不同的执行结果
多态案例:
需求:
1. 在 Dog 类中封装方法 game
– 普通狗只是简单的玩耍
2. 定义 XiaoTianDog 继承自 Dog,并且重写 game 方法
– 哮天犬需要在天上玩耍
3. 定义 Person 类,并且封装一个 和狗玩 的方法
– 在方法内部,直接让 狗对象 调用 game 方法
class Dog:
def game(self):
print('普通狗简单的玩耍...')
class XTQ(Dog):
def game(self):
print('哮天犬在天上玩耍...')
class Person:
def play_with_dog(self, dog):
"""dog 是狗类或者其子类的对象"""
print('人和狗在玩耍...', end='')
dog.game()
if __name__ == '__main__':
dog1 = Dog()
xtq = XTQ()
xw = Person()
xw.play_with_dog(dog1)
xw.play_with_dog(xtq)
DAY-07课堂笔记
面向对象其他语法
私有和公有
在Python 中,定义类的时候, 可以给 属性和方法设置 访问权限, 即规定在什么地方可以使用.
权限一般分为两种: 公有权限 私有权限
公有权限
- 定义
直接定义的属性和方法就是公有的
- 特点
可以在任何地方访问和使用,只要有对象就可以访问和使用
私有权限
- 定义
1,只能在类内部定义(class关键字的缩进中)
2,只需要在属性名 或者方法名掐灭加两个下划线,这个方法或者属性就变为私有的
- 特点
私有 只能在当前类的内部使用. 不能在类外部和子类直接使用
- 应用场景
一般来说,定义的属性和方法 都为公有的.
某个属性 不想在外部直接使用, 定义为私有
某个方法,是内部的方法(不想在外部使用), 定义为私有
"""定义人类, name 属性 age 属性(私有)"""
class Person:
def __init__(self, name, age):
self.name = name # 公有
self.__age = age # 公有-->私有, 在属性名前加上两个下划线
def __str__(self): # 公有方法
return f"{self.name}, {self.__age}"
def set_age(self, age): # 定义公有方法,修改私有属性
if age < 0 or age > 120:
print('提供的年龄信息不对')
return
self.__age = age
if __name__ == '__main__':
xw = Person('小王', 18)
print(xw)
xw.__age = 10000 # 添加一个公有属性 __age
print(xw)
xw.set_age(10000)
print(xw)
对象 属性 方法
对象分类
python 中一切皆对象
类对象
类对象 就是 类, 就是使用 class 定义的类
在代码执行的时候, 解释期会自动的创建.
作用:
1, 使用类对象创建 实例对象
2, 存储一些类的特征值(类属性)
实例对象
1, 创建对象也称为实例化, 所以 由类对象(类) 创建的对象 称为是 实例对象, 简称实例
2, 一般来说,没有特殊强调,我们所说的对象 都是指 实例对象(实例)
3, 实例对象 可以保存实例的特征值 (实例属性)
4, 就是使用 类名() 创建的对象
属性的划分
使用 实例对象.属性 访问 属性的时候, 会先在 实例属性中查找,如果找不到,去类属性中查找, 找到就使用, 找不到,就报错
即: 每个实例对象 都有可能访问类属性值(前提,实例属性和类属性不重名)
实例属性
-
概念
是每个实例对象 具有的特征(属性), 每个实例对象的特征
-
定义
一般都是在 init 方法中,使用 self.属性名 = 属性值 来定义
-
特征(内存)
每个实例对象 都会保存自己的 实例属性, 即内存中存在多份
-
访问和修改
# 可以认为是通过 self 实例对象.属性 = 属性值 # 修改 实例对象.属性 # 访问
类属性
-
概念
是类对象具有的 特征, 是整个类的特征
-
定义
一般 在类的内部(class 缩进中), 方法的外部(def 的缩进外部) 定义的变量
-
特征(内存)
只有类对象保存一份, 即在内存中只有一个
-
访问和修改
# 即通过类名 类对象.属性 = 属性值 类对象.属性
什么时候 定义类属性?
代码中 使用的属性 基本上 都是 实例属性,即都通过 self 定义.
当 某个属性值描述的信息是整个类的特征(这个值变动,所有的这个类的对象这个特征都会发生变化)
案例
1. 定义一个 工具类
2. 每件工具都有自己的 name
3. 需求 —— 知道使用这个类,创建了多少个工具对象?
类名: Tool
属性: name(实例属性) count(类属性)
方法: init 方法
class Tool:
# 定义类属性 count,记录创建对象的个数
count = 0
def __init__(self, name):
self.name = name # 实例属性, 工具的名字
# 修改类属性的值
Tool.count += 1
if __name__ == '__main__':
# 查看 创建对象的个数
print(Tool.count) # 查看类属性
tool1 = Tool('锤子')
print(Tool.count)
tool2 = Tool('扳手')
print(Tool.count)
print(tool2.count) # 先找实例属性 count, 找不到, 找类属性 count, 找到,使用
方法的划分
实例方法
-
定义时机
如果方法中 需要使用 实例属性, 则这个方法 **必须** 定义为实例方法
-
定义
# 直接定义的方法就是实例方法 class 类名: def 方法名(self): pass
-
参数
参数一般写作 self, 表示的是 实例对象
-
调用
实例对象.方法名()
类方法
-
定义时机
如果 方法中 不需要使用 实例属性, 但需要使用 类属性, 则这个方法 **可以** 定义为 类方法(建议)
-
定义
# 定义类方法,需要在方法名上方 书写 @classmethod , 即使用 @classmethod 装饰器装饰 class 类名: @classmethod def 方法名(cls): pass
-
参数
参数 一般写作 cls, 表示类对象, 即 类名, 同样不需要手动传递,Python 解释器会自动传递
-
调用
# 方法一 类名.方法名() # 方法二 实例对象.方法名()
静态方法
-
定义时机
方法中即不需要使用 实例属性, 也不需要使用 类属性, **可以** 将这个方法定义为 静态方法
-
定义
# 定义静态方法, 需要使用 装饰器 @staticmethod 装饰方法 class 类名: @staticmethod def 方法名(): pass
-
参数
静态方法,对参数没有要求, 一般没有
-
调用
# 方法一 类名.方法名() # 方法二 实例对象.方法名()
案例
需求:
1. 设计一个 Game 类
2. 属性:
• 定义一个 top_score 类属性 -> 记录游戏的历史最高分
• 定义一个 player_name 实例属性 -> 记录当前游戏的玩家姓名
3. 方法:
• 静态方法 show_help() -> 显示游戏帮助信息
• 类方法 show_top_score() -> 显示历史最高分
• 实例方法 start_game() -> 开始当前玩家的游戏
- ① 使用随机数 生成 10-100 之间数字 作为本次游戏的得分
- ② 打印本次游戏等分 : 玩家 xxx 本次游戏得分 ooo
- ② 和历史最高分进行比较, 如果比历史最高分高, 修改历史最高分
4. 主程序步骤: main
1 查看帮助信息
2 查看历史最高分
3 创建游戏对象,开始游戏
4 再一次游戏
import random
class Game:
top_score = 0
def __init__(self, player_name):
self.player_name = player_name
@staticmethod
def show_help():
print("帮助你一下")
@classmethod
def show_top_score(cls):
print(f"最高分{Game.top_score}")
def start_game(self):
print("开始游戏")
max2 = random.randint(10, 100)
print(f"{self.player_name}本场得分{max2}")
if max2 > Game.top_score:
Game.top_score = max2
if __name__ == '__main__':
Game.show_help()
Game.show_top_score()
xm = Game("小明")
xm.start_game()
Game.show_top_score()
xh = Game("小h")
xh.start_game()
Game.show_top_score()
上面代码每次执行,top_score 都会重新赋值为0。为解决数据一直保存,引入 文件
(下一篇笔记)