python之面向对象的三大特性,私有属性与私有方法,类的结构,设计模型
1.面向对象的三大特性
(1)封装:
根据职责将属性和方法封装到一个抽象的类中定义的准则
(2)继承:
实现代码的重用,相同的代码不需要重复的编写,设计类的技巧,子类针对自己特有的需求,编写特定的代码
(3)多态:
不同的子类对象调用相同的方法,产生不同的执行结果
1.封装
(1).封装是面向对象编程的一大特点
(2).面向对象编程的第一步,将属性和方法封装到一个抽象的类中
(3).外界使用类创建对象,然后让对象调用的方法
(4).对象方法的细节的都被封装在类的内部
例1:
xxx喜欢跑步
1.xxx体重为75.0公斤
2.xxx每次跑步会减肥0.5公斤
3.xxx每次吃定西会增加1公斤
代码:
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_dec(self):
print '%s 喜欢跑步' % self.name
self.weight -= 0.5
def eat_inc(self):
print '%s 喜欢吃东西' % self.name
self.weight += 1
lily = Person('lily',75)
lily.run_dec()
lily.eat_inc()
print lily
执行结果:
例2:
1.xxx和xxx喜欢跑步
2.xxx体重为75.0公斤
3.xxx每次跑步会减肥0.5公斤
4.xxx每次吃定西会增加1公斤
代码:
# 创建人类
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_dec(self):
print '%s 喜欢跑步' % self.name
self.weight -= 0.5
def eat_inc(self):
print '%s 喜欢吃东西' % self.name
self.weight += 1
# 创建类对象lily
lily = Person('lily', 75)
lily.run_dec()
lily.eat_inc()
print lily
# 创建类对象tom
tom = Person('tom', 45)
tom.run_dec()
tom.eat_inc()
print tom
执行结果:
实例:
1.摆放家具
需求:
1.房子有户型,总面积和家具名称列表
新房子没有任何家具
2.家具有名字和占地面积,其中
床:占4平方米
衣柜:占2平方米
餐桌:占1.5平方米
3.将以上三件家具添加到房子中
4.打印房子时,要求输出:户型,总面积,剩余面积,家具名称列表
代码:
# 定义家具类
class Further():
# 初始化方法定义属性
def __init__(self, name, area):
self.name = name
self.area = area
def __str__(self):
return '%s 占地面积为 %.2f' % (self.name, self.area)
# 创建家具对象
bed = Further('bed', 5)
chest = Further('chest', 3)
desk = Further('desk', 4)
# 定义房子类
class Home():
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 \n剩余面积%.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
# 2.将家具的名称添加到列表中
self.item_list.append(item.name)
# 3. 计算剩余面积
self.free_area -= item.area
# 创建房子对象
my_home = Home('两室一厅', 100)
my_home.add_item(bed)
my_home.add_item(chest)
my_home.add_item(desk)
print my_home
执行结果:
2.士兵开枪
需求:
1.士兵瑞恩有一把AK47
2.士兵可以开火
3.枪能够发射子弹
4.枪能够装填子弹
代码:
class Gun():
def __init__(self,model):
# 枪的型号
self.model = model
# 子弹的数量
self.buttet_count = 0
def add_buttet(self,count):
self.buttet_count += count
def shoot(self):
# 1.判断子弹的数量
if self.buttet_count <= 0:
print '%s没有子弹了'%self.model
return
# 2.发射子弹
self.buttet_count -= 1
# 3.提示发射信息
print '%s突突突%d' %(self.model,self.buttet_count)
class Soldier():
def __init__(self,name):
self.name = name
self.gun = None
def fire(self):
# 1.判断士兵有没有枪
if self.gun == None:
print '%s没有枪!!!'%self.name
# 2.高喊口号
print 'go!!!!%s' %self.name
# 3.让枪装填子弹
self.gun.add_buttet(50)
# 4.发射子弹
self.gun.shoot()
# 1.创建枪对象
ak47 = Gun('Ak47')
# 2.创建士兵
ryan = Soldier('Ryan')
ryan.gun = ak47
ryan.fire()
print ryan.gun
执行结果:
2.继承
(1)单继承:
1.继承的概念:
子类拥有父类的所有方法和属性,只需要封装自己特有的属性和方法
2.语法格式:
class 类名(父类):
def 子类特有的方法和属性
代码:
class Animal():
def eat(self):
print '吃'
def drink(self):
print '喝'
def run(self):
print '跑'
def sleep(self):
print '睡'
# 继承
class Cat(Animal):
# 子类特有的方法
def call(self):
print '喵喵'
fentiao= Cat()
#子类拥有父类的所有属性和方法
fentiao.eat()
fentiao.drink()
fentiao.run()
fentiao.sleep()
fentiao.call()
#子类继承自父类,可以直接享受父类中已经封装好的方法
#子类中应该根据自己的职责,封装子类特有的属性和方法
执行结果:
3.继承的传递性:(爷爷,父亲,儿子)
C类从B类继承,B类从A类继承
那么C类具有A类和B类的所有方法和属性
代码:
class Animal():
def eat(self):
print '吃'
def drink(self):
print '喝'
def run(self):
print '跑'
def sleep(self):
print '睡'
# 继承Animal类
class Cat(Animal):
# 子类拥有父类的所有属性和方法
def call(self):
print '喵喵'
# 继承Cat类
class Hollekitty(Cat):
def speak(self):
print '我可以说日语'
kt = Hollekitty()
# 由继承的传递性知Hollekitty类具有Cat类和Animal类的所有方法和属性
kt.speak()
kt.call()
kt.eat()
kt.drink()
kt.run()
kt.sleep()
执行结果:
(3)多继承:
子类拥有多个父类,并且具有所有父类的属性和方法
例如:孩子会继承自己父亲和母亲的特性
语法格式:
class 子类名(父类名1,父类名2...)
代码1:
class A():
def test(self):
print 'test'
class B():
def kisk(self):
print 'kisk'
# 多继承可以让子类对象,同时具有多个父类的属性和方法
class C(A, B):
pass
c = C()
c.test()
c.kisk()
执行结果:
代码2:
class A():
def test(self):
print 'test'
def kisk(self):
print 'A---kisk'
class B():
def kisk(self):
print 'B---kisk'
# 继承的两个父类具有相同的方法按照继承顺序进行继承
class C(B, A):
pass
c = C()
c.test()
# 当父类A与父类B拥有同名的方法时
# 调用子类的此方法时,会执行放在前边的父类
c.kisk()
执行结果:
(4)重写父类有两种情况:
1.覆盖父类的方法
使用情景:
如果在开发中,父类的方法的实现和子类的方法的实现完全不同
就可以使用覆盖的方法在子类中重写父类的方法
具体实现方式:
在子类中定义一个和父类同名的方法,并且实现重写之后
在运行时,只会调用子类的重写方法,而不会调用父类的重写方法
代码:
class Animal():
def eat(self):
print '吃'
def drink(self):
print '喝'
def run(self):
print '跑'
def sleep(self):
print '睡'
class Cat(Animal):
# 子类拥有父类的所有属性和方法
def call(self):
print '喵喵'
class Hollekitty(Cat):
def speak(self):
print '我可以说日语'
# 覆盖父类的方法
def call(self):
print '欧哈哟,空尼其瓦'
kt = Hollekitty()
kt.call()
执行结果:
2.扩展父类的方法
具体实现方式:
(1)在子类中重写父类的方法(方法名要相同)
(2)在需要的位置使用 父类名.方法名(self) 来调用父类原有的方法
(3)在其他的位置针对子类的需求,编写子类特有的属性和方法
代码:
class Animal():
def eat(self):
print '吃'
def drink(self):
print '喝'
def run(self):
print '跑'
def sleep(self):
print '睡'
class Cat(Animal):
def call(self):
print '喵喵'
class Hollekitty(Cat):
# 子类特有的方法
def speak(self):
print '我可以说日语'
def call(self):
print '欧哈哟,空尼其瓦'
# 调用原本在父类中封装的方法
Cat.call(self)
# 子类特有的功能
print '#@!$%'
kt = Hollekitty()
kt.call()
执行结果:
覆盖与扩展的实例:
代码:
class Bird():
def __init__(self):
self.hungry = True
def eat(self):
if self.hungry:
print 'Enenenen....'
self.hungry = False
else:
print 'No Thanks'
class SongBird(Bird):
def __init__(self):
self.sound = 'Squawk'
# 调用Bird类原有的属性
Bird.__init__(self)
# 子类特有的方法
def sing(self):
print self.sound
bird = Bird()
bird.eat()
littlebird = SongBird()
littlebird.eat()
littlebird.sing()
执行结果:
3.多态
不同的子类对象调用相同的方法,产生不同的执行结果
代码:
class Dog(object):
def __init__(self, name):
self.name = name
def play(self):
print '%s 蹦蹦跳跳的玩耍' % self.name
# 继承
class Xiaotianquan(Dog):
def play(self):
print '%s 飞到天上玩耍' % self.name
# 定义一个人类,让人和狗一起玩耍
class Person(object):
def __init__(self, name):
self.name = name
def person_with_dog(self, dog): # dog为形参
print '%s 和 %s 一起开心的玩耍' % (self.name, dog.name)
dog.play()
# wangcai = Dog('旺财')
wangcai = Xiaotianquan('旺财')
xiaohua = Person('小花')
# wangcai 与 wangcai = Xiaotianquan('旺财') 联系
xiaohua.person_with_dog(wangcai)
执行结果:
2.私有属性和私有方法
应用场景:
在实际开发中,对象的某些属性或方法只希望在对象内部使用,而不希望在对象外部使用
私有属性就是对象不希望公开的属性;私有方法就是方法不希望公开的方法
定义方法:
在定义属性或方法时,在属性或方法前面加两个下划线,定义的就是私有属性和私有方法
代码:
class Women():
def __init__(self, name):
self.name = name
self.__age = 18
def __secret(self):
print '%s 的年龄是 %d' % (self.name, self.__age)
# 创建对象
lily = Women('lily')
# 在外界,无法直接访问私有属性
print lily.age
# 在外界,无法直接访问私有方法
lily.secret()
# 程序会报错无法访问
执行结果:
3.父类的私有属性和私有方法
私有属性:私有属性是对象的隐私,不对外公开,外界以及子类都不能直接访问
私有方法:私有方法常用做一些内部的事情
注意:
1.子类对象不能在自己的方法内部,直接访问父类的私有属性和私有方法
2.子类对象不能在外部直接访问父类的私有属性和私有方法
解决方案:
子类对象可以通过父类的公有方法间接访问到私有属性和私有方法
代码:
class A(object):
def __init__(self):
self.num1 = 1
# 初始化方法创建私有属性
self.__num2 = 2
# 创建私有方法
def __test(self):
print '私有方法 %d %d' % (self.num1,self.__num2)
# 要想使子类访问到父类的私有属性和私有方法
# 需要创建一个公有方法
def test(self):
# 访问私有属性
print '%d' % self.__num2
# 访问私有方法
self.__test()
class B(A):
def demo(self):
# 在子类的方法中,不能访问父类的私有属性
print '访问父类的私有属性 %d'% self.__num2
# 在子类的方法中,不能访问父类的私有方法
self.__test()
#创建子类对象
b = B()
print b
# 无法访问父类的私有属性
# print b.__num2
# 无法调用父类的私有方法
# b.test
b.test()
执行结果:
4.类的结构:
实例:
1.使用面向对象开发,第一步是设计类
2.使用类名()创建对象,创建对象的动作有两步
1.在内存中为对象分配空间
2.调用__init__初始化方法为对象初始化
3.对象创建后,内存中就有了一个对象的实实在在的存在--->实例
因此:
1.创建出来的对象叫做类的实例
2.创建对象的动作叫做实例化
3.对象的属性叫做实例属性
4.对象调用的方法叫做实例方法
在程序执行时:
1.对象各自调用自己的实例属性
2.调用对象的方法,可以通过self
访问自己的属性
调用自己的方法
结论:
1.每一个对象有自己独立的内存空间,保存各自不同的属性
2.多个对象的方法,在内存中只有一份,在调用方法时,需要把对象的传递到方法内部
(1)类属性
类是一个特殊的对象
python中的所有对象
class A: 定义的类属性属于类对象
obj1 = A: 属于实例对象
在运行程序时,类同样会被加载到内存,在python中,类是一个特殊的对象 -- 类对象
除了封装 实例(对象)的属性和方法外,类对象还可以有自己的属性
通过 类名.属性名/方法名 的方式直接访问类的属性或者方法
代码:
"""使用附值语句定义类属性,记录所有工具的数量"""
class Tool(object):
count = 0 # count 为 类属性
def __init__(self, name):
self.name = name # name 为 实例属性
# 计数
Tool.count += 1
# 创建工具对象时,会自动调用初始化方法
# 故创建一个工具便会自加1,达到计数的目的
tool1 = Tool('斧头')
tool2 = Tool('榔头')
tool3 = Tool('梯子')
# 输出工具对象的数目
# 必须是 类名.属性名
print Tool.count
执行结果:
(2)类方法
类属性就是针对类对象定义的方法
在类方法内部就可以直接访问类属性或者调用其他类方法
语法结构:
@classmethod
def 类方法名(cls):
pass
通过类名.类方法名 来调用
代码:
""" 利用类方法,记录所有玩具的数量 """
class Toy(object):
count = 0
# 类方法
@classmethod
def show_toy_count(cls):
# 在类方法的内部,访问当前的类属性
print '玩具对象的数量为: %d' % cls.count
def __init__(self, name):
self.name = name
Toy.count += 1
# 创建类对象
toy1 = Toy('乐高')
toy2 = Toy('小珠佩琦')
toy3 = Toy('叮当猫')
# 在方法的内部,可以直接访问类属性
# 调用 类对象
Toy.show_toy_count()
执行结果:
(3)静态方法
在开发时,如果需要在类中封装一个方法,这个方法既不需要访问实例属性或者调用实例方法
也不要需要访问类属性或者调用类方法,此时便可以把这个方法封装成一个静态方法
语法结构:
@staticmethod
def 静态方法名():
pass
静态方法需要修饰器@staticmethod来标识,告诉解释器这是一个静态方法
通过类名.静态方法名 来调用
代码:
class Cat(object):
# 静态方法不需要传递参数self
@staticmethod
def call():
print '小猫喵喵叫~'
# 通过类名.静态方法名 调用
# 不需要创建对象,可以直接使用
Cat.call()
执行结果:
实例:
需求
1.设计一个Game类
(1)属性:
记录游戏的历史最高分(类属性,因为它只与游戏类有关,而与游戏对象无关)
记录当前游戏玩家的玩家姓名(实例属性)
(2)方法:
显示游戏帮助信息(静态方法)
显示历史最高分(类方法)
开始当前玩家的游戏(实例方法)
2.主程序
查看那帮助信息
查看历史最高分
创建游戏对象,开始游戏
代码:
class Game(object):
# 类属性
top_scorce = 103
# 实例属性
def __init__(self,name):
self.name = name
# 静态方法
@staticmethod
def show_help():
print '帮助手册'
# 类方法
@classmethod
def show_top_scorce(cls):
print '历史最高分为: %d ' % cls.top_scorce
# 实例方法
def start_game(self):
print '%s 开始游戏!' % self.name
# 查看游戏帮助信息
Game.show_help()
# 查看历史最高分
Game.show_top_scorce()
# 创建类对象,开始游戏
lily = Game('lily')
lily.start_game()
执行结果:
案例小结:
1.实例方法:方法内部需要访问实例属性
2.类方法: 方法内部只需要访问类属性
3.静态方法:方法内部不需要访问实例属性和类属性
如果方法内部,既要访问实例属性,又要访问类属性,应该定义实例方法
5.设计模型
需求:模拟播放器 一个播放器同一时刻只能播放一首歌曲!
代码1:(不会自动执行初始化)
class MusicPlayer(object):
# 在创建对象时,new方法会自动被调用,new方法为对象分配内存空间
# 重写new方法(覆盖)
def __new__(cls, *args, **kwargs):
# cls 表示哪一个类调用就传递哪一个类
# *args 表示多值参数
# **kwargs 表示多值的字典参数
print '创建对象,分配空间'
# 创建对象时 会自动执行初始化方法
# 实际上 在创建对象时 执行初始化之前会
# 自动执行原本的new方法对象分配内存空间
def __init__(self):
print '播放器初始化'
player = MusicPlayer()
print player
""" 以覆盖的形式重写new方法后,创建类对象时便不会给它分配内存空间,
故此时便无法自动执行初始化方法"""
执行结果:
代码2:(可以自动执行初始化,但会因对象不同,而使其分配的内存地址不同)
class MusicPlayer(object):
# 扩充new方法
def __new__(cls, *args, **kwargs):
# 1.创建对象 new方法会自动被调用
print '创建对象,分配空间'
# 2.为对象分配空间
# 调用object基类原本的new方法,并将其赋值给instance
instance = object.__new__(cls)
# 3. 返回对象引用(地址)
return instance
def __init__(self):
print '播放器初始化'
player1 = MusicPlayer()
print player1
player2 = MusicPlayer()
print player2
""" 以扩充的方式重写new方法,创建类对象时便不会影响自动执行初始化方法"""
执行结果:
代码3:(虽对象不同但分配的内存地址仍然相同,但初始化会执行两次)
class MusicPlayer(object):
instance = None
# 创建对象时 new方法会自动被调用
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = object.__new__(cls)
return cls.instance
def __init__(self):
print '播放器初始化'
player1 = MusicPlayer()
print player1
player2 = MusicPlayer()
print player2
执行结果:
代码4:(初始化只执行1次)
class MusicPlayer(object):
instance = None
init_flag = False
# 创建对象 new方法会自动被调用
def __new__(cls, *args, **kwargs):
if cls.instance is None:
cls.instance = object.__new__(cls)
return cls.instan
def __init__(self):
if MusicPlayer.init_flag:
return
print '播放器初始化'
MusicPlayer.init_flag = True
player1 = MusicPlayer()
print player1
player2 = MusicPlayer()
print player2
执行结果: