注册 登录
Python面向对象
首页 > Python面向对象 > 1.5节:类的封装与继承

1.5节:类的封装与继承

薯条老师 2020-05-28 07:25:03 186837 0

编辑 收藏

广州番禺Python, Java小班周末班培训

薯条老师的线下Python小班办得很好,学员的平均就业薪资有11K。线下小班培训的课程有Python爬虫,Python后端开发,Python办公自动化,Python大数据分析,Python量化投资,Java中高级后端开发。授课详情请点击:http://chipscoco.com/?cate=6

1.5.1 类的封装

封装的一个很重要的性质,就是藏起来。在面向对象的程序设计中,将数据类型的属性和方法的实现细节藏起来,对外提供对数据类型进行操作的接口。在前面几节的代码实例中,我们定义的类属性/方法,对象属性/方法都可以被客户端进行访问。
凡是类或对象的使用者,都可称为客户端。

将属性与方法全部暴露给客户端,有违面向对象的设计思想。抽象一种数据类型时,最佳实践是将内部属性进行隐藏,而对外提供对属性进行操作的方法。在Python中通过在属性名或函数名前面加上双下划线__,来对属性和方法进行访问控制。 属性名或函数名加上双下划线以后,对外是不能访问的,但在类内部依然可以被访问。

代码实例:

# __desc__ = 使用双下划线对属性和方法进行隐藏
 
# 定义Cat类
class Cat:
    # 定义类属性species, 表示猫所属的物种
    __species = "Cat" 
 
    """
    将猫抓老鼠的行为,封装为一个类方法
    所有猫都会抓老鼠
    """
    @classmethod
    def __catch_mice(cls):
        # 不会抓老鼠的猫不是好猫
        print("A {} doesn't catch a mouse is not a good {}!".format(cls.__species,cls.__species))
 

    def __init__(self, name):
        self.__name = name
 
Cat.__species
Cat.__catch_mice()
将以上代码写入到Python脚本中,然后在PyCharm中运行,会抛出AttributeError的属性错误异常,因为我们将属性进行了隐藏,不能再通过客户端进行访问。同学们可以在对象方法前加上一个双下划线进行测试。

Python内部使用的是一种叫做name mangling 的技术,将带双下划线的属性或方法变成"私有"成员。实现细节是在双下划线前加上一个_类名的前缀,例如定义了一个Computer类,类私有属性为__modules, 在经过name mangling改造以后,私有属性__modules被修改成_Computer__modules。

1.5.2 类的继承

通过类的继承,可以实现软件的复用。假设有两个类A,B。B类继承自A类,那么A类就是B类的父类或基类,B类是A的子类或派生类。

判断类类型是否是其它类型的子类,可以通过issubclass(type2, type)方法。用来判断type2类型是否是type类型的子类,返回值为布尔类型。

类继承的语法:

class Child(Parent1, Parent2...):
    pass
没有父类时,类名后面无需加括号。子类继承于单个类时,被称为单继承。子类如果同时继承于多个父类,父类在括号中需以逗号进行分隔,这种继承方式被称为多继承。

在Python3中,Object是所有类型的顶层基类, 即,在定义类类型时,该数据类型会隐式地继承于Object。

在面向对象设计中,子类继承父类时应该具备逻辑上的继承关系,没有这种逻辑关系的继承是毫无意义的,比如一头猪继承于某一个亿万富豪。再者父类充当的是一个基类的角色,定义的是该类型共有的属性和操作方法,这样子类才能在父类已有的基础上,实现代码的复用和扩展,否则只会带来设计上的冗余。

(1) 单继承
子类继承于单个类时,被称为单继承。
代码实例:

# __desc__ = 通过super方法来调用父类的方法
 
# 定义Cat类,作为父类
class Cat:
    # 定义类属性species, 表示猫所属的物种
    __species = "Cat" 
 
    @classmethod
    def catch_mice(cls):
        # 不会抓老鼠的猫不是好猫
        print("A {} doesn't catch a mouse is not a good {}!".format(cls.__species,cls.__species))
 

# 定Babby类,表示家猫,继承自Cat
class Babby(Cat):
    pass
 
babby = Babby()
babby.catch_mice()
 
"""
程序的输出为:
A cat that doesn't catch a mouse is not a good cat!
"""
子类继承了父类的方法,如果在子类中定义了与父类同名的方法,则会对父类的方法进行覆盖。以子类继承的构造函数__init__为例,如果子类未对__init__方法进行定义,会默认调用父类的__init__方法。

代码实例:

# __desc__ = 子类未对__init__方法进行定义,会默认调用父类的__init__方法
 
# 定义Cat类,作为父类
class Cat:
    # 定义类属性species, 表示猫所属的物种
    __species = "Cat" 
 
    @classmethod
    def catch_mice(cls):
        # 不会抓老鼠的猫不是好猫
        print("A {} doesn't catch a mouse is not a good {}!".format(cls.__species,cls.__species))
 
    def __init__(self, name):
        self.__name = name
 
 
