16 - 面向对象的三大特征
一.封装性
1.封装性的作用
- 在实际开发中,对象的某些属性或者方法可能只希望在对象的内部别使用,而不希望在外部被访问到,这时就可以定义私有属性和私有方法。或者不希望外界随便改和查看,那么可以定义get、set方法,将一些限制条件加到方法中,外界传参如果不满足方法中的条件不赋值,满足条件才可修改
2.定义私有属性和私有方法
-
在定义属性或方法时,在属性名或者方法名前增加两个下划线
__,定义的就是私有属性或方法。class Person: def __init__(self,name,age): self.name = name self.age = age self.__money = 2000 #私有属性 def __shopping(self, cost): #私有方法 self.__money -= cost # __money 只能在对象内部使用 print('还剩下%d元'%self.__money) def test(self): self.__shopping(200) # __shopping 方法也只能在对象内部使用 p = Person('张三',18) # print(p.__money) 这里会报错,不能直接访问对象内部的私有属性 p.test() # p.__shopping() 这里会报错,__shopping 只能在对象内部使用,外部无法访问
3.定义方法来访问和修改私有变量
-
使用定义
get和set方法这种方式来实现class Person: def __init__(self,name,age): self.name = name self.age = age self.__money = 2000 # __money 是私有变量,外部无法访问 def get_money(self): # 定义了get_money 方法,在这个方法里获取到 __money return self.__money def set_money(self,money): # 定义了set_money 方法,在这个方法里,可以修改 __money if money < 0: print('不行!') else: self.__money = money p = Person('王五',21) # 外部通过调用 get_money 和 set_money 这两个公开方法获取和修改私有变量 print(p.get_money()) p.set_money(8000) print(p.get_money())
4.直接访问私有变量
-
使用方式:在私有属性名或方法名前添加
_类名class Person: def __init__(self,name,age): self.name = name self.age = age self.__money = 2000 def __shopping(self, cost): self.__money -= cost p = Person('李四',20) print(p._Person__money) # 使用对象名._类名__私有属性名 可以直接访问对象的私有属性 p._Person__shopping(100) # 使用对象名._类名__函数名 可以直接调用对象的私有方法 print(p._Person__money)
在开发中,我们强烈不建议使用
对象名._类名__私有属性名的方式来访问对象的私有属性!
二.property装饰器
-
使用场景
我们在类中定义get和set方法来访问和修改私有变量,使用时却每次都要
对象名.get()或对象名.set(),那么现在想要用访问和修改普通类的变量的方式对象.属性或对象.属性 = 值一样的方式来对私有变量进行操作,但是实际上还是使用get和set方法,只是调用方式变了。就可以在类中定义get和set的上面加一个property装饰器class Person: def __init__(self, name): self.name = name self.__money = 2000 @property def getMoney(self): return self.__money @property def setMoney(self,money): if self.__money <= 0: print('输入错误') else: self.__money = money p = Person('zjj') p.money = 1000 print(p.money)最常用的定义方式:
class Person: def __init__(self, name): self.name = name self.__money = 2000 @property #先定义get def money(self): return self.__money @money.setter #这里就是set方法要依赖于get方法,所有要用money.setter来装饰 def money(self,money): if self.__money <= 0: print('输入错误') else: self.__money = money p = Person('zjj') p.money = 1000 print(p.money)
三.继承性
1.继承的作用
- 继承可以使得子类具有父类的属性和方法或者重新定义、追加属性和方法等。
- 在程序中,继承描述的是多个类之间的所属关系。(is a)
2.继承格式
- class 类名(继承的父类)
class Animal:
def __int__(self):
pass
"""动物类"""
def sleep(self):
print('正在睡觉')
class Cat(Animal): # 定义类时,在括号后面传入父类的类名,表示子类继承父类
"""Cat类继承自Animal类"""
def __int__(self):
pass
#Cat继承自Animal类,可以直接使用Animal类里的sleep方法
cat = Cat()
cat.sleep()
3.继承的注意点
1)super关键字及__init__和__new__的说明
-
super()表示父类对象,然后
super().属性或方法得到父类的属性和方法 -
为什么要有super:
当父类中定义了
__init__方法,而子类中也定义了__init__方法,如果不使用super虽然不会报错,但是就体现不了继承性了,因为创建子类对象时会自动执行子类中的__init__方法,而不会去执行父类中的__init__,那么父类的一些在init方法中定义的属性就无法加载到子类。 -
super使用说明
-
如果子类中不定义
__init__,则会自动去调用父类中的__init__,将父类init中声明的属性加载到子类的空间中 -
如果子类中定义了
__init__- 不使用super,则调用自己的init方法,而不去调用父类的init,那么父类init中声明的属性就得不到
- 使用super,则会调用自己的init时会去调用父类中的init,把父类init方法中声明的属性加载到子类空间中
但是注意:子类只要继承父类那么就可以使用父类中的类属性(可以使用没有定义到方法中的属性,如下方的变量n)和父类中的非私有方法。原理就是当子类空间中没找到时会去父类中找。
class Person: n = 100 def __init__(self): self._name = 'zjj' def getName(self): return self._name def eat(self): print('{}吃!'.format(self)) def __str__(self): return '人' class Student(Person): def __init__(self): self._id = 2019 def study(self): print('学生学习!') def __str__(self): return '学生' class Teacher(Person): pass p = Person() s = Student() print(s.n) #100 p.eat() #人吃 s.eat() #学生吃 #s.getName() #会报错,因为Student类中已经自己定义了__init__方法,那么在创建对象的时候就自动执行了此方法,把要加载的属性加载到内存中而此时此方法中没有super关键字来调用父类中的__init__方法,所以父类中的属性不会加载到子类空间中去,那么Student类中就没有name属性,而getName需要使用name属性所以会报错。 t = Teacher() print(t.getName()) #如果Teacher类中没有写init方法,那么先看看自己类中是否有此方法,没有则在父类中找,没有则继续向父类的父类找,最后没有就报错
- 使用super时说明:
class Person: def __init__(self,name): self._name = name def getName(self): return self._name def eat(self): print('{}吃!'.format(self)) class Student(Person): def __init__(self,numid,name): self._id = numid super().__init__(name) #父类中init方法中有参数就要传参 #或者使用super(Student,self).__init__(name) 这里使用的是带参数的super多了一部判断self是否是Student类型的过程 s = Student(2019,'zjj') print(s.getName()) #zjj -
还可以使用
父类名.__init__(self)来调用父类的init方法,但是注意这样只会调父类名只一个init()方法,而super()会调用所有的父类的init方法,可以解决多继承问题,会一次性的执行所有的父类的对应方法
2)子类重写父类的方法
-
子类中和父类中有同名的方法,那么调用此方法,优先执行子类中的方法。
-
父类提供的方法不能满足子类的需求,就可以在子类中定义一个同名的方法,实现更多的功能,就叫重写
3)多继承
-
python中一个类可以继承多个类
-
格式:
class 子类(父类1,父类2...): -
多继承的搜索顺序
新式类:广度优先。以
object为基类的类,推荐使用经典类:从左至右,深度优先。不以object为基类的类,不推荐使用
在 Python3.x 以后定义类时,如果没有指定父类,这个类会默认继承自 object,所以,python3.x版本定义的类都是新式类。在Python2.x中定义类时,如果没有指定父类,则不会继承自object.
-
__mro__:Python中针对类提供了一个内置属性__mro__可以用来查看方法的搜索顺序。主要用于在多继承时判断方法属性的调用顺序class P1: #默认以object为基类 def foo(self): print('P1------foo') def bar(self): print('p1------bar') class P2: def foo(self): print('P2------foo') class C1(P1,P2): pass class C2(P1,P2): def bar(self): print('C2------bar') class D(C1,C2): pass d = D() d.foo() #P1------foo d.bar() #C2------bar print(D.__mro__) #(<class '__main__.D'>, <class '__main__.C1'>, <class '__main__.C2'>, <class '__main__.P1'>, <class '__main__.P2'>, <class 'object'>)图示:

