Python------面向对象之三大特性之封装,多态and类的约束

时间:2022-11-12 15:40:53

1.面向对象之三大特性

一.封装

封装,顾名思义就是将内容封装到某个地方,以后再去调用被封装在某处的内容。

所以,在使用面向对象的封装特性时,需要:

  • 将内容封装到某处
  • 从某处调用被封装的内容

第一步:将内容封装到某处

self 是一个形式参数,当执行 obj1 = Foo('wupeiqi', 18 ) 时,self 等于 obj,当执行 obj2 = Foo('alex', 78 ) 时,self 等于 obj2.

第二步:从某处调用被封装的内容

调用被封装的内容时,有两种情况:

  • 通过对象直接调用
  • 通过self间接调用

1、通过对象直接调用被封装的内容

上图展示了对象 obj1 和 obj2 在内存中保存的方式,根据保存格式可以如此调用被封装的内容:对象.属性名

`class` `Foo:` `    ``def` `__init__(``self``, name, age):``        ``self``.name ``=` `name``        ``self``.age ``=` `age` `obj1 ``=` `Foo(``'wupeiqi'``, ``18``)``print` `obj1.name    ``# 直接调用obj1对象的name属性``print` `obj1.age     ``# 直接调用obj1对象的age属性` `obj2 ``=` `Foo(``'alex'``, ``73``)``print` `obj2.name    ``# 直接调用obj2对象的name属性``print` `obj2.age     ``# 直接调用obj2对象的age属性`

2、通过self间接调用被封装的内容

执行类中的方法时,需要通过self间接调用被封装的内容

`class` `Foo:`` ` `    ``def` `__init__(``self``, name, age):``        ``self``.name ``=` `name``        ``self``.age ``=` `age`` ` `    ``def` `detail(``self``):``        ``print` `self``.name``        ``print` `self``.age`` ` `obj1 ``=` `Foo(``'wupeiqi'``, ``18``)``obj1.detail()  ``# Python默认会将obj1传给self参数,即:obj1.detail(obj1),所以,此时方法内部的 self = obj1,即:self.name 是 wupeiqi ;self.age 是 18`` ` `obj2 ``=` `Foo(``'alex'``, ``73``)``obj2.detail()  ``# Python默认会将obj2传给self参数,即:obj1.detail(obj2),所以,此时方法内部的 self = obj2,即:self.name 是 alex ; self.age 是 78`

综上所述,对于面向对象的封装来说,其实就是使用构造方法将内容封装到 对象 中,然后通过对象直接或者self间接获取被封装的内容。

二.多态

多态,同一个对象,多种形态。python默认支持多态。

在java或者c#定义变量或者给函数传值必须定义数据类型,否则就报错。

def func(int a):
    print('a必须是数字')
    
# 而类似于python这种弱定义类语言,a可以是任意形态(str,int,object等等)。
def func(a):
    print('a是什么都可以')
    
# 再比如:
class F1:
    pass


class S1(F1):
    
    def show(self):
        print 'S1.show'


class S2(F1):
    
    def show(self):
        print 'S2.show'


# 由于在Java或C#中定义函数参数时,必须指定参数的类型
# 为了让Func函数既可以执行S1对象的show方法,又可以执行S2对象的show方法,所以,定义了一个S1和S2类的父类
# 而实际传入的参数是:S1对象和S2对象

def Func(F1 obj):
"""Func函数需要接收一个F1类型或者F1子类的类型"""

    print obj.show()
    

s1_obj = S1()
Func(s1_obj)  # 在Func函数中传入S1类的对象 s1_obj,执行 S1 的show方法,结果:S1.show

s2_obj = S2()
Func(s2_obj)  # 在Func函数中传入Ss类的对象 ss_obj,执行 Ss 的show方法,结果:S2.show

Python伪代码实现Java或C  # 的多态

鸭子类型

python中有一句谚语说的好,你看起来像鸭子,那么你就是鸭子。
对于代码上的解释其实很简答:
class A:
    def f1(self):
        print('in A f1')
    
    def f2(self):
        print('in A f2')


