组合

什么是组合

  • 一个对象中的属性是另一个类

为什么要组合

  • 减少代码冗余并且可以提高程序可扩展性

如何使用组合

  • 将一个类实例出的对象当做属性添加给另一个类实例出的对象

    class People:
        def __init__(self,name):
            self.name = name
    
    class Date:
        def __init__(self,year,month,day):
            self.year = year
            self.month = month
            self.day = day
    
        def tell_birth(self,obj):
            print(
                f'''
                ==={obj.name}的出生日期===
                年 {self.year}
                月 {self.month}
                日 {self.day}
                '''
            )
    
    class Teacher(People):
        pass
    
    class Student(People):
        pass
    a = Teacher('蔡启龙')
    d = Date(1994,12,10)
    
    a.birth = d
    a.birth.tell_birth(a)
    
    '''
     ===蔡启龙的出生日期===
                年 1994
                月 12
                日 10          
    '''
    

练习---学生选课系统

  • '''
    选课系统需求:
        1.学生类,老师类, 学生和老师都有课程属性, 每一门课程都是一个对象.
            课程: 课程名字,课程周期,课程价钱
    
        2.学生和老师都有选择课程的功能, 还有打印所有课程的功能.
    '''
    
    class People:
        def __init__(self,name):
            self.name = name
            self.course_lt = []
    
        #选择一门课程的方法
        def choose_course(self,obj):
            self.course_lt.append(obj)
            print(f'{self.name}选择了{obj.course_name}课程')
    
        def prt_course(self):
            print(f'{self.name}的课程信息为:')
            for i in self.course_lt:
                print(f'''
                课程名:    {i.course_name}
                课程周期:  {i.period}
                课程价格:  {i.price}
                ''')
    
    class Course:
        def __init__(self,course_name,period,price):
            self.course_name = course_name
            self.period = period
            self.price = price
    
    class Student(People):
        def __init__(self,name,score):
            super().__init__(name)
    
    class Teacher(People):
        def __init__(self,name,level):
            super().__init__(name)
    
    teacher_cql = Teacher('蔡启龙',10)
    stu_tank = Student('tank',0)
    python = Course('python',6,'2万')
    linux = Course('linux',4,1)
    teacher_cql.python = python
    teacher_cql.linux = linux
    
    
    teacher_cql.choose_course(teacher_cql.python)
    teacher_cql.choose_course(teacher_cql.linux)
    teacher_cql.prt_course()
    
    '''
    蔡启龙选择了python课程
    蔡启龙选择了linux课程
    
    蔡启龙的课程信息为:
    
                课程名:    python
                课程周期:  6
                课程价格:  2万
                
    
                课程名:    linux
                课程周期:  4
                课程价格:  1
    '''
    

封装

什么是封装?

  • 比喻
    • 封:比如把一个袋子封起来
    • 装:比如把一堆小猫,小狗,nick装到袋子里
    • 对象就好比一个袋子,袋子里面装一堆属性
  • 封装指的是把一堆属性(特征与技能)封装到一个对象---存
  • 对象可以通过 . 的方式获取属性---取

为什么要封装?

  • 方便存取,可以通过 对象. 的方式获取属性

如何封装?

  • 在类内部,定义一堆属性(特征与技能)
  • 特征:变量---数据属性
  • 技能---方法属性
  • 通过 对象.属性 = 属性值 修改属性

访问限制机制

什么是访问限制机制?

  • 在类内部定义,凡是以 __ 开头的数据属性与方法属性,都会被python隐藏起来,外部不能直接访问这些属性,比如 __name = 'tank'

为什么要有访问限制机制?

  • 对重要重要及隐私数据获取的逻辑更加严谨,保证数据的安全性

怎么实现

  1. 隐私属性通过封装一个接口,在接口内做业务逻辑处理,再把数据返回给调用者.

    class Foo:
        # 数据属性
        __name = 'tank'
    
        # 方法属性
        def __run(self):
            print('running...')
    
        # 获取数据接口
        def get_name(self):
            return self.__name
    
        # 修改属性接口
        def set_name(self):
            self.__name = 'json_sb'
            return self.__name
    
    foo = Foo()
    # print(foo.__name)   #AttributeError: 'Foo' object has no attribute '__name'
    
    res = foo.get_name()  # 获取属性
    print(res)
    
    # print(foo._Foo__name) #强制访问
    
    foo.set_name()  # 修改属性
    print(foo.get_name())  # 获取修改后的属性
    
  2. 在python中不会强制限制属性的访问,类内部使用 __属性名 定义的属性变形成了 _类名__属性名 ,若想直接访问,可以通过 对象.变形后的名字 访问

    class Foo:
        __name = 'tank'	# -->	_Foo__name
    

练习

  • class Teacher:
        # 初始化对象定制属性
        def __init__(self, name, age, gender):
            self.__name = name
            self.__age = age
            self.__gender = gender
    
        # 打印用户信息接口
        def prt_userinfo(self):
            username = input('请输入用户名:')
            userpwd = input('请输入用户密码:')
            if username == '蔡启龙' and userpwd == '123':
                print(f'''
    用户信息为:
    姓名: {self.__name}
    年龄: {self.__age}
    性别: {self.__gender}
                ''')
    
        # 修改用户信息接口
        def change_userinfo(self, name, age, gender):
            if not isinstance(name, str) or not isinstance(age, int) or not isinstance(gender, str):
                raise TypeError('非法输入!')  # 抛错
            else:
                self.__name = name
                self.__age = age
                self.gender = gender
    
    
    teacher = Teacher('蔡启龙', 18, 'male')  # 实例出一个老师对象
    
    # teacher.prt_userinfo()    #通过打印用户信息接口打印用户信息
    
    teacher.change_userinfo('tank', 19, 'female')  # 通过修改用户信息接口修改用户信息
    
    teacher.prt_userinfo()  # 通过打印用户信息接口打印修改后的用户信息
    
    class ATM:
    
        def __insert_card(self):
            print('插卡...')
    
        def __input_pwd(self):
            print('输入密码...')
    
        def __input_amount(self):
            print('输入金额...')
    
        def __get_money(self):
            print('正在吐钱...')
    
        def __prt_flow(self):
            print('打印账单...')
    
        # 取款接口
        def withdraw(self):
            self.__insert_card()
            self.__input_pwd()
            self.__input_amount()
            self.__get_money()
            self.__prt_flow()
    
    
    atm = ATM()  # 实例化出ATM对象
    
    atm.withdraw()  # 通过取款接口取款,保证取款流程不被修改
    

