DAY19 面向对象三大特性之多态、封装
面向对象三大特性之:多态
多态:指的是一类事物有多种形态。
例如:动物具有多种形态。
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之一:狗 def talk(self): print('barking') class Cat(Animal): #动物的形态之一:猫 def talk(self): print('miao')
多态性
什么是多态动态绑定?(在继承的背景下使用,也称为多态性。)
在面向对象方法中一般这样表述多态性:
-----> 向不同的对象发送同一条消息,不同的对象在接受时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数。不同的行为就是指不同的实现,即执行不同的函数。
-----> 换句人话说,就是在子类和父类中有同名的方法,接受相同的方法调用,都执行自己的同名方法,表现出不一样的行为。
import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): print('say hello') class Dog(Animal): #动物的形态之一:狗 def talk(self): print('barking') class Cat(Animal): #动物的形态之一:猫 def talk(self): print('miao') p = People() d = Dog() c = Cat() p.talk() d.talk() c.talk() >>> say hello barking miao
鸭子类型(duck Typing)
在程序设计中,鸭子类型是一种动态类型的一种风格。python崇尚鸭子类型,即“如果看起来像,叫声像而且走路像鸭子的,那么它就是鸭子。”
例子一:在java语言中,定义一个函数,传递参数的类型必须得到控制,需要定义一个类来定义这些参数的数据类型,参数必须都继承这个类。换成python类型,例如:内置函数len()中的参数,可以是字符串,容器类型,iterator类型。因为这些数据类型的类中都有一个__len__()方法,并没有像java一样定义一个类用来继承,而是通过模糊的概念,并不是通过明确的继承来实现多态,认为拥有__len__()方法的就可以当做参数,符合鸭子类型。
面向对象三大特性之:封装
封装:隐藏对象的属性以及实现方法,仅对外提供公共的访问方法。
封装的优点:
(1)将变化隔离
(2)便于使用
(3)提高安全性
封装的原则:
(1)将不需要对外提供的内容隐藏起来。
(2)把属性都隐藏起来,提供一个公共访问的接口来对其进行访问。
广义的封装和狭义的封装
(1)广义的封装:是为了只有这个类的对象才能够使用定义在类中的方法。
class 类名: def func1(self):pass def func2(self):pass def func3(self):pass
(2)狭义的封装:把一个名字藏在类中
##### 私有静态变量/私有对象属性/私有方法 #####
在python中用双下划线__开头的方式将属性隐藏起来(设置成私有的)
为什么要定义一个私有变量?
(1)不想让你看到这个值。
(2)不想让你修改这个值。
(3)想让你在修改这个值得时候有一些限制。
(4)有些属性或者方法不希望被子类继承。
##### 私有静态变量/私有对象属性/私有方法 #####
私有变量
一般来说类中的属性就应该是共享的,但是语法上可以把类的属性设置成私有的。
#其实类中的__私有属性,都只是一个变形,变形为“_类名__属性”
#一。设置私有属性
class A:
__N = 0 #类的数据属性就应该是共享的,但是语法上是可以把类的属性设置成私有的。
print(A._N) #在类的外部就不能引用私有的静态变量
>>>
AttributeError: type object 'A' has no attribute '_N'
#二。但是谨记,都只是变形。
print(A.__dict__)
>>>
{'__module__': '__main__', '_A__N': 0, '__dict__': <attribute '__dict__' of 'A' objects>, '__weakref__': <attribute '__weakref__' of 'A' objects>, '__doc__': None}
#通过__dict__方法,可以巧妙地打破类的外部不能访问私有属性,但是这种方法被约定俗成的禁止了。
print(A.__dict__['_A__N'])
>>>
0
#其实这仅仅这是一种变形操作 #类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式: class A: __N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N def __init__(self): self.__X=10 #变形为self._A__X def __foo(self): #变形为_A__foo print('from A') def bar(self): self.__foo() #只有在类内部才可以通过__foo的形式访问到.
总结:
(1)类中定义的私有变量只能在类的内部使用,如:self.__x,其实已经变形为_类名__x
(2)这种变形其实是针对外部的变形,令外部无法通过self.__x的方式访问到私有方法。
(3)在子类中定义__x是无法覆盖父类中的__x属性的,因为在子类中定义就会变形为"_子类__x",在父类中"_父类__x"。
私有方法
在继承中,父类如果不想让子类覆盖自己的方法,可以把方法设置成私有方法。
#正常情况
>>> class A:
... def fa(self):
... print('from A')
... def test(self):
... self.fa() #看self代表谁
...
>>> class B(A):
... def fa(self):
... print('from B')
...
>>> b=B()
>>> b.test()
from B
#把fa定义成私有的,即__fa
>>> class A:
... def __fa(self): #在定义时就变形为_A__fa
... print('from A')
... def test(self):
... self.__fa() #只会与自己所在的类为准,即调用_A__fa
...
>>> class B(A):
... def __fa(self):
... print('from B')
...
>>> b=B()
>>> b.test()
from A
内置函数补充之:property
property是一个装饰器,它的作用是将一个方法伪装成一个属性。
class Student: def __init__(self,name,age): self.__name = name self.age = age @property #将一个方法伪装成一个属性 def name(self): return self.__name zhuge = Student('诸葛',20) print(zhuge.name) >>> 诸葛
为什么要用property?
将一个类的函数定义成一个属性后,对象在去使用obj.name的时候,根本不会想到这个是经过函数运算后返回的,遵循了统一访问的原则。
ps:面向对象中封装的三大方式:
【public】:这种就是对外公开,不封装。
【protected】:这种封装方式就是对外不公开,但是对子类公开。
【private】:这种封装方式就对谁的不公开
在python中并没有在语法上把它们三种都内建到class机制中,在c++里面一般会把所有的数据都设置成私有的,然后提供set和get方法去修改和获取,在python中可以通过property去实现。
class Foo: def __init__(self,val): self.__NAME= val #将所有数据属性都转化为私有属性存起来。 @property def name(self): return self.__NAME @name.setter def name(self,value): if not isinstance(value,str): raise TypeError('%s must be str'%value) self.__NAME = value # 通过类型检测后,将值value存放到真实的位置self.__NAME @name.deleter def name(self): raise TypeError('can not delete') f = Foo('egon') # print(f.name) #通过name函数返回self.__NAME # f.name = 10 #TypeError: 10 must be str del f.name #TypeError: can not delete
property总结:
(1)一个静态属性property本质就是实现了get,set,delete三种方法。
(2)只有在属性xxx定义了property后,才能使用xxx.setter,xxx.deleter。