面向对象基本特征
封装
封装是指对外界隐藏信息,不能随意访问、修改对象的数据和方法
封装是通过限制类的属性和方法的访问方式来实现的封装效果
封装的三个层次:
- 类的封装:外部可以任意访问、修改类中的属性和方法
- 私有属性:外部不可以访问、修改勒种的属性和方法
- 公有方法+私有属性:外部有条件限制的访问、修改属性,调用方法
封装表现1:
类的定义:将某些特定属性和方法进行“隔离”
每个学生有自己的年龄,外部可以任意读取或者修改
class Student:
def __init__(self,name,age):
self.name = name
self.age = age
s1 = Student("李白",18)
s2 = Student("杜甫",20)
s2.age = 200
print(s1.name)
print(s2.age)
封装表现2:
属性私有:只能在类的内部使用,外部不能使用
不让外部读取、修改学生的年龄
class Student:
def __init__(self,name,age):
self.name = name
self.__age = age #使用两个下划线开头的属性,定义为私有属性
print(self.__age)
__secret = True
s1 = Student("李白",18)
# print(s1.__age) #外界无法直接通过属性名称来访问
#虽然语法可以在外部通过_Student__age访问到私有属性,但是不推荐使用
print(s1._Student__age)
#__dict__查看当前对象或者类有哪些属性
print(s1.__dict__)
print(Student.__dict__)
封装表现3:
私有属性+ 公有方法:有限制条件的开放给外部
可以读取年龄,但不能随意修改年龄
设定年龄必须在(1,125)之间
class Student:
def __init__(self,name,age):
self.name = name
self.__age = age
def getAge(self):
return self.__age
def setAge(self,age):
if 0 < age < 125:
self.__age = age
else:
print("年龄不符合人类")
s1 = Student("李白",18)
print(s1.getAge())
s1.setAge(200)
print(s1.getAge())
私有方法
可以在类的内部使用,有条件的开放给外部
class Person:
def __tellSercert__(self):
print("夏天夏天悄悄过去,留下小秘密~~~~")
def talk(self,object):
if object == "好基友":
self.__tellSercert__()
else:
print("我是一个木得感情木得秘密的人")
ming = Person()
ming.talk("好基友")
ming.talk("隔壁小黑")
封装的简化写法
装饰器
property装饰器:把一个方法伪装成一个属性,在调用这个方法的时候不需要加()就可以直接得到返回值
class Dog:
@property #装饰器
def bark(self):
print("旺旺旺~~~")
return 89757
d = Dog()
num = d.bark #注意这里没有括号呦
print(num)
总结
使用@property装饰器时,方法名不必与属性名相同
可以更好的防止外部通过猜测私有属性名称来访问
凡是赋值语句就会触发set方法,获取属性值会触发get方法
继承
面临的问题:
- 相似类型的定义会产生大量重复的代码
- 类的定义没有很好的扩展性‘
解决的方法:
定义类和类之间的关系,子类拥有父类的全部属性和方法,并可以为之扩充
示例
class Pet:
def __init__(self,name,age):
self.name = name
self.age = age
print(f"一只名字叫{self.name}的宠物出生了")
def eat(self):
print(f"{self.name}在吃东西~~~")
def run(self):
print(f"{self.name}在溜达~~~")
class Dog(Pet):
pass
class Cat(Pet):
pass
d = Dog("咯咯",3)
d.run()
c = Cat("布丁",1)
c.eat()
1.继承的概念:
- Dog和Cat继承了Pet类
- Dog和Cat称为Pet的子类、派生类
- Pet称为Dog和Cat的父类、超类、基类
- 子类默认拥有父类公有的属性和方法的定义
2.子类调用方法的顺序
- 调用方法时,子类中有的,调用子类中的
- 子类中没有的,调用父类的
- 一旦在子类中找到,就不在去父类中查找了
子类不能继承父类的私有属性或者方法
class Father:
__secret = "小秘密"
story = "从前有座山,山上有座庙"
def tellStory(self):
print(self.story)
def __tellSecret(self):
print(self.__secret)
class Son(Father):
def tell(self):
# self.tellStory()
self.__tellSecret()
s = Son()
s.tell()
3.重写/覆盖
- 子类和父类拥有相同名称的方法
- 可以理解为子类对于父类行为的扩展和补充
class Pet:
master = True
def __init__(self,name,age):
self.name = name
self.age = age
print(f"一只名字叫{self.name}的宠物出生了")
def eat(self):
print(f"{self.name}在吃东西~~~")
def run(self):
print(f"{self.name}在溜达~~~")
class Dog(Pet):
#重写父类方法
def eat(self):
print(f"{self.name}在啃骨头")
#扩充,定义子类特有功能
def lookAfter(self):
print(f"{self.name}在看门,汪汪汪!")
d = Dog("旺财",2)
d.eat()
d.lookAfter()
4.子类可以在类定义时,可以使用super()调用父类的方法
应用1:和重写不同,重写是对父类方法的完全覆盖,这是对父类方法的补充
class Pet:
def __init__(self,name,age):
self.name = name
self.age = age
print(f"一只名字叫{self.name}的宠物出生了")
def eat(self):
print(f"{self.name}在吃东西~~~")
def run(self):
print(f"{self.name}在溜达~~~")
class Cat(Pet):
def eat(self):
print(f"{self.name}伸了个懒腰")
super().eat()
print(f"{self.name}吃完东西后,舔了舔爪子")
c = Cat("布丁",2)
c.eat()
应用2:对象初始化时,简化重复属性的赋值
class Cat(Pet):
def __init__(self,name,age,sex):
# self.name = name
# print(f"一只名叫{self.name}的宠物出生了")
super().__init__(name) #代替了上两句代码,简化了重复属性的赋值
self.age = age
self.sex = sex
5.多继承
- 子类可以有多个父类
- 例如:狗是脊椎动物,哺乳动物,宠物。。。
class Father:
caihua = "有才"
class Mother:
yanzhi = "有貌"
class Child(Father,Mother):
pass
c = Child()
print(c.caihua,c.yanzhi)
print(Child.__bases__) #可以通过类名.__bases__查看其父类
6.访问子类的属性或者方法时,解析的路径(顺序)
class Father:
def getMoney(self):
print("爸爸给了零花钱")
class Mother:
def getMoney(self):
print("妈妈给了零花钱")
class Child(Father,Mother):
def getMoney(self):
super().getMoney()
#按照继承的顺序查找父类中的方法,所以先找Father类
print("孩子获得了零花钱")
c = Child()
c.getMoney()
从第一个父类开始向上查找,直到查找结束都没有,再对第二个父类进行查找
深度优先:Child -> Father -> GrandFather -> Mother
class GrandFather:
pass
# def getMoney(self):
# print("爷爷给了零花钱")
class Father(GrandFather):
pass
# def getMoney(self):
# print("爸爸给了零花钱")
class Mother:
def getMoney(self):
print("妈妈给了零花钱")
class Child(Father,Mother):
def getMoney(self):
super().getMoney()
#按照继承的顺序查找父类中的方法,所以先找Father类
print("孩子获得了零花钱")
c = Child()
c.getMoney()
print(Child.mro()) #可以通过类名.mro()的方式来查看类的解析顺序
- object是所有类的父类
- “万事万物”皆为对象,因为所有的类都默认继承object
7.菱形继承
广度优先:Child -> Father -> Mother -> Human
多态
想要解决的问题
- 让程序能够有更强的灵活性
- 让程序能够有更好的适应性
- 概念:一个类的多种形态
情况1:通过继承和重写来实现
class Animal:
def eating(self):
print("动物在吃东西")
class Pet(Animal):
def eating(self):
print("宠物在吃东西")
class Dog(Pet):
def eating(self):
print("狗在啃骨头~")
class Cat(Pet):
def eating(self):
print("小猫在吃鱼")
class Zoo:
def animalEating(self,animal):
animal.eating()
z = Zoo()
a = Animal()
p = Pet()
d = Dog()
c = Cat()
#Dog和Cat都作为Animal的不同形态
#都可以直接调用animal所具有的属性和方法
z.animalEating(a)
z.animalEating(p)
z.animalEating(d)
z.animalEating(c)
情况2:Python持有
没有继承关系,但是也具备相同的特征、方法,也可以直接使用
class venusFlytrap: #捕蝇草
def eating(self):
print("捕蝇草在吃小虫子")
v = venusFlytrap()
z.animalEating(v)
捕蝇草不继承于Animal类,但是具有和它相同的特征,在python只要具有相同的特征和方法,没有继承关系也可以直接使用
type和isinstance
#type判断类型时,只看直接类型
print(type(c) is Cat) #True
print(type(c) is Animal) #False
print(type(c) is Pet) #False
#isinstance 判断对象是否为一个类型的实例
#判断实例类型时,涵盖父类的类型
print(isinstance(c,Cat)) #True
print(isinstance(c,Pet)) #True
print(isinstance(c,Animal)) #True
print(isinstance(venusFlytrap,Animal)) #False