Python 类与对象编程详解四(封装、继承、多态)

您所在的位置:网站首页 python封装属性的访问 Python 类与对象编程详解四(封装、继承、多态)

Python 类与对象编程详解四(封装、继承、多态)

2023-08-07 21:13| 来源: 网络整理| 查看: 265

上一篇:Python 类与对象编程详解三(类属性、实例属性)

目录 封装为什么要封装封装方法封装例子封装特性面向对象的封装有三种形式: 继承什么是继承为什么要继承如何用继承单继承多继承新式类、经典类继承与抽象派生类组合属性查找顺序重写子类中访问父类的内容子类初始化基类的私有成员类的mro方法菱形继承 多态什么是多态为什么要用多态

封装 为什么要封装

封装不是单纯意义的隐藏 加粗样式 1、封装数据:主要原因是:保护私隐,明确区分内外。将数据隐藏起来这不是目的。隐藏起来然后对外提供操作该数据的接口,然后我们可以在接口附加上对该数据操作的限制,以此完成对数据属性操作的严格控制。 实例1:

class teacher: def __init__(self,name,age): self.__name = name self.__age = age def tell_info(self): print("姓名:%s,年龄:%s"%(self.__name,self.__age)) def set_info(self,name,age): if not isinstance(name,str): raise TypeError('姓名必须是字符串类型') if not isinstance(age,int): raise TypeError('年龄必须是整形') self.__name = name self.__age = age t = teacher('jack',19) t.tell_info() t.set_info('jery',18) t.tell_info()

运行结果:

姓名:jack,年龄:19 姓名:jery,年龄:18 封装方法

目的是隔离复杂度 实例:

#取款是功能,而这个功能有很多功能组成:插卡、密码认证、输入金额、打印账单、取钱 #对使用者来说,只需要知道取款这个功能即可,其余功能我们都可以隐藏起来,很明显这么做隔离了复杂度,同时也提升了安全性 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()

运行结果:

插卡 用户认证 输入取款金额 打印账单 取款 封装例子

比如说私有变量和私有方法,如果你还不太了解,请看这篇文章

Python 类与对象编程详解二(成员保护和访问限制) 1.类中定义的__x只能在内部使用,如self.__x,引用的就是私有属性的结果。外部是无法通过__x这个名字访问到的。 2.在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。 这种变形需要注意的问题是: 1.这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

封装特性

什么是特性property property是一种特殊的属性,访问它时会执行一段功能(函数)然后返回值 为什么要用property 将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则 在上一篇:Python 类与对象编程详解三(类属性、实例属性) 中也有property的实例,这里不在举例

面向对象的封装有三种形式:

public 这种其实就是不封装,是对外公开的 protect 下面的代码会用到装饰器,如果您还不太了解,请看这篇文章 Python 装饰器 实例:

class people(object): def __init__(self): self._x = 'male' # # p2.sex=male 一初始化或赋值操作就找 sex.setter @property # 负责查询 def x(self): # 通过接口可以查看隐藏的性别 return self._x # p2.__sex= male @x.setter# 定义修改性别的接口 def x(self,value): # sexes=['male','female'] if not isinstance(value,str): # 在设定值之前进行类型检查,增加限制的扩展性 raise TypeError('性别必须是字符串类型') # if value not in sexes: # raise TypeError('性别只能是 male 或者 female') self._x=value # p2.__sex= male @x.deleter def x(self): # 删除属性接口 del self._x # del p2.__sex p2=people() # 触发 init 执行,这里有个赋值操作 p2.sex='male' print(p2.x) p2.x = 'female' print(p2.x) del p2.x # 删掉 __sex 数据属性 print(p2.x) # 再去 property 找的话找不到了

运行结果:

male female AttributeError: 'people' object has no attribute '_x'

private 这种封装对谁都不公开

class people(object): def __init__(self): self.__name = "hello" Jack = people() print(Jack.__name) print(people.__name)

上面的代码执行就会报错,因为类和对象不能再类外访问私有属性

继承 什么是继承

继承是一种创建新的类的方式,新创建的叫子类,继承的叫父类、超类、基类。

特点:子类可以使用父类的属性,方法

继承是类与类之间的关系

为什么要继承

减少代码冗余、提高重用性

如何用继承 单继承

运行结果

class animal(): print("我是动物") class pet(animal): print("我是宠物") class cat_pet(pet): print("我是宠物猫") cat = cat_pet

运行结果:

我是动物 我是宠物 我是宠物猫 多继承 class animal(): print("我是动物") class pet(): print("我是宠物") class cat_pet(pet,animal): print("我是宠物猫") cat = cat_pet