四.多态
-
python没有严格意义上的多态,因为在java中多态是:子类的对象赋给父类的引用----也就是说现在两个类有继承关系,如果一个方法的参数为一个类类型,那么可以传该类类型的变量进去,也可以传该类的子类的类型变量进去。只能是该类或该类的子类。但是python中,一个参数类型假设我要传一个类类型变量,那么只要是类类型都可以往里传,不需要一定满足是它的子类才能传。
isinstance(obj,类名)--> 判断obj是不是类的对象或者判断obj是不是该类的子类对象 -
举例:
class Person: def __init__(self,name): self.name = name def feed_pet(self,pet): #这个pet可以接收Pet类的子类,还可以接收其他类的变量 #isinstance(obj,类名) --> 判断obj是不是类的对象或者判断obj是不是该类的子类对象 if isinstance(pet,Pet): print('{}养的是宠物是{},昵称叫{}'.format(self.name,pet.kind,pet.name)) else: print('不能养野兽') class Pet: def __init__(self,name): self.name = name class Cat(Pet): kind = '修猫' def __init__(self,name): super(Cat, self).__init__(name) class Dog(Pet): kind = '修狗' def __init__(self,name): super(Dog, self).__init__(name) class Tiger: kind = '修脑虎' def __init__(self,name): self.name = name c = Cat('富贵') d = Dog('大黄') t = Tiger('吼吼') p = Person('zjj') p.feed_pet(c) p.feed_pet(d) p.feed_pet(t)

浙公网安备 33010602011771号