class B:
    def f1(self):
        print('in A f1')
    
    def f2(self):
        print('in A f2')
        
obj = A()
obj.f1()
obj.f2()

obj2 = B()
obj2.f1()
obj2.f2()
# A 和 B两个类完全没有耦合性,但是在某种意义上他们却统一了一个标准。
# 对相同的功能设定了相同的名字,这样方便开发,这两个方法就可以互成为鸭子类型。

# 这样的例子比比皆是:str  tuple list 都有 index方法,这就是统一了规范。
# str bytes 等等 这就是互称为鸭子类型。

三.类的约束

对类的约束有两种:

  1. 提取⽗类. 然后在⽗类中定义好⽅法. 在这个⽅法中什么都不⽤⼲. 就抛⼀个异常就可以了. 这样所有的⼦类都必须重写这个⽅法. 否则. 访问的时候就会报错.
  2. 使⽤元类来描述⽗类. 在元类中给出⼀个抽象⽅法. 这样⼦类就不得不给出抽象⽅法的具体实现. 也可以起到约束的效果.

先用第一种方式解决:

class Payment:
    def pay(self,money):
        raise Exception('子类必须定义此方法')

class QQpay(Payment):
    def pay(self,money):
        print(f'利用qq支付了{money}')

class Alipay(Payment):
    def pay(self,money):
        print(f'利用支付宝支付了{money}')

class Wechatpay(Payment):
    def pay(self,money):
        print(f'利用微信支付了{money}')

def pay(obj,money):
    obj.pay(money)

obj3 = Wechatpay()
pay(obj3,300)

第二种方式:引入抽象类的概念处理。

from abc import ABCMeta,abstractmethod
class Payment(metaclass=ABCMeta):
    @abstractmethod
    def pay(self,money):
        pass

class QQpay(Payment):
    def pay(self,money):
        print(f'利用qq支付了{money}')

class Alipay(Payment):
    def pay(self,money):
        print(f'利用支付宝支付了{money}')

class Wechatpay(Payment):
    def pay(self,money):
        print(f'利用微信支付了{money}')

obj3 = Wechatpay()

总结: 约束. 其实就是⽗类对⼦类进⾏约束. ⼦类必须要写xxx⽅法. 在python中约束的⽅式和⽅法有两种:

1. 使⽤抽象类和抽象⽅法, 由于该⽅案来源是java和c#. 所以使⽤频率还是很少的

2. 使⽤⼈为抛出异常的⽅案. 并且尽量抛出的是NotImplementError. 这样比较专业, ⽽且错误比较明确.(推荐)

四.super()的深入了解

class A:
    def f1(self):
        print('in A f1')

    def f2(self):
        print('in A f2')

class Foo(A):
    def f1(self):
        # super().f2()
        super(Foo, self).f2()
        print('in A Foo')
obj = Foo()
obj.f1()

class A:
    def f1(self):
        print('in A')

class Foo(A):
    def f1(self):
        super(Foo,self).f1()
        print('in Foo')  # 2

class Bar(A):
    def f1(self):
        print('in Bar')  # 1

class Info(Foo,Bar):

    def f1(self):
        super(Info,self).f1()
        print('in Info f1')  # 3

obj = Info()
print(Info.mro())  # [Info, Foo, Bar, A]
obj.f1()

# super() 严格意义并不是执行父类的方法.
# 单继承: super() 肯定是执行父类的方法.
# 多继承: super(S,self) 严格按照self从属于的类的mro的执行顺序,执行 S类的下一位.



class A:
    def f1(self):
        print('in A')

class Foo(A):

    def f1(self):
        super().f1()
        print('in Foo')

class Bar(A):

    def f1(self):  # self = obj
        print('in Bar')

class Info(Foo,Bar):

    def f1(self):  # self = obj
        super(Foo,self).f1()
        print('in Info f1')

obj = Info()  # [Info, Foo, Bar, A]
obj.f1()
# 多继承: super(S,self) 严格按照self从属于的类的mro的执行顺序,执行 S类的下一位.