面向对象之组合、封装、多态性、鸭子类型

时间:2021-09-22 21:56:36

一、组合

1. 什么是组合

一个对象的属性是来自于另外一个类的对象,称之为组合

2. 为何用组合

组合也是用来解决类与类代码冗余的问题

3. 如何用组合

# class Foo:
#     aaa=1111
#     def __init__(self,x,y):
#         self.x=x
#         self.y=y
#
#     def func1(self):
#         print('Foo内的功能')
#
#
# class Bar:
#     bbb=2222
#     def __init__(self, m, n):
#         self.m = m
#         self.n = n
#
#     def func2(self):
#         print('Bar内的功能')
#
# obj1=Foo(10,20)
# obj2=Bar(30,40)
#
# obj1.xxx=obj2  #组合的使用方法
#
#
# print(obj1.x,obj1.y,obj1.aaa,obj1.func1)
# print(obj1.xxx.m,obj1.xxx.n,obj1.xxx.bbb,obj1.xxx.func2)

 

conclusion:

当类之间有显著不同,并且较小的类是较大的类所需要的组件时,用组合比较好

二、封装

1. 什么是封装
装指的是把属性装进一个容器
封指的是隐藏的意思,但是这种隐藏式对外不对内的

2. 为何要封装
封装不是单纯意义的隐藏
封装数据属性的目的:将数据属性封装起来,类 外部的使用就无法直接操作该数据属性了
需要类 内部开一个接口给使用者,类的设计者可以在接口之上附加任意逻辑,从而严格
控制使用者对属性的操作
封装函数属性的目的:隔离复杂度

3. 如何封装?
只需要在属性前加上__开头,该属性就会被隐藏起来,该隐藏具备的特点:
1. 只是一种语法意义上的变形,即__开头的属性会在检测语法时发生变形_类名__属性名
2. 这种隐藏式对外不对内的,因为在类内部检测语法时所有的代码统一都发生的变形
3. 这种变形只在检测语法时发生一次,在类定义之后新增的__开头的属性并不会发生变形
4. 如果父类不想让子类覆盖自己的属性,可以在属性前加__开头

class Foo:
    __x=111 #_Foo__x
    def __init__(self,m,n):
        self.__m=m # self._Foo__m=m
        self.n=n

    def __func(self): #_Foo__func
        print('Foo.func')

    def func1(self):
        print(self.__m) #self._Foo__m
        print(self.__x) #self._Foo__x

# print(Foo.__dict__)
# Foo.__x
# Foo.__func
# print(Foo._Foo__x)
# print(Foo._Foo__func)

封装数据属性的真实意图:

1、封装数据:

class Peoole:
    def __init__(self,name,age):
        self.name = name
        self.age = age
    def tell_info(self):
        print('<name:%s age:%s>'%(self.name,self.age))

    def set_info(self,new_name,new_age):
        if type(new_name) is not str:
            print('名字必须是str类型')
            return
        if type(new_age) is not int:
            print('年龄必须是int类型')
            return
        self.name = new_name
        self.age = new_age

    def clear_info(self):
        del self.name


  people1 = Peoole('wwl',18)
  people2 = Peoole('wlw',20)
  #
  people1.tell_info()
  people2.tell_info()
  #
  people1.set_info('leilei',30)
  print(people1.tell_info())
  #
  people2.clear_info()

2、封装方法:目的是隔离复杂度

# class ATM:
#
def __card(self): # print('插卡') # def __auth(self): # print('用户认证') # def __input(self): # print('输入取款金额') # def __print_bill(self): # print('打印账单') # def __take_money(self): # print('取款') # # def withdraw(self): # self.__card() # self.__auth() # self.__input() # self.__print_bill() # self.__take_money() # # a=ATM() # a.withdraw()
取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱
对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,
很明显这么做
隔离了复杂度,同时也提升了安全性

*了解装饰器property的用法
1、什么是property?
property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值
2、为何用?

将一个类的函数定义成特性以后,对象再去使用的时候obj.bim,根本无法察觉自己的bim
执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则
eg:
'''
例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解)

成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86
'''
# class People:
#     def __init__(self,name,weight,height):
#         self.name=name
#         self.weight=weight
#         self.height=height
#
#     @property
#     def bmi(self):
#         return self.weight / (self.height ** 2)
#
# obj=People('egon',70,1.82)
# obj.height=1.85
#
# print(obj.bmi)
 

3、怎么用

新写法:

# class People:
#     def __init__(self,name):
#         self.__name=name
#
#     @property
#     def name(self):
#         return '<name:%s>' %self.__name
#
#     @name.setter
#     def name(self,new_name):
#         if type(new_name) is not str:
#             print('名字必须是str类型')
#             return
#         self.__name=new_name
#
#     @name.deleter
#     def name(self):
#         del self.__name
#
# obj=People('egon')
# print(obj.name)
#
# # obj.name=123
# # print(obj.name)
#
# del obj.name
# print(obj.__dict__)

旧写法:

class People:
    def __init__(self,name):
        self.__name=name

    def xxx_name(self):
        return '<name:%s>' %self.__name

    def yyy_name(self,new_name):
        if type(new_name) is not str:
            print('名字必须是str类型')
            return
        self.__name=new_name

    def zzz_name(self):
        del self.__name

    name=property(xxx_name,yyy_name,zzz_name) #以前的写法,且函数名的位置不能改变

obj=People('egon')
print(obj.name)

# obj.name=123
# print(obj.name)

del obj.name
print(obj.__dict__)

三、多态性

1. 什么是多态
同一种事物的多种形态

2. 为何要用多态
多态性:指的是可以在不用考虑对象具体类型的前提下而直接使用对象下的方法

3. 如何用多态

# import abc
#
# class Animal(metaclass=abc.ABCMeta):
#     @abc.abstractmethod
#     def speak(self):
#         pass
#
# # Animal() # 父类不能实例化,因为父类本身就是用来制定标准的
# class People(Animal):
#     def speak(self):
#         print('say hello')
#      def jiao(self):
#        print('say hello')
#
# class Dog(Animal):
#      def speak(self):
#         print('汪汪汪')
#
# class Pig(Animal):
#      def speak(self):
#         print('哼哼哼')
#
#
# peo=People()
# dog1=Dog()
# pig1=Pig()
# #
# #
# peo.speak()
# dog1.speak()
# pig1.speak()
 def speak(animal):
    animal.speak()

speak(peo)
speak(dog1)
speak(pig1)

四、鸭子类型

Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子

python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象

也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

#二者都像鸭子,二者看起来都像文件,因而就可以当文件一样去用
class TxtFile:
    def read(self):
        pass

    def write(self):
        pass

class DiskFile:
    def read(self):
        pass
    def write(self):
        pass
class Memory:
    def read(self):
        print('mem read')

    def write(self):
        print('mem write')

class Disk:
    def read(self):
        print('disk read')

    def write(self):
        print('disk write')

class Cpu:
    def read(self):
        print('cpu read')

    def write(self):
        print('cpu write')


obj1=Memory()
obj2=Disk()
obj3=Cpu()

obj1.read()
obj2.read()
obj3.read()

其实我们一直在享受着多态性带来的好处,比如Python的序列类型有多种形态:字符串,列表,元组,多态性体现如下

#str,list,tuple都是序列类型
s=str('hello')
l=list([1,2,3])
t=tuple((4,5,6))

#我们可以在不考虑三者类型的前提下使用s,l,t
s.__len__()
l.__len__()
t.__len__()

len(s)
len(l)
len(t)