Day 21 组合/封装/property特性/多态和多态性/抽象类/鸭子类型

时间:2019-10-11
本文章向大家介绍Day 21 组合/封装/property特性/多态和多态性/抽象类/鸭子类型,主要包括Day 21 组合/封装/property特性/多态和多态性/抽象类/鸭子类型使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

组合

什么是组合

组合就是一个类的对象具备某一个属性,该属性的值是指向另外一个类的对象

为什么要使用组合

为了减少类与类之间代码的冗余

如何使用组合

class Course:
    def __init__(self, course_name, course_period):
        self.course_name = course_name
        self.course_period = course_period

    def tell_info(self):
        print(f'''
            课程信息
            课程名称:{self.course_name}
            课程时长:{self.course_period}
        ''')


class Student:
    def __init__(self, name, age, gender):
        self.name = name
        self.age = age
        self.gender = gender
        self.course_list = []

    def choose_course(self, course_obj):
        self.course_list.append(course_obj)

    def tell_course_info(self):
        for course in self.course_list:
            course.tell_info()

# 创建课程和学生对象
python = Course('python', '6个月')
stu = Student('Tiny', 18, 'male')

#添加课程
stu.choose_course(python)
# 获取课程信息
stu.tell_course_info()

封装

什么是封装

封装指的是将一堆属性(特征和技能)放到一个对象中

为什么要封装

  1. 封装数据的原因:保护隐私
  2. 封装方法的原因:隔离复杂度

两个层面的封装

封装其实分为两个层面,但无论哪种封装,都要对外界提供好访问你内部隐藏内容的接口(接口可以理解为入口,有了这个入口,使用者无需且不能够直接访问到内部隐藏的细节,只能走接口,并且我们可以在接口的实现上附加更多的处理逻辑,从而严格控制使用者的访问)

第一个层面的封装

创建类和对象会分别创建二者的名称空间,我们可以用类名.或者对象.的方式去访问里面的名字,这本身就是一种封装

注意:对于这一层面的封装,类名.和对象名.就是访问隐藏属性的接口

第二个层面的封装

类中把某些属性和方法隐藏起来(或者说定义成私有),只有类的内部使用,外部无法访问,或者留下少量接口(函数)供外部使用

在python中用__(双下划线)的方式来实现隐藏属性(私有化)

类中所有双下划线开头的名称如__x都会自动变成:_类名__x的形式:

class A:
    __name = 'Tiny'

    def __foo(self):
        print('from A')

    def get_name(self):
        return self.__name

    def set_name(self):
        self.__name = 'Nick'


a = A()
print(a.get_name())
a.set_name()
print(a.get_name())
print(a._A__name)
Tiny
Nick
Nick

这种自动变形的特点:

  1. 类中定义的__name只能在类中使用,如self.__name
  2. 这种变形其实正是针对内部的变形,在外部是无法通过__name这个名字访问到的
  3. 在子类定义的__name不会覆盖在父类定义的__name,因为子类中变形成了_子类名__name,而父类中变形成了_父类名__name,即双下划线开头的属性在继承给子类时,子类是无法覆盖的

注意:对于这一层面的封装,我们需要在类中定义一个函数(接口函数)在它内部访问被隐藏的属性,然后外部就可以使用了

这种变形需要注意的问题是:

  1. 这种机制也并没有真正意义上限制我们从外部直接访问,知道了类名和属性名就可以拼出名字,_类名__属性名,然后就可以访问了
  2. 变形的过程只在类的定义时发生一次,在定义后的赋值操作,不会变形
  3. 在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

类的property特性

什么是property特性

property装饰器用于将被装饰的方法伪装成一个数据属性,在使用时可以不加括号而直接使用

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 * self.height)

    @property
    def get_name(self):
        return self.name

    @get_name.setter
    def set_name(self, val):
        self.name = val


people = People('tiny', 54, 1.7)
print(people.bmi)
people.set_name = 'jack'
print(people.get_name)

property属性的定义和调用要注意以下几点:

  1. 定义是,在实例方法的基础上添加@property装饰器;并且仅有一个self参数
  2. 调用时,无需括号

类的多态和多态性

什么是多态

多态指的是一类事物有多种形态

  1. 序列数据类型有多种形态:字符串,列表,元组
  2. 动物有多种形态:人,狗,猪

什么是多态性

多态性是指具有不同功能的函数可以使用相同的函数名,这样就可以用一个函数名调用不同内容的函数.

class Pig:
    def run(self):
        print('pig is running')


class Dog:
    def run(self):
        print('dog is running')

        
pig = Pig()
dog = Dog()

pig.run()
dog.run()

抽象类

什么是抽象类

abc模块 abstract_class

为什么使用抽象类

强制子类必须遵循父类的一套标准

如何使用抽象类

import abc


class Animal(metaclass=abc.ABCMeta):
    @abc.abstractmethod
    def run(self):
        pass


class Pig(Animal):
    def run(self):
        print('pig is running')


class Dog(Animal):
    def run(self):
        print('dog is running')


pig = Pig()
dog = Dog()

pig.run()
dog.run()

鸭子类型

什么是鸭子类型

Duck typing 这个概念来源于美国印第安纳州的诗人詹姆斯·惠特科姆·莱利(James Whitcomb Riley,1849-
1916)的诗句:”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.”
在python中,不推荐使用抽象类来强制限制子类的定义,但是推荐都遵循鸭子类型

继承:

​ 耦合性太高,程序的可扩展性差

鸭子类型:

​ 耦合性低,程序的可扩展性强

原文地址:https://www.cnblogs.com/2222bai/p/11655187.html