运行结果:

我是动物 我是宠物 我是宠物猫

使用魔法方法__bases__方法可以获取子类继承的类 如果你还想了解其他的魔法方法,请看这篇文章 Python 高级用法 实例:

class animal(): print("我是动物") class pet(): print("我是宠物") class cat_pet(pet,animal): print("我是宠物猫") print(cat_pet.__bases__)

运行结果:

我是动物 我是宠物 我是宠物猫 (, ) 新式类、经典类 继承了object的类以及该类的子类,都是新式类。

在Python3中如果一个类没有继承任何类,则默认继承object类。因此python3中都是新式类

没有继承object的类以及该类的子类,都是经典类。在Python2中如果一个类没有继承任何类,不会继承object类。因此,只有Python2中有经典类。 继承与抽象

抽象:通过抽象可以得到类,抽象是一种分析的过程。可以从猪、猫、狗等中,可以抽象出一个动物类。先分析、抽象之后,就可以通过继承,在程序上实现这个结构。

class Animals: pass class Pig(Animals): pass class Dog(Animals): pass class Cat(Animals): pass 派生类

概念:派生,就是在子类继承父类的属性的基础上,派生出自己的属性。子类有不同于父类的属性,这个子类叫做派生类。通常情况下,子类和派生类是同一个概念,因为子类都是有不同于父类的属性,如果子类和父类属性相同,就没必要创建子类了。 实例1:

class Animals: pass class Dog(Animals): pass

这时候Dog类不是派生类 实例2:

class Animals: def __init__(self, name): self.name = name def walk(self): print('我会走') class Dog(Animals): #Dog类派生出bite功能 #派生:狗有咬人的技能 def bite(self): print('我会咬人')

这个类就是派生类

组合

除了继承之外,还有一种提高重用性的方式:组合 组合指的是,在一个类A中,使用另一个类B的对象作为类A的数据属性(特征)(变量),称为类的组合 比如:人和手机,人想要有打电话的功能,想要打电话,就需要用到手机,人想要用到手机里面的打电话功能,肯定不能用继承,人继承手机就非常尴尬了,这时候就可以用到组合。

class Mobile(): def __init__(self, name): self.name = name def call(self): print('我要打电话') class People(): def __init__(self, name, mobile): self.name = name self.mobile = mobile mobile = Mobile('华为') people = People('小白', mobile) people.mobile.call()

运行结果:

我要打电话

继承建立了派生类和基类的关系,是一种是的关系,比如白马是马,人是动物。

组合建立了两个类之间’有’的关系,比如人有手机,然后人可以使用手机打电话。

属性查找顺序

对象查找属性的顺序:对象自己的 - > 所在类中 -> 找父类 - >父类的父类 ->Object

重写

当子类出现了与父类名称完全一致的属性或是方法,子类就会重写父类的方法 实例:

class Person(object): def __init__(self,name,age): self.name = name self.age = age def speak(self): print("Hello") class student(Person): def speak(self): print("world") Jack = student('jack',19) Jack.speak()

运行结果:

world 子类中访问父类的内容

方式1: super(当前类名称,self).你要调的父类的属性或方法 方式2: super().你要调的父类的属性或方法 方式3: 类名称.你要调的父类的属性或方法(self) 实例:

class Person(object): name = "jack" def __init__(self,age): self.age = age def speak(self): print("Hello") class student(Person): def speak(self): print(super(student,self).name) Jack = student(19) Jack.speak()

运行结果:

jack 子类初始化基类的私有成员

当你继承一个现有的类,并且你覆盖了父类的init方法时,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数 实例:

class Person(object): def __init__(self,name,age): self.name = name self.age = age def speak(self): print("Hello") class student(Person): def __init__(self,name,age,grade): super(student,self).__init__(name,age) self.grade = grade def speak(self): print("world") Jack = student('jack',19,2) Jack.speak()

运行结果:

world 类的mro方法

mro方法可以列出类继承的所有父类,并且有顺序。调用mro方法要用类名.mro 实例:

class Person(object): name = "jack" def __init__(self,age): self.age = age def speak(self): print("Hello") class student(Person): def speak(self): print(super(student,self).name) Jack = student(19) print(student.mro())

运行结果:

[, , ] 菱形继承

类的继承图是从一个类开始继承,继承到顶点时,多个类继承同一个顶点,这种就叫做菱形继承。菱形继承查找类时就用广度查找。 遇到共同父类时就广度 经典类都是深度优先 实例:

