PythonDay10
本节内容
- 过程和函数
- 类和对象
- 类的设计
- 初始化方法
- 封装
- 封装案例
- 身份运算符
- 继承
- 方法的重写
- 多继承
- 多态
- 类的结构
-
方法综合案例
1.过程和函数
过程是早期的一个编程概念;过程类似于函数,只能执行,但没有返回值;
函数不仅能执行,还可以返回结果;
面向过程
- 把完成某一需求的所有步骤从头到尾逐步实现
- 根据开发需求,将某些功能独立的代码封装成一个又一个函数
- 最后完成的代码,就是顺序地调用不同函数
面向对象 相比较函数,面向对象是更大的封装,根据职责在一个对象中封装多个方法
- 在完成某一需求前,首先确定职责--要做的事情
- 根据职责确定不同的对象,在对象内部封装不同的方法
- 最后完成的代码,就是顺序地让不同的对象调用不同的方法
专门应对复杂项目开发,提供固定的套路;注重对象和职责;需要在面向过程的基础上,在学习一些面向对象的语法;
2.类和对象
类是对一群具有相同特征或者行为的事物的一个统称,是抽象的,不能直接使用;
特征被称为属性,行为被称为方法;
类相当于制造飞机的图纸,是一个模板,负责创建对象的;
对象由类创建出来的一个具体存在,可以直接使用;
由哪一个类创建出来的对象,就拥有在哪一个类中定义的:属性和方法;
类是模板,对象是根据类这个模板创建出来的,应该先有类,再有对象;
类只有一个,而对象可以有很多个;不同对象之间属性可能各有不同;
类中定义什么属性和方法,对象中就有什么属性和方法;
3.类的设计
在程序开发中,要设计一个类,通常需要满足三个要素:
- 类名 这类事物的名字,满足大驼峰命令法(CapWords)
- 属性 这类事物具有什么样的特征
- 方法 这类事物具有什么样的行为
属性:对对象的特征描述;
方法:对象具有的行为(动词);
4.dir内置函数
使用内置函数dir传入标识符/数据,可以查看对象内的所有属性及方法;
5.定义简单的类
6.面向对象的程序
需求:小猫爱吃鱼,小猫要喝水;
class Cat:
def eat(self):
print("小猫吃鱼")
def drink(self):
print("小猫喝水")
#创建对象
tom=Cat()
tom.eat()
tom.drink()
7.初始化方法
当使用类名()创建对象时,会自动执行以下操作:
- 为对象在内存中分配空间---创建对象
- 为对象的属性设置初始值---初始化方法(init)
_init_方法是对象的内置方法
在初始化方法内部定义属性
在_init_方法内部使用self.属性名=属性的初始值就可以定义属性
定义属性后,再使用Cat类创建的对象,都会拥有该属性
class Cat:
def __init__(self):
print("这是一个初始化方法")
#self.属性名=属性的初始值
self.name="Tom"
def eat(self):
print("%s爱吃鱼"%self.name)
#使用类名()创建对象的时候,会自动调用初始化方法_init_
tom=Cat()
print(tom.name)
如果希望在创建对象的同时,就设置对象的属性,可以对_init_方法进行改造;
把希望设置的属性值,定义成_init_方法的参数
在方法内部使用 self.属性=形参 接收外部传递的参数
在创建对象时,使用 类名(属性1,属性2....)调用
class Cat:
def __init__(self,new_name):
print("这是一个初始化方法")
#self.属性名=属性的初始值
#self.name="Tom"
self.name=new_name
def eat(self):
print("%s爱吃鱼"%self.name)
#使用类名()创建对象的时候,会自动调用初始化方法_init_
tom=Cat("Tom")
print(tom.name)
lazy_cat=Cat("大懒猫")
lazy_cat.eat()
_del_如果希望在对象被销毁前,会自动调用_del_方法
生命周期
- 一个对象从调用类名()创建,生命周期开始
- 一个对象的_del_方法一旦被调用,生命周期结束
- 在对象的生命周期内,可以访问对象属性,或者让对象调用方法
class Cat():
def __init__(self,new_name):
self.name=new_name
print("%s 来了"%self.name)
def __del__(self):
print("%s 我去了"%self.name)
#tom是一个全局变量
tom=Cat("Tom")
print(tom.name)
#del可以删除一个对象
del tom
print("-"*50)
_str_方法必须返回一个字符串,能够打印自定义内容;
class Cat():
def __init__(self,new_name):
self.name=new_name
print("%s 来了"%self.name)
def __del__(self):
print("%s 我去了"%self.name)
def __str__(self):
#必须返回一个字符串
return "我是小猫[%s]"%self.name
#tom是一个全局变量
tom=Cat("Tom")
print(tom)
8.封装
封装是面向对象编程的一大特点;
面向对象编程的第一步是将属性和方法封装到一个抽象的类中,外界使用类创建对象,然后让对象调用方法;
对象方法的细节都被封装在类的内部;
同一个类创建的多个对象之间,属性互不干扰的;
class Person:
def __init__(self,name,weight):
#self.属性=形参
self.name=name
self.weight=weight
def __str__(self):
return "我的名字叫%s 体重是%.2f公斤"%(self.name,self.weight)
def run(self):
print("%s爱跑步"%self.name)
self.weight-=0.5
def eat(self):
print("%s是吃货"%self.name)
self.weight+=1
xiaoming=Person("小明",75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
在对象方法内部,是可以直接访问对象的属性的;
# data: 2021-04-18
class HouseItem:
def __init__(self,name,area):
self.name=name
self.area=area
def __str__(self):
return "[%s]占地%.2f"%(self.name,self.area)
class House:
def __init__(self,house_type,area):
self.house_type=house_type
self.area=area
#剩余面积
self.free_area=area
#家具名称列表
self.item_list=[]
def __str__(self):
#Python能够自动将一对括号内部的代码拼接在一起
return ("户型:%s\n总面积:%.2f[剩余:%.2f]\n家具:%s")\
%(self.house_type,self.area,
self.free_area,self.item_list)
def add_item(self,item):
print("要添加%s"%item)
#判断家具的面积
if item.area>self.free_area:
print("%s面积太大,无法添加"%item.name)
return
#将家具的名称添加到列表中
self.item_list.append(item.name)
#计算剩余面积
self.free_area-=item.area
#创建家具
bed=HouseItem("席梦思",4)
chest=HouseItem("衣柜",2)
table=HouseItem("餐桌",1.5)
print(bed)
print(chest)
print(table)
#创建房子对象
my_home=House("两室一厅",80)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
一个对象的属性可以是另一个类创建的对象
需求
枪类
- 士兵许三多有一把AK47
- 士兵可以开火
- 枪能够发射子弹
- 枪可以装填子弹--增加子弹数量
士兵类
假设每个新兵都没有枪
定义没有初始值的属性
定义属性时,如果不知道设置什么初始值,可以设置为None;
None关键字表示什么都没有
表示一个空对象,没有方法和属性,是一个特殊的常量;
可以将None赋值给任何一个变量;
# data: 2021-05-04
class Gun:
def __init__(self,model):
#枪的型号
self.model=model
#子弹的数量
self.bullet_count=0
def add_bullet(self,count):
self.bullet_count+=count
def shoot(self):
#判断子弹数量
if self.bullet_count<=0:
print("[%s]没有子弹了"%self.model)
return
#发射子弹,数量减少
self.bullet_count-=1
print("[%s]突突突...[%d]"%(self.model,self.bullet_count))
class Soldier:
def __init__(self,name):
#定义新兵的姓名
self.name=name
#枪-新兵没有枪
self.gun=None
def fire(self):
#判断士兵是否有枪
if self.gun is None:
print("[%s]没有枪"%self.name)
return
#高喊口号
print("冲啊...[%s]"%self.name)
#装填子弹
self.gun.add_bullet(50)
#发射子弹
self.gun.shoot()
#创建枪对象
ak47=Gun("AK47")
#创建许三多
xusanduo=Soldier("许三多")
xusanduo.gun=ak47
xusanduo.fire()
print(xusanduo.gun)
9.身份运算符
身份运算符用于比较两个对象的内存地址是否一致--是否是对同一个对象的引用;
is:is是判断两个标志符是不是引用同一个对象 x is y,类似id(x)==id(y)
is not:is not是判断两个标志符是不是引用不同对象 x is not y,类似id(a)!=id(b)
is与==区别:
is用于判断两个变量引用对象是否为同一个
==用于判断引用变量的值是否相等
a=[1,2,3]
b=[1,2,3]
print(b is a)
print(b==a)
10.私有属性和私有方法
定义方式
在定义属性或方法时,在属性名或者方法名前增加两个下划线,定义的就是私有属性或方法;
class Women:
def __init__(self,name):
self.name=name
self.__age=18
def __secret(self):
#在对象的方法内部,是可以访问对象的私有属性的
print("%s年龄是%d"%(self.name,self.__age))
xiaofang=Women("小芳")
#私有属性在外界不能够被直接访问
#print(xiaofang.__age)
print(xiaofang._Women__age)
#私有方法,同样不允许在外界直接访问
#xiaofang.__secret()
xiaofang._Women__secret()
11.继承
继承实现代码的重用,相同的代码不需要重复编写;
继承:子类拥有父类的所有方法和属性;
继承的语法
class 类名(父类名):
pass
class Animal:
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(Animal):
def bark(self):
print("汪汪汪")
class XiaoTianQuan(Dog):
def fly(self):
print("飞")
wangcai=Dog()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
xtq=XiaoTianQuan()
xtq.fly()
xtq.bark()
xtq.eat()
子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发;
子类中应该根据职责,封装子类特有的属性和方法;
专业术语
- Dog类是Animal类的子类,Animal类是Dog类的父类,Dog类从Animal类继承
- Dog类是Animal类的派生类,Animal类是Dog类的基类,Dog类从Animal类派生
C类从B类继承,B类又从A类继承
那么C类就具有B类和A类的所有属性和方法;
12.方法的重写
重写父类方法有两种情况:覆盖父类的方法;对父类方法进行扩展。
覆盖父类方法:具体实现方式,就相当于在子类中定义了一个和父类同名的方法并且实现;
#覆盖父类的方法
class Animal:
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(Animal):
def bark(self):
print("汪汪汪")
class XiaoTianQuan(Dog):
def fly(self):
print("飞")
def bark(self):
print("叫的很厉害")
xtq=XiaoTianQuan()
#如果子类中,重写父类的方法
#在使用子类对象调用方法时,会调用子类中重写的方法
xtq.bark()
对父类方法进行扩展:在子类中重写父类的方法,在需要的位置使用super().父类方法来调用父类方法的执行,代码其它的位置针对子类的需求,编写子类特有的代码实现;
关于super:是一个特殊的类,super()就是使用super类创建出来的对象,最常使用的场景就是在重写父类方法时,调用在父类中封装的方法实现。
class Animal:
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class Dog(Animal):
def bark(self):
print("汪汪汪")
class XiaoTianQuan(Dog):
def fly(self):
print("飞")
def bark(self):
#针对子类特有需求编写代码
print("神一样叫唤")
#使用super().调用原本在父类中封装的方法
super().bark()
#增加其他子类的代码
print("-----")
xtq=XiaoTianQuan()
#如果子类中,重写父类的方法
#在使用子类对象调用方法时,会调用子类中重写的方法
xtq.bark()
子类对象不能在自己的方法内部,直接访问父类的私有属性或私有方法;
子类对象可以通过父类的公有方法间接访问到私有属性或私有方法;
class A:
def __init__(self):
self.num1=100
self.__num2=200
def __test(self):
print("私有方法%d%d"%(self.num1,self.__num2))
def test(self):
print("父类的公有方法")
self.__test()
class B(A):
def demo(self):
#在子类的对象方法中,不能访问父类的私有属性
#print("访问父类私有属性%d"%self.__num2)
#在子类的对象方法中,不能调用父类的私有方法
#self.__test()
#访问父类的公有属性
print("子类方法%d"%self.num1)
#调用父类的公有方法
self.test()
b=B()
b.demo()
print(b.num1)
b.test()
13.多继承
子类可以拥有多个父类,并且具有所有父类的属性和方法;
class 子类名(父类名1,父类名2...):
pass
class A:
def test(self):
print("test方法")
class B:
def demo(self):
print("demo方法")
class C(A,B):
"""多继承可以让子类对象,同时具有多个父类的属性和方法"""
pass
c=C()
c.test()
c.demo()
MRO--方法搜索顺序
针对类提供一个内置属性__mro__可以查看方法搜索顺序
MRO是method resolution order,主要用于在多继承时判断方法,属性的调用路径;
print(C.__mro__)
14.多态
不同的子类对象调用相同的父类方法,产生不同的执行结果;
多态可以增加代码的灵活度;以继承和重写父类方法为前提;是调用方法的技巧,不会影响到类的内部设计;
class Dog(object):
def __init__(self,name):
self.name=name
def game(self):
print("%s蹦蹦跳跳"%self.name)
class XiaoTianDog(Dog):
def game(self):
print("%s飞到天上玩耍"%self.name)
class Person(object):
def __init__(self,name):
self.name=name
def game_with_dog(self,dog):
print("%s和%s快乐玩耍"%(self.name,dog.name))
dog.game()
#创建狗对象
wangcai=Dog("旺财")
feitianwangcai=XiaoTianDog("飞天旺财")
#创建人对象
xiaoming=Person("小明")
#调用和狗玩的方法
xiaoming.game_with_dog(wangcai)
xiaoming.game_with_dog(feitianwangcai)
15.类的结构
- 使用面向对象开发,第一步是设计类;
- 使用类名()创建对象,创建对象的动作有两步:在内存中为对象分配空间;调用初始化方法__init__为对象初始化。
- 对象创建后,内存中就有了一个对象的实实在在的存在---案例;
创建出来的对象叫做类的实例;创建对象的动作叫做实例化;对象的属性叫做实例属性;对象调用的方法叫做实例方法;
类是一个特殊的对象,Python中一切皆对象;
class Tool(object):
#使用赋值语句,定义类属性,记录创建工具对象的总数
count=0
def __init__(self,name):
self.name=name
Tool.count+=1
#创建工具对象
tool1=Tool("斧头")
tool2=Tool("榔头")
tool3=Tool("水桶")
print(Tool.count)
访问类属性有两种方式:类名.类属性;对象.类属性(不推荐)
如果使用对象.类属性=值赋值语句,只会给对象添加一个属性,而不会影响到类属性的值;
类方法就是针对类对象定义的方法;类属性就是针对类对象定义的属性;
类方法的定义
@classmethod
def 类方法名(cls)
pass
class Tool(object):
count=0
@classmethod
def show_tool_count(cls):
print("工具数量%d"%cls.count)
def __init__(self,name):
self.name=name
Tool.count+=1
tool1=Tool("斧头")
tool2=Tool("榔头")
Tool.show_tool_count()
静态方法
@staticmethod
def 静态方法():
pass
class Dog(object):
@staticmethod
def run():
#不访问实例属性/方法
print("小狗要跑")
#调用静态方法--不需要创建对象
Dog.run()
16.方法综合案例
class Game(object):
top_score=0
def __init__(self,player_name):
self.player_name=player_name
@staticmethod
def show_help():
print("帮助信息:hello!")
@classmethod
def show_top_score(cls):
print("历史记录最高:%d"%cls.top_score)
def start_game(self):
print("%s开始游戏"%self.player_name)
#1.查看游戏帮助信息
Game.show_help()
#2.查看历史最高分
Game.show_top_score()
#3.创建游戏对象
game=Game("小明")
game.start_game()
小结:实例方法--方法内部需要访问实例属性;实例方法内部可以使用类名.访问类属性
类方法--方法内部只需要访问类属性;
静态方法--方法内部,不需要访问实例属性和类属性;