14、面向对象核心知识之:封装与继承

一、封装

1.1 封装的引入
  1. 封装的概念: 封装是面向对象的三大特性之一。就是为了保护数据安全,形成的一个默认的规则,告知协同开发者,这个属性很重要,不要去随意修改。
  2. 使用封装的背景:隐藏对象中一些不希望被类的外部所访问到的属性或方法。
    比如:
class Car():
    def __init__(self, name, color):
        self.name = name
        self.color = color

    def run(self):
        print("这是一辆{0}的{1}".format(self.color, self.name))

    def ddd(self):
        print('汽车鸣笛了')
car = Car('法拉利', '红色')
car.run()

car.name = '中华田园犬'    # 这样会把前面的类的属性修改了,相当于重新修改了实例属性。
car.run()
car.ddd()
--------------执行---------------
这是一辆红色的法拉利
这是一辆红色的中华田园犬
汽车鸣笛了
  • 影响: 上面程序中,假如项目开发完成后,后面其他同时随意修改代码,代码本身没有影响,但是代码逻辑发生改变,造成影响。一般实际工作中,都是多开发人员协同开发,我们不能去修改当前的代码,因此引入了封装的概念,
1.2 封装的引入

下面介绍2种相对封装的方法: 私有属性 和 隐藏属性。

(1)私有属性
  • 私有属性:方法外可以访问,告诉别人不要去轻易修改这个变量
  • 定义格式: 一个下划线 +属性名称 (如:_name)
class Car():

    def __init__(self, name, color):
        self._name = name   # _name 私有属性,外面可以访问,但是不要轻易去修改它
        self.color = color

    def run(self):
        print("这是一辆{0}的{1}".format(self.color, self._name))

car = Car('法拉利', '红色')
car.run()
car._name = '帕萨特'
print(car._name)    # 依然是可以修改的
--------------执行---------------
这是一辆红色的法拉利
帕萨特
(2)隐藏属性
  • 隐藏属性: 在类的外面无法访问,是不可读的。相当于一个常量,外面不可以去修改。
  • 定义格式: 2个下划线+ 属性名称 (如: __name)
class  Car():

    def __init__(self, name, color):
        self.__name = name   ## __name 私有属性,相当于一个常量,不可以更改
        self.color = color

    def run(self):
        print("这是一辆{0}的{1}".format(self.color, self.__name))

car = Car('法拉利', '白色')
car.run()
# print(car.__name)  # 隐藏属性,外面无法访问,是不可读的。
print(car._Car__name)   # 可以通过这种方式获取私有属性。
--------------执行---------------
这是一辆白色的法拉利
法拉利
1.3 getter() 与 setter() 方法

上面我们介绍到了,为了保护代码,提供2种封装方式,私有属性和隐藏属性,都是为了防止代码外部修改属性或方法,但是如果真的要修改呢? 也有专门的方式可以访问到属性或方式: getter() 和 setter() 方法。

getter() 方法 : 提供给代码外部访问这个属性的方法(访问)。
setter() 方法:提供给代码外部修改这个属性的方法 (修改,重新赋值)。
class  Car():
    def __init__(self,name, color):
        self._name = name  # 隐藏属性,不可读的属性
        self.color = color

    def get_name(self):   # getter方法,return返回到外层,提供给你访问这个属性的方法
        return self._name

    def set_name(self, name):   #注意这里要传参
        self._name = name # setter方法,重新赋值,提供给你修改这个属性的方法

    def run(self):
        print("这是一辆{0}的{1}".format(self.color,self._name))

car = Car('法拉利','红色')
print(car.get_name())
car.run()
car.set_name('保时捷')
print(car.get_name())
car.run()
--------------执行---------------
法拉利
这是一辆红色的法拉利
保时捷
这是一辆红色的保时捷

注意:

  1. 假如上面,魔法方法下面,既没有getter方法,也没有setter方法,就说明这个隐藏属性,不要去访问也不要去修改。
  2. 假如上面,魔法方法下面,如果只有getter 方法,说明上面隐藏属性是可读的,可以访问。
  3. 假如上面,魔法方法下面,如果只有setter 方法,说明上面隐藏属性是可以修改。

二、property 装饰器

2.1 引入property装饰器的背景

装饰器为了让我们更好的去定义属性。
比如上面,我们如果要去访问属性和调用方法时,需要:

print(cat.name)  # 访问属性
car.name()    # 调用方法

那么如果要去访问 get_name() 的属性呢 ?

print(car.get_name())

这样一来,是不是容易记混淆。所以,我们引入了@property装饰器,来帮助我们创建私有属性。

2.2 getter 装饰器的使用
class Car:
    def __init__(self, name, color):
        self._name = name
        self.color = color

    # property装饰器
    @property
    def get_name(self):
        return self._name

    def set_name(self, name):
        self._name = name

    def run(self):
        print("这是一辆{0}的{1}".format(self.color, self._name))


car = Car('法拉利','红色')
car.run()  # 调用方法
print(car._name)  # 访问属性
# print(car.get_name())
print(car.get_name)  # 去掉括号,直接使用属性
--------------执行---------------
这是一辆红色的法拉利
法拉利
法拉利
2.3 setter 装饰器的使用
class Car:
    def __init__(self, name, color):
        self._name = name
        self.color = color

    # getter方法装饰器
    @property
    def name(self):
        return self._name

    # setter 方法装饰器
    @name.setter
    def name(self, name):  # 改成同名的方法
        self._name = name

    def run(self):
        print("这是一辆{0}的{1}".format(self.color, self._name))

