天数:第十三天
时间:2023年12月27日
内容:
- 面向对象
- 大背景:在很多编程语言中,面向对象都是很重要的部分。但是在python中却没有强调面向对象的用法和特性。主要是增长我们编程思维
- 搞清楚:
- 对象(通俗说就是东西):万事万物皆对象 (问题:世界上有什么? 有名胜古迹,人,动物,车子.........这些东西都是对象,都能用面向对象来表示 )
- 也可以是:我们编程中,遇到的东西都是对象,例如一个变量,一组数据/ 我想在脑袋里在想一个东西,那么这个东西就是对象/等等你想到的都是对象,都是东西
- 对象(通俗说就是东西):万事万物皆对象 (问题:世界上有什么? 有名胜古迹,人,动物,车子.........这些东西都是对象,都能用面向对象来表示 )
- 对象:用于描述客观事物的一个实体,由一组属性和方法组成
- 实体:是一个实实在在有的,不是一个概念的,是具体的
- 属性:对象的特征,并且对象的每个特征都有特定的值(例:张浩和小米,年龄,和姓名不一样)
- 方法:对象执行的操作(收银员张浩,执行的操作有收银,打印账单,刷卡等)(函数类似于方法)
- 类和对象 (使用属性和方法去描述对象)
- 类:对象的分类,是对对象的归纳和概念,并不是一个具体的实体。(汽车类,狗类等等,都是一个初步的分类过程)
- 定义类的过程:定义类名 ---> 编写类的属性 -----> 编写类的方法
- 编程思维:(情景:当我们拿到一个新项目的时候,如何了解这个业务以及构建这个业务)
- 1. 面向过程:之前所学的都是面向过程,将业务分成一个一个的过程和步骤
- 2. 面向对象:将业务分成一个一个的对象
- ***例子1:李四去上班:李四规定5点钟起床,6点吃早饭,7点坐公交车去公司。(这个规划是一个面向过程)。那么面向对象思维就是李四类(五点钟起床),早饭类(吃了啥),公交车类(哪一路公交车),等等
- ***例子2:图书管理系统(首先,每一本书是一个对象),里面必须有属性,方法。其中属性是编号,书名,作者,上架日期,状态。功能和方法是书本借出(把原来的可借状态,改为已借出状态),书本归还(把原来的已借状态,改为归还出状态)
- ***例子3:人力资源管理系统(首先,每一个人是一个对象),里面必须有属性,方法。其中属性是姓名,年龄,薪资,部门。功能和方法是调整部门,调整薪资
- 创建类:定义某个类的对象具有的属性(变量)和方法(函数)
- 语法: class 类名: (类名首字母大写)
- def __init__(self, nick, color, age): (每个类里面必须有一个这样的初始化方法)
- self.nick = nick (定义属性过程:昵称,颜色,年龄)
- self.color = color
- self.age = age
- self.strain = "加菲猫" (这个不用传入属性,每一次使用该类都会显示strain)
- def eat (self): (每个函数中都有固定参数self)
- print("猫在吃鱼!!")
- 创建对象(实例化):
- 语法:对象名 = 类名(属性值1,属性值2,属性值3)
- cat1 = Cat("小喵","白色", 2 )
- cat2 = Cat("小黑", "黑色", 1)
- 用法1:获取某个对象属性 对象名. 属性名
- print(cat1.nick) 结果:"小喵" "加菲猫"
- print(cat2.color) 结果:"黑色" "加菲猫"
- 用法2:通过某个对象来调用方法 对象. 方法名( )
- cat1. eat() 结果: "猫在吃鱼!!"
- ***tips: 如果直接print( cat1 ) 结果:<__ main __ . Cat object at 0 x 000001C53A60DD13> 这会输出你调用了Cat类(是一个自定义的),可对这个对象进行解析。后面跟的是内存地址
- self关键词和初始化方法
- self关键词:在类的内部表示所有当前对象。
- 比如在传入参数时,cat1.eat(3) 想让其表示为吃了3只鱼 那么在Cat类的,eat函数就要写为:eat(self ,count) 这里的self会隐式传递,不需要在传的时候再次加参数。
- 并且,根据self是表示所有当前对象,那么我把更改eat()函数为:
- def eat (self,count):
- print( self. nick , "在吃" ,count , "只鱼!!")
- 那么,对特定对象cat1进行编码时,就会指定cat1对象的nick和参数3进去: 结果是:“小喵在吃3条鱼!!”
- 初始化方法:在创建对象时被调用,用于构建对象,给对象的属性赋值
- 验证方法:在Cat类的 __init__()方法第一行输入 print(“初始值次数验证”),并且此时,我创建了3个对象, cat1 = Cat("小喵","白色", 2 ), cat1 = Cat("小喵","白色", 2 ), cat3 = Cat("张三","粉色", 2 )
- 输出后,不管你有没有对这3个对象进行操作,他会3次执行“初始值次数验证”。这说明:在创建对象时被调用,并且储存在内存中
- 验证方法:在Cat类的 __init__()方法第一行输入 print(“初始值次数验证”),并且此时,我创建了3个对象, cat1 = Cat("小喵","白色", 2 ), cat1 = Cat("小喵","白色", 2 ), cat3 = Cat("张三","粉色", 2 )
- self关键词:在类的内部表示所有当前对象。
-
class Cat: def __init__(self,nick,color,age): print("初始值次数验证") self.nick = nick self.color = color self.age = age def eat(self,count): print(self.nick,"在",count,"条吃鱼!!!") def sleep(self): print("猫在睡觉!!!") cat1 = Cat("小喵","白色",3) cat2 = Cat("小黑","黑色",1) #用法1:获取某个对象属性 对象名. 属性名 print(cat1.nick) print(cat2.color) # 用法2:通过某个对象来调用方法 对象.方法名() cat1.sleep() cat2.eat(8)
- 类的封装
- 想让别人用你码的时候,不想让别人直接绕过验证等操作,因为可以直接调用就能修改,这样会有错误。
- ***例如,你写了个登录方法,一个存款方法。在存款方法里有验证(密码正确进入,密码失败则进不去),不使用封装就会跳过验证,这不符合逻辑正确性,此时就必须封装
- 用法:
- 方法封装: 在前面加2个下划线 def __login(self )
- 变量封装: 同上 c2. __balance c2 = BandCard("bbbbbb", "123", "lisi", 3000) BandCard是类方法,后面跟的是具体银行数据
- 说明为什么要使用这两种封装?首先是为了安全性。原因是当你想直接调用c2 的balance数值( c2. balance),可以得到3000,那么现在封装变量__balance,那么适用于同样方法直接调用c2 的balance数值( c2. __balance),此时会报错。 那么封装方法也是为了安全性。当你想调用def deposit(self)方法时,会忽略前面的验证(比如deposit前面执行的方法是用户登录,return出1表示用户输入正确)。因为在deposit你设了一个判断 val = 1,没封装就会导致直接跳过val = 1(看下面代码可更加明白)。
-
# 案例-银行卡类 # 编写一个银行卡类: # 属性:银行名称,卡号,密码,姓名,余额 # 方法:登录,存款 class BandCard: def __init__(self, account, pwd, name, balance): self.bank = "python bank" self.account = account self.pwd = pwd self.name = name self.balance = balance def login(self): account = input("请输入卡号: ") pwd = input("请输入密码") if self.account == account and self.pwd == pwd: print("成功登录") return 1 else: print("登录失败") return 0 def __deposit(self): val = self.login() if val == 1: money = float(input("存款金额:")) currentMoney = self.balance return print(self.bank, "今天, 用户: ", self.name, "存入金额为", (money + currentMoney)) # --------------------------------------------------------- user1 = BandCard("aaaaaa", "123", "zhangsan", 2000) user2 = BandCard("bbbbbb", "123", "lisi", 3000) # 好处:与面向过程对比,在用户信息输入时,直接拿该对象调用即可。不需要循环遍历一个由字典组成的列表 user2.deposit()
- 写一个方法里面你没有封装,其余的全部封装,然后那个没有封装是一个启动你程序的方法,此方法执行你的代码逻辑(相当于没封装的方法是个main方法)
-
# 案例-银行卡类 # 编写一个银行卡类: # 属性:银行名称,卡号,密码,姓名,余额 # 方法:登录,存款 class BandCard: def __init__(self, __account, __pwd, __name, __balance): self.bank = "python bank" self.account = __account self.pwd = __pwd self.name = __name self.balance = __balance def __login(self): account = input("请输入卡号: ") pwd = input("请输入密码") if self.account == account and self.pwd == pwd: print("成功登录") return 1 else: print("登录失败") return 0 def deposit(self): val = self.login() if val == 1: money = float(input("存款金额:")) currentMoney = self.balance return print(self.bank, "今天, 用户: ", self.name, "存入金额为", (money + currentMoney)) # --------------------------------------------------------- user1 = BandCard("aaaaaa", "123", "zhangsan", 2000) user2 = BandCard("bbbbbb", "123", "lisi", 3000) # 好处:与面向过程对比,在用户信息输入时,直接拿该对象调用即可。不需要循环遍历一个由字典组成的列表 user2.deposit()
- 类的继承 (减少代码量)
- 将多个类中的公共属性和方法统一定义(比如:动物类里面有鸟类,猫类,狗类等,其中动物类就是父类)
- 实际意义:当你调用子类的时候,父类的方法也会被调用,这样就不用在每个类里面写同样的内容了
- 记住:python中,父类可以被多个子类拥有,并且子类也可以拥有多个父类
-
class Animal(): def __init__(self, color, age): self.__color = color self.__age = age class Cat(Animal): # 把父类写到括号里,即继承父类 pass class Dog(Animal): # 把父类写到括号里,即继承父类 pass class Bird(Animal): # 把父类写到括号里,即继承父类 pass