面向对象:
独立独立存在,在程序开发时,现有类再有对象(属性 方法)
类名 大驼峰命名法
属性 这类事物具有什么样的特征
方法 这类事物具有什么样的行为
对对象的特征描述,通常可以定义成属性
对象具有行为,通常可以定义成方法
面向过程的基础语法 dir内置函数 ,使用dir传入标识符/数据 可以查看对象内的所有属性及方法。
提示 __方法名__
格式的方法是python提供的内置方法/属性
方法名 | 类型 | 作用 |
---|---|---|
__new__ |
方法 | 创建对象时,会被自动调用 |
__init__ |
方法 | 对象被初始化时,会被自动调用 |
__del__ |
方法 | 对象被从内存中销毁前,会被自动调用 |
__str__ |
方法 | 返回对象的描述信息,print函数输出使用 |
定义一个简单的类
面向对象是一个更大的封装,一个类中封装多个方法,这样通过这个类创建的对象,就可以直接调用这些方法了。
class 类名:
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass
方法的定义格式和之前学习的函数几乎一样,区别就是第一个参数必须为self 类名的命名规则要符合大驼峰命名法!
创建对象 对象变量=类名()
面向对象的第一个对象程序:
需求 : 小猫爱吃鱼 小猫要喝水
class Cat:
"""这是一个猫类"""
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫要喝水")
#创建对象
tom = Cat()
tom.eat()
tom.drink()
引用的概念:与面向过程一样
%x 打印出的是16进制,%d是十进制打印数字
在cat类中创建多个对象
class Cat:
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫要喝水")
#创建对象
tom = Cat()
tom.eat()
tom.drink()
print(tom)
#再创建一个对象
lazy_cat =Cat()
lazy_cat.eat()
lazy_cat.drink()
print(lazy_cat)
#lazy_cat2 =lazy_cat是一个对象
#可以使用相同的类创建不同的对象
lazy_cat2=lazy_cat
print(lazy_cat2)
案例改造 给对象增加属性
在python中,要给对象设置属性非常的容易,但是不推荐使用,因为对象属性的封装应该封装在类的内部 只需要在类的外部的代码中直接通过 .
来设置一个属性即可。
class Cat:
def eat(self):
print("小猫爱吃鱼")
def drink(self):
print("小猫要喝水")
#创建对象
tom = Cat()
#可以使用.属性名 利用赋值语句就可以了 在pycharm的单步调试可以看到name的属性
#不推荐使用 因为只在外面强制修改了名字
tom.name ="Tom"
tom.eat()
tom.drink()
哪一个对象调用方法,方法内的self就是哪一个对象的引用
在类封装的方法内部,self就表示当调用方法的对象自己
在调用方法时,开发用户不需要传递self参数。
在方法内部可以通过self. 访问对象属性
也可以通过self. 调用其他的对象方法
class Cat:
def eat(self):
#哪一个对象调用的方法,self就是哪一个对象的引用
print("%s爱吃鱼"%self.name)
def drink(self):
print("%s要喝水"%self.name)
#创建对象
tom = Cat()
#可以使用.属性名 利用赋值语句就可以了
tom.name ="Tom"
tom.eat()
tom.drink()
#再创建一个对象
lazy_cat =Cat()
lazy_cat.name ="大懒猫"
lazy_cat.eat()
lazy_cat.drink()
在类的外部调用如果赋值在调用后面,否则就会报错。所以,非常不推荐在类外面进行创建。
对象的初始化方法:
当使用类名创建对象时,会自动执行以下操作
为对象分配空间 为对象的属性设置初始值,初始化方法(init)
这个初始化方法就是 __init__
方法 __init__
是对象的内置方法
__init__
方法是专门用来定义一个类具有哪些属性的方法
Cat中增加 __init__
方法,验证该方法在创建对象时会被自动调用。
class Cat:
def __init__(self):
print("这是一个初始化方法")
#使用类名()创建对象的时候,会自动调用初始化方法__init__
tom =Cat()
在初始化方法内部定义属性
在 __init__
方法内部使用 self.属性名 =属性的初始值就可以定义属性
在定义属性之后,再使用Cat类创建对象,都会拥有该属性。
class Cat:
def __init__(self):
print("这是一个初始化方法")
#self.属性名 =属性的初始值
self.name="Tom"
#使用类名()创建对象的时候,会自动调用初始化方法__init__
tom =Cat()
print(tom.name)
改造初始化方法–初始化的同时设置初始值
在开发过程中,如果希望在创建对象的同时,就设置对象的属性,就可以对 __init__
方法进行改造,把希望设置的属性值,定义成 __init__
方法的参数 在方法内部使用 self.属性 =形参
接收外部传递的参数。 在创建对象时,使用 类名(属性1,属性2,……)
class Cat:
def __init__(self,new_name):
print("这是一个初始化方法")
#self.属性名 =属性的初始值
self.name =new_name
def eat(self):
print("%s 爱吃鱼"%self.name)
#使用类名()创建对象的时候,会自动调用初始化方法__init__
tom =Cat("大懒猫")
tom.eat()
print(tom.name)
__del__
方法:
当一个对象被从内存中销毁前,再自动调用 __del__
方法
应用场景:
__init__
方法改造初始化方法,可以让创建对象更加灵活
__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__
方法:
如果再开发中使用print输出变量时,能够打印自定义内容,就可以利用 __str__
这个内置方法。
注意:使用 __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)
面向对象的封装案例:
将属性和方法封装到一个抽象的类中 对象方法的细节都被封装在类的内部。
小明爱跑步
1.小明体重75.0公斤
2.小明每次跑步会减肥0.5公斤
3.小明每次吃东西会增加1公斤
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 =self.weight-0.5
def eat(self):
print("%s是吃货,吃完这顿再减肥"%(self.name))
self.weight= self.weight+1
xiaoming =Person("小明",75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
小明爱跑步拓展–小美也爱跑步
1.小明和小美都爱跑步
2.小明体重 75.0公斤
3.小美体重 45.0公斤
4.每次跑步都会减少0.5公斤
5.每次吃东西都会增加1公斤
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 =self.weight-0.5
def eat(self):
print("%s是吃货,吃完这顿再减肥"%(self.name))
self.weight= self.weight+1
xiaoming =Person("小明",75.0)
xiaoming.run()
xiaoming.eat()
print(xiaoming)
xiaomei =Person("小美",45)
xiaomei.eat()
xiaomei.run()
print(xiaomei)
在同一个类创建的多个类,属性是互不干扰的!!!!
案例:(摆放家具)
需求
1.房子有户型,总面积和家具名称列表 新房子没有任何的家具
2.家具有名字和占地面积其中 席梦思占地4平米 衣柜占地2平米 餐桌占地1.5平米
3.将以上三件家具添加到房子里
4.打印房子的时候,户型 总面积,剩余面积,家具名称列表
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)
#1.判断家具面积
if item.area >self.free_area:
print("%s 的面积太大了,无法添加"%item.name)
return
#2.将加剧的名称添加到列表中
self.item_list.append(item.name)
#3.计算剩余面积
self.free_area=self.free_area-item.area
# 1.创建家具
bed =HouseItem("席梦思",4)
chest =HouseItem("衣柜",2)
table =HouseItem("餐桌",1.5)
print(chest)
print(bed)
# 2.创建房子对象
my_home =House("两室一厅",60)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(table)
print(my_home)
案例二
一个对象的属性可以是另一个类创建的对象!
需求:
1.一个士兵许三多有一个 AK47
2.士兵可以开火
3.枪能够发射子弹
4.枪填装子弹–增加子弹数量
注意:
在定义属性的时候,如果不知道设置什么初始值,可以设置为None
-
None关键字表示什么都没有
-
表示一个空对象,没有方法和属性,是一个特殊的变量
-
可以将None赋值给任何一个变量
class Gun:
def __init__(self,model):
#1. 枪的型号
self.model =model
#2. 子弹数量
self.bullet_count =0
def add_bullet(self,count):
self.bullet_count+=count
def shoot(self):
#1.判断子弹数量
if self.bullet_count<=0:
print("[%s]没有子弹了..."%self.model)
return
#2.发射子弹 -1
self.bullet_count -=1
#3. 提示发射信息
print("[%s] 突突突...[%d]"%(self.model,self.bullet_count))
class Soldier:
def __init__(self,name):
#1.新兵姓名
self.name =name
#2.枪 新兵没有枪
self.gun =None
def fire(self):
#1.判断士兵是否有枪
if self.gun ==None:
#if self.gun is None:
print("[%s]还没有枪..."%self.name)
return
#2.高喊口号
print("冲啊...[%s]"%self.name)
#3.让枪装填子弹
self.gun.add_bullet(50)
#4.让枪发射子弹
self.gun.shoot()
#1. 创建枪类对象
ak47 =Gun("AK47")
#2.假设每一个新兵都没有枪 创建许三多
xusanduo =Soldier("许三多")
xusanduo.gun=ak47
xusanduo.fire()
print(xusanduo)
python的身份运算符None 在python中针对None比较时,建议使用is判断
运算符 | 描述 | 示例 |
---|---|---|
is | is是判断两个标识符是不是引用同一个对象 | x is y ,类似id(x)==id(y) |
is not | is not 是判断两个标识符是不是引用不同的对象 | x is not y ,类似id(a) !=id(b) |
is 与==区别:
-
is用于判断两个变量引用对象是否为同一个
-
==用于判断引用变量的值是否相等
私有属性和私有方法
应用场景及定义方式,在实际开发中,对象的某些属性或方法可能只希望在对象的内部被使用,而不希望在外部访问到。
- 私有属性就是对象不希望公开属性
- 私有方法就是对象不希望公开方法
定义方法: 在定义属性或方法时,在属性名或方法名前增加两个下划线,定义就是私有属性或方法。
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.name)
#私有属性,在外界不能够被访问,但是在内部是可以访问的
#方法也一样 __secret(self): 同样在外部不允许访问
# print(xiaofang.__age)
xiaofang.secret()
继承
单继承和多继承
面向对象的三大特性: 封装 继承 多态
封装:根据职责将属性和方法封装到一个抽象的类中
继承:实现代码的重用 相同的代码不需要重复的编写
多态 不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
继承的概念:子类拥有父类的全部方法和属性
继承的语法:
class 类名(父类名):
pass
-
子类继承父类,可以直接享受父类封装好的方法,不需要再次开发
-
子类中应该根据职责,封装子类特有的属性和方法
class Animals:
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class dog(Animals):
def bark(self):
print("汪汪叫")
#创建一个对象 -狗对象
wangcai =dog()
wangcai.eat()
wangcai.drink()
wangcai.sleep()
wangcai.bark()
专业术语:
- Dog类是Animals的子类,Animals类是Dog类的父类,Dog类从Animals类继承
- Dog类是Animal类的派生类,Animals类是Dog类的基类,Dog类从Animals类派生
继承的传递性,子类拥有父类的全部特性,同时也继承有父类的父类的全部特性
class Animals:
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class dog(Animals):
def bark(self):
print("汪汪叫")
class xiaotianquan(dog):
def fly(self):
print("我会飞!")
#创建一个对象 -wangcai
wangcai =xiaotianquan()
wangcai.eat()
wangcai.drink()
wangcai.sleep()
wangcai.bark()
wangcai.fly()
继承的传递性的注意事项,一定要注意是否直接的继承关系,没有直接的继承的关系,那么就不能调用
方法的重写
- 子类拥有父类的所有方法和属性
- 子类继承自父类 可以直接 享受父类中已经封装好的方法,不需要再次开发
- 当父类的方法不能满足子类的需求时,可以对方法进行重写
实现方式:在子类中重新编写一下父类的方法,简单来说就是重新定义一个重名的函数,直接覆盖掉就行。
class Animals:
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class dog(Animals):
def bark(self):
print("汪汪叫")
class xiaotianquan(dog):
def fly(self):
print("我会飞!")
def bark(self):
print("叫的不一样")
xtq =xiaotianquan()
#如果子类中重写了父类的方法
#在使用子类对象调用方法时,会调用子类中封装的方法
xtq.bark()
对父类方法进行拓展:
在父类的方法上进行拓展可以使用
super().父类的方法
class Animals:
def eat(self):
print("吃")
def drink(self):
print("喝")
def run(self):
print("跑")
def sleep(self):
print("睡")
class dog(Animals):
def bark(self):
print("汪汪叫")
class xiaotianquan(dog):
def fly(self):
print("我会飞!")
def bark(self):
#1. 针对子类特有的需求编写代码
print("不一样的叫法")
#2. 使用 super().调用原本在父类中封装的方法
super().bark()
#3. 增加其他子类代码
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))
class B(A):
def demo(self):
#1.在子类的对象方法中,不能访问父类的私有属性
# print("访问父类的私有属性%d"%(self.__num2))
#2.子类对象无法调用,调用父类的私有方法
# self.__test()
pass
#创建一个子类对象
b =B()
print(b)
#在外界不能直接访问对象的私有属性/调用私有方法
# print(b.__num2)
# b.__test()
多继承,子类拥有一个父类被称为单继承,但是子类可以拥有多个父类
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()
使用多继承的注意事项,存在同名的方法时应当注意避免多继承
python的MRO –方法搜索顺序(可以搜索一下哪个优先调用)
print(c.__mro__)
多态
不同的子类对象调用相同的父类方法,产生不同的执行结果
- 多态可以增加代码的灵活度
- 以继承和重写父类方法为前提
- 是调用方法的技巧,不会影响到类的内部设计
多态案例演练:
需求:
- 在dog类中封装方法game (普通狗只是简单的玩耍)
- 定义xiaotianquan继承自dog并重写game方法 (哮天犬需要在天上玩耍)
- 定义Person类,并且封装一个和狗玩的方法 在方法内部,(直接让狗对象调用game方法)
class Dog(object):
def __init__(self,name):
self.name =name
def game(self):
print("%s 蹦蹦跳跳的玩耍"%self.name)
class xiaotianquan(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()
#1.创建一个狗对象
# wangcai =Dog("旺财")
wangcai =xiaotianquan("飞天旺财")
#2.创建一个小明对象
xiaoming =Person("小明")
#3. 让小明调用狗玩的方法
xiaoming.game_with_dog(wangcai)
类的结构
面向对象开发–实例
-
首先要先设计类。
-
使用 类名() 创建对象
- 在内存中为对象分配空间
- 调用实例化方法
__init__
为对象初始化
-
对象创建后,内存中就有了一个对象的实实在在的实例
类名() =====> __init__
定义(实例属性)
对象名.方法名() ====> 实例方法(self)
紧接着对象去调用属性
每一个对象都有自己的独立的内存空间,保存各自不同的属性
类是一个特殊的对象
类属性
- 类属性是类对象中定义的属性
- 通常用来记录与这个类相关的特征
- 类属性不会用于记录具体对象的特征
class Tool(object):
#使用赋值语句,定义类属性,记录创建工具对象的总数
count =0
def __init__(self,name):
self.name =name
#让类属性的值+1
Tool.count +=1
#1.创建工具对象
tool1 =Tool("斧头")
tool2 =Tool("榔头")
#2. 输出工具对象的总数
print(Tool.count)
属性获取机制: python中属性的获取存在一个向上查找的机制
tool1.count
- 首先在对象内部 查找对象属性
- 没有找到就会向上寻找类属性
如果使用 对象.类属性 =值
赋值语句,只会给对象添加一个属性,而不会用想到类属性的值
类方法和静态方法
语法如下:
@classmethod
def 类方法名(cls):
pass
类方法必须要用到修饰器 @classmethod来进行标识,告诉解释器这是一个类方法,类方法的第一个参数应该是cls
- 由哪一个类调用的方法,方法内的cls就是哪一个类的引用
- 这个参数和示例方法的第一个参数是self类似
- 提示,使用其他名称也可以,不过习惯用cls
通过对类名 调用类方法 调用方法时,不需要传递cls参数
在方法内部,也可以通过cls访问类的属性,也可以通过cls调用其他类的方法
示例需求:
- 定义一个工具类
- 每件工具都有自己的name
- 需求–在类封装一个show_tool_count 的类方法,输出使用当前这个类,创建对象的个数
class Tool(object):
count =0
@classmethod
def show_tool_count(cls):
print("工具对象的数量%d"%cls.count)
def __init__(self,name):
self.name =name
#让类属性的值+1
Tool.count +=1
#创建工具对象
tool1 =Tool("斧头")
tool2 =Tool("锄头")
#调用类方法
Tool.show_tool_count()
静态方法:
需要加上@staticmethod关键字
class Dog(object):
@staticmethod
def run():
#不访问实例属性/类属性
print("小狗要跑。。。。")
#通过类名.调用静态方法
#不需要创建对象,可以直接第哦啊用你
Dog.run()
示例:
-
设计一个Game类
-
属性
- 定义一个类属性,top_score 记录游戏的历史最高分
- 定义一个实例属性 play_name 记录当前游戏的玩家姓名
-
方法
- 静态方法 show_help 显示游戏帮助信息
- 类方法 show_top_score 显示历史最高分
- 实例方法 start_grame 开始当前玩家的游戏
-
主程序步骤
- 查看帮助信息
- 查看历史最高分
- 创建游戏对象,开始游戏
class Game(object):
#历史最高分
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("历史记录%d"%cls.top_score)
def sart_game(self):
print("%s 开始游戏啦..."%self.player_name)
#1.查看游戏帮助信息
Game.show_help()
#2.查看历史最高分
Game.show_top_score()
#3.创建游戏对象
game = Game("小明")
game.sart_game()
案例总结
-
案例方法–方法内部需要访问实例属性
- 案例方法内部可以使用赖明访问类属性
-
类方法 –方法内部只需要访问类属性
-
静态方法 –方法内部,不需要访问实例属性和类属性
单例
单例设计模式 让类创建对象,在系统中只有唯一的一个实例,每一次执行 类名() 返回的对象,内存地址是相同的
__new__
方法是一个由object基类提供的内置的静态方法,为对象分配空间,返回对象引用
__init__
方法是初始化,定义实例属性
重写 __new__
方法的代码非常固定
重写 __new__
方法一定要 return super().__new__(cls)
否则 __new__
是一个静态方法,在调用是需要注意注定传递cls参数
class MusicPlayer(object):
def __new__(cls, *args, **kwargs):
#1.创建对象时,new方法会被自动调用
print("创建方法。分配空间")
#2.为对象分配空间
result = super().__new__(cls)
#3.返回对象的引用
return result
def __init__(self):
print("播放器初始化")
#创建播放器对象
play =MusicPlayer()
print(play)
单例设计模式设计演练
class MusicPlayer(object):
#记录第一个被创建的对象的引用
instance =None
def __new__(cls, *args, **kwargs):
#1.创建类属性是否为空对象
if cls.instance is None:
#2.调用父类的方法,为第一个对象分配空间
cls.instance =super().__new__(cls)
return cls.instance
play1 =MusicPlayer()
print(play1)
play2 =MusicPlayer()
print(play2)
写了这么多方法,只要能认真的看完,我相信一定会有质的飞跃的!!!!!