类与对象
类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法,对象是类的实例。
类的三要素:类名、属性、方法
类名:大驼峰命名法每个单词首字母大写,无下划线
属性和方法的确定:
对对象的特征描述通常可以定义成属性
对象具有的行为通常可以定义成方法
面向对象三大特性:
封装:根据职责将属性和方法封装到一个抽象的 类 中
继承:实现代码的重用,相同的代码不需要重复的编写
多态:不同的对象调用相同的方法,产生不同的执行结果,增加代码的灵活度
定义简单类
创建只包含对象的方法
class 类名:
def 方法1(self,参数列表):
pass
def 方法2(self,参数列表):
pass
创建对象
对象变量=类名()
`dir` 内置函数返回对象的方法
方法中的self参数
在python中给对象设置属性,非常的容易,但是不推荐使用
(不修改类,直接给类增加一个属性)
对象名.属性=属性值
class Animal:
def eat(self):
print("吃饭")
def run(self):
print("跑步")
xx = Animal()
xx.name = "Tom"
ss = Animal()
print(xx.name)
print(ss.name)
哪一个对象调用的方法,self就是哪一个对象的引用(在内存中的地址相同)
在类封装的方法内部,self 就表示当前调用方法的对象自己
调用方法时,程序员不需要传递 self 参数
在方法内部
可以通过 self. 访问对象的属性
也可以通过 self. 调用其他的对象方法
初始化方法
当使用 类名() 创建对象时,会自动执行以下操作:
1.为对象在内存中 分配空间 —— 创建对象
2.为对象的属性 设置初始值 —— 初始化方法(init)
这个初始化方法 就是 __init__ 方法,__init__ 是对象的内置方法
__init__ 方法是专用来定义一个类 具有哪些属性的方法!
如:
class Cat:
def __init__(self):
print("这是一个初始化方法")
tom = Cat()
>>>这是一个初始化方法
创建对象时会自动调用__init__方法
在初始化方法内部定义属性
在 __init__ 方法内部使用 self.属性名 = 属性的初始值 就可以 定义属性
定义属性之后,再使用 Cat 类创建的对象,都会拥有该属性
如:
class Cat:
def __init__(self):
self.name="Tom"
tom = Cat()
print(tom.name)
>>>Tom
改造初始化方法 —— 初始化的同时设置初始值
在开发中,如果希望在 创建对象的同时,就设置对象的属性,可以对 __init__ 方法进行 改造
1.把希望设置的属性值,定义成 __init__ 方法的参数
2.在方法内部使用 self.属性 = 形参 接收外部传递的参数
3.在创建对象时,使用 类名(属性1, 属性2...) 调用
如:
class Cat:
def __init__(self,new_name):
self.name = new_name
def eat(self):
print("%s爱吃鱼" % self.name)
tom = Cat("Tom")
lazy_cat=Cat("lazy_cat")
tom.eat()
lazy_cat.eat()
内置方法和属性
__del__ 方法:对象被从内存中销毁前,会被自动调用
__str__ 方法:返回对象的描述信息,print 函数输出使用
__del__ 方法
在 Python 中
当使用 类名() 创建对象时,为对象 分配完空间后,自动调用 __init__ 方法
当一个对象被从内存中销毁前,会自动调用 __del__ 方法
应用场景:
__init__ 改造初始化方法,可以让创建对象更加灵活
__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=Cat("tom")
print(tom.name)
print("-"*50)
>>>
tom来了
tom
--------------------------------------------------
tom我去了
__str__ 方法
在 Python 中,使用 print 输出对象变量,默认情况下,会输出这个变量引用的对象是由哪一个类创建的对象,以及在内存中的地址(十六进制表示)
如果在开发中,希望使用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=Cat("tom")
print(tom)
>>>
tom来了
我是小猫[tom]
tom我去了
封装
封装根据 职责将 属性和 方法封装到一个抽象的 类 中
案例一
1.封装是面向对象编程的一大特点
2.面向对象编程的第一步 —— 将 属性和方法 封装到一个抽象的类中
3.外界使用类创建对象然后让对象调用方法
4.对象方法的细节都被封装在类的内部
如:
class Person:
def __init__(self,name,weight):
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+=0.6
x=Person("xiao ming",65.3)
x.run()
x.eat()
print(x)
>>>
xiao ming爱跑步,跑步锻炼身体
xiao ming是吃货,吃完再减肥
我的名字叫 xiao ming 体重是 65.40 公斤`
案例二
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):
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,chest,table,sep="\n")
my_house=House("两室一厅",60)
my_house.add_item(bed)
my_house.add_item(chest)
my_house.add_item(table)
print(my_house)
案例三
一个对象的 属性可以是 另外一个类创建的对象
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("Peng![%s]发射了一枚子弹" % self.model)
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")
xusanduo.gun=ak47
xusanduo.fire()
print(xusanduo.gun)
>>>
[xusanduo]冲锋
Peng![ak47]发射了一枚子弹
<__main__.Gun object at 0x000001C069E90408>
身份运算符
身份运算符用于比较两个对象的内存地址是否一致 —— 是否是对同一个对象的引用
在Python中针对None比较时,建议使用is判断
is:is 是判断两个标识符是不是引用同一个对象x is y,类似 id(x) == id(y)
is not:is not 是判断两个标识符是不是引用不同对象 x is not y,类似 id(a) != id(b)
私有属性和私有方法
应用场景及定义方式
应用场景:
在实际开发中,对象的某些属性或方法 可能只希望在对象的内部被使用,而不希望在外部被访问到
私有属性就是对象不希望公开的属性
私有方法就是对象不希望公开的方法
定义方式:
在定属性或方法时,在属性名或者方法名前增加两个下划线定义的就是 私有属性或方法
伪私有属性和私有方法(科普)
提示:在日常开发中,不要使用这种方式,访问对象的 私有属性 或 私有方法
Python 中,并没有 真正意义 的 私有
- 在给 属性、方法 命名时,实际是对 名称 做了一些特殊处理,使得外界无法访问到
- 处理方式:在 名称 前面加上
_类名 => _类名__名称
python中没有真正意义上的私有,可以通过 对象._类名.__名称来访问私有方法和私有属性
继承
继承 实现代码的重用,相同的代码不需要重复的编写
单继承
子类拥有 父类 的所有 方法和 属性
继承的概念、语法和特点
继承的语法
class 类名(父类名):
pass
子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
子类中应该根据职责,封装子类特有的属性和方法
Dog类是Animal类的子类,Animal类是Dog类的父类,Dog类从Animal类继承
Dog类是Animal类的派生类,Animal类是Dog类的基类,Dog类从Animal类派生
继承的传递性:
C类从B类继承,B类又从A类继承
那么C类就具有B类和A类的所有属性和方法
子类拥有父类以及父类的父类中封装的所有属性和法
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):
pass
wangcai = xiaotianquan()
wangcai.eat()
wangcai.drink()
wangcai.run()
wangcai.sleep()
wangcai.bark()
方法的重写
子类拥有父类的所有方法和属性
子类继承自父类,可以直接享受父类中已经封装好的方法,不需要再次开发
当 父类 的方法实现不能满足子类需求时,可以对方法进行 重写(override)
重写父类方法有两种情况:
1. 覆盖父类的方法
2. 对父类方法进行 扩展
覆盖父类的方法
如果在开发中,父类的方法实现和子类的方法实现,完全不同
就可以使用覆盖的方式,在子类中重新编写父类的方法实现
具体的实现方式,就相当于在 子类中定义了一个和父类同名的方法并且实现
重写之后,在运行时,只会调用子类中重写的方法,而不再会调用父类封装的方法
对父类方法进行 扩展
如果在开发中,子类的方法实现中包含父类的方法实现
父类原本封装的方法实现是子类方法的一部分
就可以使用扩展的方式
1.在子类中重写父类的方法
2.在需要的位置使用 super().父类方法 来调用父类方法的执行
3.代码其他的位置针对子类的需求,编写子类特有的代码实现
关于 super
在 Python中 super 是一个 特殊的类
super() 就是使用 super 类创建出来的对象
最常 使用的场景就是在 重写父类方法时,调用 在父类中封装的方法实现
调用父类方法的另外一种方式(知道)
在 Python 2.x 时,如果需要调用父类的方法,还可以使用以下方式:
父类名.方法(self)
这种方式,目前在 Python 3.x 还支持这种方式
这种方法 不推荐使用,因为一旦 父类发生变化,方法调用位置的 类名 同样需要修改
提示
在开发时,父类名 和 super() 两种方式不要混用
如果使用 当前子类名 调用方法,会形成递归调用,出现死循环
父类的私有属性和私有方法
1.子类对象不能在自己的方法内部,直接访问父类的 私有属性或私有方法
2.子类对象可以通过父类的公有方法间接访问到私有属性或私有方法
私有属性、方法是对象的隐私,不对外公开,外界以及子类都不能直接访问
私有属性、方法 通常用于做一些内部的事情
B的对象不能直接访问 __num2 属性
B的对象不能在demo方法内访问 __num2 属性
B的对象可以在demo方法内,调用父类的test方法
父类的test方法内部,能够访问 __num2 属性和 __test 方法
多继承
子类可以拥有 多个父类,并且具有 所有父类 的 属性 和 方法
class 子类名(父类名1, 父类名2...)
pass
多继承的使用注意事项
问题的提出
如果不同的父类中存在同名的方法,子类对象在调用方法时,会调用哪一个父类中的方法呢?
提示:
开发时,应该尽量避免这种容易产生混淆的情况!
—— 如果父类之间存在 同名的属性或者方法,应该尽量避免使用多继承
多态
多态 不同的 子类对象调用相同的 父类方法,产生不同的执行结果
多态可以增加代码的灵活度
以继承和重写父类方法为前提
是调用方法的技巧,不会影响到类的内部设计
案例
需求:
在Dog类中封装方法game
普通狗只是简单的玩耍
定义XiaoTianDog继承自Dog,并且重写game方法
哮天犬需要在天上玩耍
定义Person类,并且封装一个和狗玩的方法
在方法内部,直接让狗对象调用game方法
Person类中只需要让狗对象调用game方法,而不关心具体是什么狗
game方法是在Dog父类中定义的
在程序执行时,传入不同的狗对象实参,就会产生不同的执行效果
多态 更容易编写出出通用的代码,做出通用的编程,以适应需求的不断变化!
class Dog:
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:
def __init__(self,name):
self.name=name
def game_with_dog(self,dog):
print("%s和%s一起玩"%(self.name,dog.name))
dog.game()
dahuang=Dog("大黄")
wangcai=XiaoTianQuan("神*旺财")
xiaoming=Person("小明")
xiaoming.game_with_dog(wangcai)
xiaoming.game_with_dog(dahuang)
>>>
小明和神*旺财一起玩
神*旺财飞到天上去玩耍
小明和大黄一起玩
大黄蹦蹦跳跳的玩耍
!**
class Dog:
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:
def __init__(self,name):
self.name=name
def game_with_dog(self,dog):
print("%s和%s一起玩"%(self.name,dog.name))
dog.game()
dahuang=Dog("大黄")
wangcai=XiaoTianQuan("神*旺财")
xiaoming=Person("小明")
xiaoming.game_with_dog(wangcai)
xiaoming.game_with_dog(dahuang)
>>>
小明和神*旺财一起玩
神*旺财飞到天上去玩耍
小明和大黄一起玩
大黄蹦蹦跳跳的玩耍
|