car = Car('保时捷','白色')
car.run()

# 使用set装饰器时,怎么去使用
car.name = '玛莎拉蒂'
print(car.name)
--------------执行---------------
这是一辆白色的保时捷
玛莎拉蒂

三、 继承

  • 继承是面向对象三大特性之一。<封装,继承,多态>
  • 通过继承我们可以使一个类获取到其他类中的属性和方法。
  • 在定义类时,可以在类后面的括号种指定当前类的父类(超类、基类)。
  • 继承提高了类的复用性,让类与类之间产生了关系,有了这个关系,才有了多态的特性。
3.1 继承的简介
class Doctor():
    name = ''
    age = ''

    def study(self):
        print('治病救人')

class Soldier():
    name = ''
    aget = ''

    def study(self):
        print('保家卫国')
  • 上面这种情况下,代码逻辑都一样,相当于代码重复,那么这2个类怎么去优化呢 ?
class Person():
    name = ''
    age = ''

    def study(self):
        pass
3.2 继承的引入
class Animal(object):

    def sleep(self):
        print('动物会睡觉')

    def run(self):
        print('动物会跑')

class Dog(Animal):   # 这样就叫继承,创建类的时候,后面括号里面写上你要继承的内容。
    pass

dog = Dog()  # 创建实例
dog.run()      # 调用方法。

补充
isinstance() : 检测实例是否为一个类的实例的方法。
issubcalss(): 检测一个类是否为一个类的父类的方法。

print(isinstance(dog, Dog))
print(isinstance(dog, Animal))
print(issubclass(Animal, object))
--------------执行---------------
True
True
True
  • object 是所有类的最大父类,处于最顶端,所有的类都是继承至object。在定义类时,如果没有些,默认所有类都继承至 object。
3.2 方法的重写(必须在继承的前提之下)

继承的特性:通过继承我们可以去查找其他类中的属性和方法,一层一层网上查找,直到找到为止。

  1. 会优先去当前对象中寻址是否具有该方法,如果有直接调用
  2. 如果没有,则取对象的父类中寻找,如果父类中有则直接调用;
  3. 如果父类中没有,则去父类的父类中寻找,依次类推,直到找到 object为止,如果最终没有找到就会报错。
class A(object): # object 是所有类共有的最大的父类
    def test(self):
        print('A.......')

class B(A):
    pass

class C(B):
    pass

c = C()
c.test()
# A.......
3.3 super方法的使用
  • super方法的作用:将被覆盖了的父类方法,重新调用。比如类Dog 中的sleep方法,在原有基础上又学习到继承的类中其他新的方法。

  • 使用条件:
    1.必须要有父类的继承。
    2.必须要有方法的重写。

  • 使用super()

class Animal(object):  # 括号中没有,默认继承object

    def sleep(self):
        print('动物会睡觉')

    def run(self):
        print('动物会跑')


class Dog(Animal):

    def sleep(self):   # 重写,可以查看左边栏监听,箭头向下表示继承,箭头向上表示重写
        print('狗会睡觉')
        super().sleep()  # 获取到父类的方法,重新调用,2个都会有了。

dog = Dog()
dog.sleep()
dog.run()
------------------执行-----------------
狗会睡觉
动物会睡觉
动物会跑

注意:如果有多级继承,super只会学习上一级的方法。

class A(object):
    def sleep(self):
        print('1111')

class Animal(A):
    def run(self):
        print('动物会跑')

    def sleep(self):
        print('动物会睡觉')

class Dog(Animal):
    def sleep(self):
        print('狗会睡觉')
        super().sleep()  # 如果有多级继承,super只会学习上一级的方法。

dog = Dog()
dog.run()
dog.sleep()
--------------执行---------------
狗会睡觉
动物会睡觉
动物会跑
3.4 多重继承
  • 什么时多重继承 ?
    • 在Python 中可以为一个类指定多个父类。
  • 定义格式: 在类名的() 里面添加多个类,来实现多重继承。
  • 多重继承:会使子类同时拥有多个父类,并且获取到所有父类中的方法;
  • 在开发中没有特殊情况下,我们应该尽量避免使用多重继承,因为多重继承会让我们的代码更加复杂。
  • 如果在多个父类中有同名的方法,则会在第一个父类中寻找,然后找第二个,找第三个……一直向上查找,前面会覆盖后面的。
class Person1(object):
    def chuiniu(self):
        print('很会吹牛')

class Person2(object):
    def paimapi(self):
        print('很会拍马屁')

class Son(Person1, Person2):  # 对应顺序
    pass   # pass 占位

son = Son()
son.chuiniu()
son.paimapi()

总结:
1. 在开发中没有特殊情况下,我们应该尽量避免使用多重继承,因为多重继承会让我们的代码更加复杂。
2. 尽量做到代码解耦合。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,047评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,807评论 3 386
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,501评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,839评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,951评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,117评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,188评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,929评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,372评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,679评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,837评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,536评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,168评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,886评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,129评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,665评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,739评论 2 351

推荐阅读更多精彩内容