property装饰器

什么是property?

  • python内置的装饰器,主要是给类内部的方法使用

为什么要用property?

  • 在对象调用某个方法时, 将调用方式从 对象.方法() 变成 对象.方法 ,使方法属性看起来像数据属性

怎么实现?

  • '''
    计算人体的bmi值: bmi值=体重/(身高**2)
    '''
    
    class People:
        def __init__(self,weight,hight):
            self.weight = weight
            self.hight = hight
    
        #计算bmi值的函数
        @property
        def bmi(self):
            return self.weight/self.hight**2
    
    p = People(60,1.75)
    print(p.bmi)
    # p.bmi = 10  #AttributeError: can't set attribute
    
  • 使用 @property 函数装饰

  • 不能对被装饰的方法进行属性修改

多态

什么是多态

  • 面向对象的三大基本特征之一,依赖于继承
  • 一个父类可以有不同的子类
  • 父类中的一个方法,在不同子类中可以有不同执行方式,产生不同的执行结果

为什么要有多态

对于不同子类之间存在的差异(多态性),可以在父类中写出通用的抽象方法,做出通用的编程,来适应需求的不断变化

如何实现?

  1. 子类重用父类的方法并重写该方法来覆盖父类原有的方法

    • class Animal:
          def speak(self):
              print('动物说话...')
      
      class Dog(Animal):
          def speak(self):
              print('狗汪汪汪的说')
      
      class Pig(Animal):
          def speak(self):
              print('猪哼哼哼地说')
      
      class Cat(Animal):
          def speak(self):
              print('猫喵喵喵地说')
      
      dog = Dog()
      pig = Pig()
      cat = Cat()
      
      dog.speak()
      pig.speak()
      cat.speak()
      
  2. 通过抽象类实现---需导入 abc---(abstract class) 模块

    • 抽象类

      • 是什么

        • 一系列类中相同特征与技能的结合体
        • 只能被继承,不能实例化,子类必须实现抽象类中定义的每个抽象方法
      • 为什么

        • 多态的一个表现形式,通过关注抽象类方法的描述而不需要考虑实现细节,有利于协同开发,实现归一化设计
      • 如何实现

        • 导入 abc---(abstract class) 模块

          import abc
          
          class Animal(metaclass=abc.ABCMeta):
              @abc.abstractmethod
              def speak(self):
                  print('动物说话...')
          
              @abc.abstractmethod
              def eat(self):
                  pass
          
          
          class Dog(Animal):
              def speak(self):
                  print('狗汪汪汪的说')
          
              def eat(self):
                  print('狗吃骨头...')
          
              #派生新方法
              def run(self):
                  print('狗奔跑...')
          
          dog = Dog()
          dog.speak()
          dog.eat()
          dog.run()
          
          '''
          没有实现抽象方法时报错:
          Can't instantiate abstract class Dog with abstract methods eat		
          '''
          

鸭子类型---Duck typing

美国印第安纳州的诗人的诗句:" When I see a bird that walks like a duck and swims like a duck and quacks like a duck, I call that bird a duck."

什么是鸭子类型

任何拥有像鸭子一样走和叫方法的对象所属的类型都是鸭子类型,鸭子类型不管制对象的类型,而是关注对象具有的方法

为什么要有鸭子类型

多态的一个表现形式,鸭子类型耦合度低,使用鸭子类型可使程序扩展性增强

如何实现

既不使用抽象类也不使用继承

  • class Animal:
        def speak(self):
            print('动物说话...')
    
        def eat(self):
            pass
    
    class Dog:
        def speak(self):
            print('狗汪汪汪的说')
    
        def eat(self):
            print('狗吃骨头...')
    

定义统一接口利用多态性对接口进行多种实现

  • # 例一
    class Animal:
        def speak(self):
            print('动物说话...')
    
    class Dog(Animal):
        def speak(self):
            print('狗汪汪汪的说')
    
    class Pig(Animal):
        def speak(self):
            print('猪哼哼哼地说')
    
    class Cat(Animal):
        def speak(self):
            print('猫喵喵喵地说')
    
    #定义统一接口
    def Bark(obj_animal):
        obj_animal.speak()
    
    dog = Dog()
    pig = Pig()
    cat = Cat()
    
    Bark(dog)
    Bark(pig)
    Bark(cat)
    print('*'*50)
    
    # 例二
    str1 = '1234'
    list1 = [1,2,3]
    
    #类的内置方法
    print(str1.__len__())
    print(list1.__len__())
    print('*'*50)
    
    #内置 "len()" 函数---统一接口
    print(len(str1))
    print(len(list1))
    print('*'*50)
    
    #自定义 "len()" 函数---统一接口
    def my_len(d):
        return d.__len__()
    
    print(my_len(str1))
    print(my_len(list1))