# 定Babby类,表示家猫,继承自Cat
class Babby(Cat):
    pass
 
babby = Babby()
在执行上文中的代码时,Python抛出了异常信息,指示在进行实例化时,缺少一个参数。在上文的代码实例中,父类Cat的构造函数带一个name的参数,子类在实例化时必须显示地传递一个参数。子类如果对__init__方法进行了定义,则会对父类的__init__方法进行覆盖,不会再调用父类的__init__方法,如需显示地调用父类的方法,可以在方法内部执行super()方法来返回一个代理对象,通过该代理对象来执行父类的方法,从而对方法进行扩展。

代码实例:

# __desc__ = 通过super()来调用父类的方法
 
# 定义Cat类,作为父类
class Cat:
    # 定义类属性species, 表示猫所属的物种
    __species = "Cat" 
 
    @classmethod
    def catch_mice(cls):
        # 不会抓老鼠的猫不是好猫
        print("A {} doesn't catch a mouse is not a good {}!".format(cls.__species,cls.__species))
 
    def __init__(self, name):
        self.__name = name
 
 
# 定Babby类,表示家猫,继承自Cat
class Babby(Cat):
    def __init__(self, name, weight):
        # 通过super()返回一个代理对象,通过代理对象执行父类的__init__方法
        super().__init__("cat")
        self.__name = name
        self.__weight = weight
 
 
    @classmethod
    def catch_mice(cls):
        # 通过super()来执行父类的catch_mice方法
        super().catch_mice()
        print("babby is too fat to catch mice!")
 
 
kitty = Babby("kitty", 18)
kitty.catch_mice()
 
"""
程序的输出为:
A cat that doesn't catch a mouse is not a good cat!
babby is too fat to catch mice!
"""
(2) 多继承
子类如果同时继承于多个父类,父类在括号中需以逗号进行分隔,这种继承方式被称为多继承。

代码实例:

# __desc__ = 多继承的代码实例
 
class Cat:
    # 定义类属性species, 表示猫所属的物种
    __species = "Cat" 
 
    @classmethod
    def catch_mice(cls):
        # 不会抓老鼠的猫不是好猫
        print("A cat that doesn't catch a mouse is not a good cat!")
    
    def miaow(self):
        print("Cats miaow when they are unhappy")
 
 
# 定义Babby类,继承于Cat
class Babby(Cat):
    pass
 
class Wildcat(Cat):
    def miaow(self):
      print("wild cats miaow when they are hungry")
 

# 定义Kitty类,同时继承于Babby和Wildcat
class Kitty(Babby, Wildcat):
    pass
 
kitty = Kitty()
kitty.miaow()

在上文代码中,Kitty类同时继承于Babby和Wildcat,Babby和Wildcat又继承于Cat。关于这样的继承关系,可以画一个图来进行直观地理解:

图片.png



从上图可以看出,这样的继承关系是一个很简单的菱形继承。但这里有一个问题,Cat类是Kitty类的父类的父类,在Cat类中定义了miaow方法,同时在Wildcat类中也定义了miaow方法。那么kitty对象在调用miaow方法时,它到底是在调用哪一个父类的miaow方法?在1.7节中会详细地介绍super类型与MRO,在掌握MRO的概念以后,同学们可再回过头来解答这个问题。

1.5.3 知识要点

(1) 抽象就是一种封装,将某一具体的事物封装为一种数据类型。在面向对象的程序设计中,将数据类型的属性和方法的实现细节藏起来,对外提供对数据类型进行操作的接口。
(2) 在子类中执行super()方法,可以返回一个代理对象,通过该代理对象可以访问父类重中的属性和方法。从而实现方法或属性的扩展。

1.5.4 最具实力的小班培训

来这里参加Python和Java小班培训的学员大部分都找到了很好的工作,平均月薪有11K,学得好的同学,拿到的会更高。由于是小班教学,所以薯条老师有精力把每位学员都教好。打算参加线下小班培训的同学,必须遵守薯条老师的学习安排,认真做作业和项目。把知识学好,学扎实,那么找到一份高薪的工作就是很简单的一件事。

(1) Python后端工程师高薪就业班,月薪11K-18K,免费领取课程大纲
(2) Python爬虫工程师高薪就业班,年薪十五万,免费领取课程大纲
(3) Java后端开发工程师高薪就业班,月薪11K-20K, 免费领取课程大纲
(4) Python大数据分析,量化投资就业班,月薪12K-25K,免费领取课程大纲

扫码免费领取学习资料:

关注微信公众号.jpg













欢迎 发表评论:

请登录

忘记密码我要注册

注册账号

已有账号?请登录