1、封装
封装是面向对象编程的一大特点,将属性和方法封装到一个抽象的类中
数据被保存在内部,程序的其他部分只有通过被授权的操作(成员方法)才能对数据进行操作。外界使用类创建对象,然后让对象调用方法
通过在实例上调用方法,我们就直接操作了对象内部的数据,但无需知道方法内部的实现细节。对象方法的细节都被封装在类的内部
——————- 练习——————
需求
1.小明体重75.0公斤
2.小明每次跑步都会减肥0.5公斤
3.小明每次吃东西体重都会增加1公斤
需求
1.小明和小美都爱跑步
2.小美体重45.0公斤
2.每次跑步都会减肥0.5公斤
3.每次吃东西体重都会增加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(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)
xiaomei = Person('小美',45.0)
xiaomei.eat()
print(xiaomei)
——————- 练习——————
根据学生的成绩得到成绩的等级
class Student:
def __init__(self,name,score):
#初始化方法,当使用类创建实例的时候都会调该方法
self.name = name
self.score = score
def get_grade(self):
#对数据封装
if self.score > 90:
print('A')
elif self.score >80:
print('B')
else:
print('C')
toto = Student('toto',95) # 创建对象
toto.get_grade() # 直接使用对象调用方法,得到结果
#输出结果:
A
一个对象的属性 可以是另一个对象创建的类
——————- 练习——————
需求:
1.房子有户型,总面积和家具名称列表
新房子是没有家具的
2.家具有名字和占地面积,其中
床:5
桌子:4
椅子:6
3.将以上三件家具添加到房子中
4.打印房子的时候,要求输出:户型 总面积 剩余面积 家具名称列表
class Furniture: # 限定义家具类
def __init__(self,name,area):
self.name = name
self.area = area
def __str__(self):
return 'this is %s, %d square '
#使用类创建好对象之后,该对象可以作为参数用于其他函数调用。
class House: #在定义房子类
def __init__(self,type,squa):
self.fu_list = []
self.type = type
self.leftsqua = self.allsqua = squa
def __str__(self):
return ' 户型是: %s\n 总面积是: %.2f\n 剩余面积: %.2f\n 家具列表:%s' \
%(self.type,self.allsqua,self.leftsqua,self.fu_list)
def add_furn(self,item): # 在房子类的方法中使用的参数是家具类的一个实例
if item.area<self.allsqua:
self.fu_list.append(item.name)
self.leftsqua -=item.area
else:
return
bed = Furniture('bed',5)
desk = Furniture('desk',4)
chair = Furniture('cabinet',6) # 先实例化 家具
house1 = House('三室',120) # 在实例化房子
house1.add_furn(bed) # 再使用房子对象的方法,传入的参赛是家具实例
house1.add_furn(desk)
house1.add_furn(chair)
print(house1)
#输出结果:
户型是: 三室
总面积是: 120.00
剩余面积: 105.00
家具列表:['bed', 'desk', 'cabinet']
——————- 练习——————
1.士兵瑞恩有一把AK47
2.士兵可以开火(士兵开火扣动的是扳机)
3.枪 能够 发射子弹(把子弹发射出去)
4.枪 能够 装填子弹 –增加子弹的数量
class Gun: # 定义枪类
def __init__(self,name):
self.name = name
self.count = 3 # 属性:枪名称以及子弹数量
def add_bullet(self): # 方法:添加子弹,将子弹数充值3
self.count = 3
def launch_bullet(self): # 发射子弹
if self.count <= 0:
self.add_bullet() #如果没有子弹,先进行添加子弹
self.count -= 1 # 然后子弹减少一个
print('%s 已经成功发射子弹 剩余子弹%d' %(self.name,self.count))
class Solair: # 定义士兵类
def __init__(self,name):
self.name = name # 设置属性 :name
def shoot(self,gun): # 定义方法:直接调用枪对象的方法
gun.launch_bullet()
AK47 = Gun('AK47') # 实例化一个枪对象
ryan = Solair('Ryan') #实例化一个士兵对象
ryan.shoot(AK47) # 士兵对象调用开火方法
ryan.shoot(AK47)
ryan.shoot(AK47)
ryan.shoot(AK47)
#结果:
AK47 已经成功发射子弹 剩余子弹2
AK47 已经成功发射子弹 剩余子弹1
AK47 已经成功发射子弹 剩余子弹0
AK47 已经成功发射子弹 剩余子弹2
2、继承
在OOP程序设计中,当我们定义一个class的时候,可以从某个现有的class继承,新的class称为子类(Subclass),而被继承的class称为基类、父类或超类(Base class、Super class)
最大的好处是子类获得了父类的全部功能,实现代码的重用,相同的代码不需要重复的写。
class Animal(): #定义动物类
def run(self):
print('running~~')
def call(self):
print('hahahah') # 动物类中存在两个方法
class Cat(Animal): # 定义猫类,继承动物类
pass
tom = Cat() # 常见一个猫类实例
tom.run() # 该实例可以直接调用动物类的方法
#输出结果:
running~~
可以在子类中添加自己特定功能的一些方法
class Animal():
def run(self):
print('running~~')
def call(self):
print('hahahah')
class Cat(Animal):
def eat(self):
print('爱吃鱼')
tom = Cat()
tom.run() # 调用父类的方法
tom.eat() # 使用自己类的方法
#输出结果:
running~~
爱吃鱼
当父类方法的实现不能满虚子类的需求的时候可以对方法进行重写,我们说,子类的方法覆盖了父类的方法,在代码运行的时候,总是会调用子类的该方法。
class Animal():
def run(self):
print('running~~') #父类中存在run方法
def call(self):
print('hahahah')
class Cat(Animal):
def run(self):
print('跑得快') # 子类中也存在run方法
def eat(self):
print('爱吃鱼')
tom = Cat()
tom.run() # 当实例调用run方法的时候,总是调用的子类中的程序
#运行结果:
跑得快
可以对父类方法进行扩展。保留父类方法的内容,在其基础上增加新的内容。只需要子子类中定义和父类中相同的方法,并且在该方法中写入super().call()(父类.方法(self) python2.x通过个该实现),然后添加需要扩展的内容即可。
class Animal():
def run(self):
print('running~~')
def call(self):
print('hahahah')
class Cat(Animal):
def run(self):
super().run() # 继承父类给方法的内容,同时添加扩展的内容
print('跑得快')
def eat(self):
print('爱吃鱼')
tom = Cat()
tom.run()
#输出结果:
running~~
跑得快
当父类中使用了初始化方法,在子类中也想使用初始化方法的时候,需要使用在子类初始化方法中使用super().init(),表示对父类初始化方法的继承扩展,如若没有,会出现冲突报错。
class Animal():
def __init__(self,name):
self.name = name
def run(self):
print('running~~')
def call(self):
print('hahahah')
class Cat(Animal):
def __init__(self,color,name):
super().__init__(name) #在子类中必须使用该语句,表示继承父类中的属性。
self.color = color
def run(self):
super().run()
print('跑得快')
def eat(self):
print('爱吃鱼')
tom = Cat('tom','red')
print(tom.name)
print(tom.color)
#输出结果:
red
tom
当子类继承子多个父类的时候,并且多个父类中存在相同的方法,这时候该子类的实例取调用这个方法的时候,就会出现那个被继承的父类写在前面,就使用哪一个父类的方法。建议在定义方法的时候,尽量不要使用相同的名称
class A: #定义A 类
def lala(self):
print('lala')
def toto(self):
print('toto') # 其中由来两个方法
class B: # 定义B类
def lala(self):
print('LALA')
def toto(self):
print('TOTO') # 其中由两个方法,并且方法名称和A中方法名称一致
class C(A,B): #定义类C 继承A,B 两个类
pass
class D(B,A): # 定义类D 继承B,A 两个类
pass
c = C()
d = D()
c.lala()
d.lala()
c.toto()
d.toto() # 当继承父类的顺序不同的时候,导致调用的方法是不同的内容,默认调用位置靠前的父类方法
#输出结果:
lala
LALA
toto
TOTO
3、多态
以封装和继承为前提,存在一个函数,需要一个参数,不用对函数进行修改当参数是不同的对象的时候输出的结果不同,
就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。
不同的子类对象调用相同的方法,产生不同的执行结果
首先要对数据类型再作一点说明。当我们定义一个class的时候,我们实际上就定义了一种数据类型。我们定义的数据类型和Python自带的数据类型,比如str、list、dict没什么两样 class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
class Cat(Animal):
pass
a = list()
b = Animal()
c = Dog()
d = Cat()
print(isinstance(a,list))
print(isinstance(b,Animal))
print(isinstance(c,Dog))
print(isinstance(d,Cat))
#输出结果:
True
True
True
True
同时子类实例化出来的对象,不仅是子类类型,同时还是父类类型。在继承关系中,如果一个实例的数据类型是某个子类,那它的数据类型也可以被看做是父类。例如Animal是Dog的父类,使用Dog实例化一个实例,该实例既是Dog类型,也是Animal类型。
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
pass
b = Animal()
c = Dog()
print(isinstance(b,Animal))
print(isinstance(c,Dog))
print(isinstance(c,Animal))
#输出结果:
True
True
True
首先存在三个类:Animal、Dog、Cat;Dog、Cat 都继承自Animal类,并且都存在run方法。
class Animal(object):
def run(self):
print('Animal is running...')
class Dog(Animal):
def run(self):
print('Dog is running')
class Cat(Animal):
def run(self):
print('Cat is running')
理解多态的好处,我们还需要再编写一个函数,这个函数接受一个Animal类型的变量:
def run_twice(animal):
animal.run()
animal.run()
当我们传入Animal类的实例时,run_twice()就打印出:
def run_twice(animal):
animal.run()
animal.run()
run_twice(Animal())
#输出结果:
Animal is running...
Animal is running...
由于Dog类的实例以及Cat类的实例都可以是Animal数据类型,同样也可以作为参数传入该函数,当传入Dog类的实例以及Cat类的实例时候,函数的输出:
def run_twice(animal):
animal.run()
animal.run()
run_twice(Dog())
run_twice(Cat())
#输出结果:
Dog is running
Dog is running
Cat is running
Cat is running
现在可以在定义一个以Animal作为父类的子类,并将其实例作为参数,函数run_twice可以正常的运行。
class Tortoise(Animal):
def run(self):
print('Tortoise is running slowly~~~')
run_twice(Tortoise())
#输出结果:
Tortoise is running slowly~~~
Tortoise is running slowly~~~
重点:
新增一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,
函数需要一个Animal类型的参数,当我们需要传入Dog、Cat、Tortoise……时,我们只需要接收其Animal类型就可以了,因为Dog、Cat、Tortoise……都是Animal类型,然后,按照Animal类型进行操作即可。函数能够正常的运行。
由于Animal类型有run()方法,因此,传入的任意类型,只要是Animal类或者子类,就会自动调用实际类型的run()方法,当然,每一个实际类型的run方法内容以及操作的数据都不相同,操作的结果也就相同,这就是多态。
对于一个变量,我们只需要知道它是Animal类型,无需确切地知道它的子类型,就可以放心地调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat还是Tortoise对象上,由运行时该对象的确切类型决定,
调用方只管调用,不管细节,而当我们新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的。这就是著名的“开闭”原则:
对扩展开放:允许新增Animal子类
对修改封闭:不需要修改依赖Animal类型的run_twice()等函数。