class A(object): print("我是A") class B(A): print("我是B") class C(A): print("我是C") class D(B,C): print("我是D") d = D()

运行结果:

我是A 我是B 我是C 我是D 多态 什么是多态

Python中多态是指一类事物有多种形态。比如动物有多种形态,人,狗,猫,等等。文件有多种形态:文本文件,可执行文件。

为什么要用多态

①增加了程序的灵活性 以不变应万变,不论对象千变万化,使用者都是同一种形式去调用,如func(animal) ②增加了程序额可扩展性 通过继承animal类创建了一个新的类,使用者无需更改自己的代码,还是用func(animal)去调用 实例:

import abc class Animals(metaclass=abc.ABCMeta): @abc.abstractmethod def talk(self): pass class People(Animals): def talk(self): print('People is talking') class Cat(Animals): def talk(self): print('Cat is miaomiao') class Dog(Animals): def talk(self): print('Dog is wangwang') cat1 = Cat() dog1 = Dog() peo1 = People() # peo、dog、pig都是动物,只要是动物肯定有talk方法 # 于是我们可以不用考虑它们三者的具体是什么类型,而直接使用 peo1.talk() dog1.talk() peo1.talk() # 定义一个统一的接口来访问 def func(obj): obj.talk() func(cat1)

运行结果:

People is talking Dog is wangwang People is talking Cat is miaomiao

要理解什么是多态,首先要对数据类型再做一点说明、在定义一个class的时候,实际上就定义了一种数据类型,通常,定义的数据类型和python自带的数据类型(例如string、list、dict)没什么区别

a = list() a 是list类型 b = animal() b 是animal 类型 c = Dog() c 是 Dog 类型

一个变量是否为某个类型可以用isinstance()判断 实例:

class Animal: def run(self): print("Animal is runing...") class Cat(Animal): def run(self): print("Cat is runing...") class Dog(Animal): def run(self): print("Dog is runing...") c = Dog() c.run() print(isinstance(c,Animal)) print(isinstance(c,Dog))

运行结果:

Dog is runing... True True

因为Dog是从Animal继承的,当创建了一个Dog的实例c时,认为c的数据类型是Dog没错,但c同时也是Animal,Dog本来也是Animal的一种 所以,在继承关系中,如果一个实例的数据类型是某个子类,那么它的数据类型也可以看做事是父类,但是,反过来就不可以

>>> b = Animal() >>> isinstance(b,Dog) False

Dog可以看成Animal,但Animal不可以看作Dog 要理解多态的好处,还需要编写一个函数,这个函数接收一个Amimal类型的变量

def run_twice(animal): animal.run() animal.run()

当传入Animal的实例时,run_twice就打印出:

>>> run_twice(Animal()) Animal is runing... Animal is runing...

当传入Dog的实例时,run_twice()就打印出

>>> run_twice(Dog()) Dog is runing... Dog is runing...

当传入Cat的实例时,run_twice()就打印出

>>> run_twice(Cat()) Catis runing... Catis runing...

现在,如果定义一个Tortoise类型,也从Animal派生

class Tortoise(Animal): def run(self): print('Tortoise is running slowly')

当调用run_twice()时传入Tortoise实例

>>> run_twice(Tortoise()) Tortoise is running slowly Tortoise is running slowly

  大家会发现新增了一个Animal的子类,不必对run_twice()做任何修改,实际上,任何依赖Animal作为参数的函数或者方法都可以不加修改地正常运行,原因就在于多态。   多态地好处就是,当传入Dog,Cat,Tortoise等时,只需要接收Animal类型就可以了,因为Dog、Cat、Tortoise等都是Animal类型,然后按照Animal类型进行操作。由于Animal类型有run()方法,因此传入的任意类型,只要是Animal或者Animal的子类,就会自动调用实际类型的run()方法,这就是多态的意思。   对于一个变量,用户只需要知道它是Animal类型,无须确切的知道它的子类型,就可以放心的调用run()方法,而具体调用的run()方法是作用在Animal、Dog、Cat、Tortoise对象上,由运行时该对象的确切类型决定,这就是多态真正的威力,调用方法只管调用,不管细节,而当新增一种Animal的子类时,只要确保run()方法编写正确,不用管原来的代码是如何调用的,这就是著名的"开闭"原则,包括以下两点。 1、对扩展开放:允许新增Animal子类 2、对修改封闭:不需要修改依赖Animal类型的run_twice()等函数 下一篇:Python 类与对象编程详解五(特殊成员)



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3