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。

 

posted @ 2018-08-29 16:47  hehehe1994  阅读(119)  评论(0编辑  收藏  举报