昨天说了面向对象三大特性的继承,今天来说多态和封装,很多其他程序员说python不支持多态没有多态,并不是python没有多态,而是python处处皆多态。今天的讲课重点会放在封装。

本篇导航:

一、多态

1、多态

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

水有多种形态:冰 水雾 水

动物有多种形态:人,狗,猪

import abc
class Animal(metaclass=abc.ABCMeta): #同一类事物:动物
@abc.abstractmethod
def talk(self):
pass class People(Animal): #动物的形态之一:人
def talk(self):
print('say hello') class Dog(Animal): #动物的形态之二:狗
def talk(self):
print('say wangwang') class Pig(Animal): #动物的形态之三:猪
def talk(self):
print('say aoao')

多态

文件有多种形态:文本文件,可执行文件

import abc
class File(metaclass=abc.ABCMeta): #同一类事物:文件
@abc.abstractmethod
def click(self):
pass class Text(File): #文件的形态之一:文本文件
def click(self):
print('open file') class ExeFile(File): #文件的形态之二:可执行文件
def click(self):
print('execute file')

文件

2、多态性

多态性是指在不考虑实例类型的情况下使用实例

在面向对象方法中一般是这样表述多态性:

向不同的对象发送同一条消息,不同的对象在接收时会产生不同的行为(即方法)。也就是说,每个对象可以用自己的方式去响应共同的消息。所谓消息,就是调用函数,不同的行为就是指不同的实现,即执行不同的函数。

比如:老师.下课铃响了(),学生.下课铃响了(),老师执行的是下班操作,学生执行的是放学操作,虽然二者消息一样,但是执行的效果不同

peo=People()
dog=Dog()
pig=Pig() #peo、dog、pig都是动物,只要是动物肯定有talk方法
#于是我们可以不用考虑它们三者的具体是什么类型,而直接使用
peo.talk()
dog.talk()
pig.talk() #更进一步,我们可以定义一个统一的接口来使用
def func(obj):
obj.talk()

多态性

3、鸭子类型

Python崇尚鸭子类型,即‘如果看起来像、叫声像而且走起路来像鸭子,那么它就是鸭子’python程序员通常根据这种行为来编写程序。例如,如果想编写现有对象的自定义版本,可以继承该对象也可以创建一个外观和行为像,但与它无任何关系的全新对象,后者通常用于保存程序组件的松耦合度。

例1:利用标准库中定义的各种‘与文件类似’的对象,尽管这些对象的工作方式像文件,但他们没有继承内置文件对象的方法

例2:序列类型有多种形态:字符串,列表,元组,但他们直接没有直接的继承关系

例子

二、封装

封装:隐藏对象的属性和实现细节,仅对外提供公共访问方式。

好处:

1、 将变化隔离;

2.、便于使用;

3.、提高复用性;

4.、提高安全性;

封装原则:

1、 将不需要对外提供的内容都隐藏起来;

2、 把属性都隐藏,提供公共方法对其访问。

1、私有变量

#其实这仅仅这是一种变形操作
#类中所有双下划线开头的名称如__x都会自动变形成:_类名__x的形式: class A:
__N=0 #类的数据属性就应该是共享的,但是语法上是可以把类的数据属性设置成私有的如__N,会变形为_A__N
def __init__(self):
self.__X=10 #变形为self._A__X
def __foo(self): #变形为_A__foo
print('from A')
def bar(self):
self.__foo() #只有在类内部才可以通过__foo的形式访问到. #A._A__N是可以访问到的,即这种操作并不是严格意义上的限制外部访问,仅仅只是一种语法意义上的变形

私有变量

特点:

1、类中定义的__x只能在内部使用,如self.__x,引用的就是变形的结果。

2、这种变形其实正是针对外部的变形,在外部是无法通过__x这个名字访问到的。

3、在子类定义的__x不会覆盖在父类定义的__x,因为子类中变形成了:_子类名__x,而父类中变形成了:_父类名__x,即双下滑线开头的属性在继承给子类时,子类是无法覆盖的。

注意:

这种机制也并没有真正意义上限制我们从外部直接访问属性,知道了类名和属性名就可以拼出名字:_类名__属性,然后就可以访问了,如a._A__N

2、私有方法

在继承中,父类如果不想让子类覆盖自己的方法,可以将方法定义为私有的

#正常情况
class A:
def fa(self):
print('from A')
def test(self):
self.fa() class B(A):
def fa(self):
print('from B') b=B()
b.test()
#from B #把fa定义成私有的,即__fa
class A:
def __fa(self): #在定义时就变形为_A__fa
print('from A')
def test(self):
self.__fa() #只会与自己所在的类为准,即调用_A__fa class B(A):
def __fa(self): #在定义时就变形为_B__fa
print('from B') b=B()
b.test()
#from A

私有方法

3、扩展性

封装在于明确区分内外,使得类实现者可以修改封装内的东西而不影响外部调用者的代码;而外部使用用者只知道一个接口(函数),只要接口(函数)名、参数不变,使用者的代码永远无需改变。

#类的设计者
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏了内部的实现细节,此时我们想求的是面积
return self.__width * self.__length #使用者
r1=Room('卧室','egon',20,20,20)
r1.tell_area() #使用者调用接口tell_area #类的设计者,轻松的扩展了功能,而类的使用者完全不需要改变自己的代码
class Room:
def __init__(self,name,owner,width,length,high):
self.name=name
self.owner=owner
self.__width=width
self.__length=length
self.__high=high
def tell_area(self): #对外提供的接口,隐藏内部实现,此时我们想求的是体积,内部逻辑变了,只需求修该下列一行就可以很简答的实现,而且外部调用感知不到,仍然使用该方法,但是功能已经变了
return self.__width * self.__length * self.__high #对于仍然在使用tell_area接口的人来说,根本无需改动自己的代码,就可以用上新功能
r1.tell_area()

扩展性

4、property属性

'''
例一:BMI指数(bmi是计算而来的,但很明显它听起来像是一个属性而非方法,如果我们将其做成一个属性,更便于理解) 成人的BMI数值:
过轻:低于18.5
正常:18.5-23.9
过重:24-27
肥胖:28-32
非常肥胖, 高于32
  体质指数(BMI)=体重(kg)÷身高^2(m)
  EX:70kg÷(1.75×1.75)=22.86
'''
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**2) p1=People('egon',75,1.85)
print(p1.bmi)

BMI指数

import math
class Circle:
def __init__(self,radius): #圆的半径radius
self.radius=radius @property
def area(self):
return math.pi * self.radius**2 #计算面积 @property
def perimeter(self):
return 2*math.pi*self.radius #计算周长 c=Circle(10)
print(c.radius)
print(c.area) #可以像访问数据属性一样去访问area,会触发一个函数的执行,动态计算出一个值
print(c.perimeter) #同上
'''
输出结果:
314.1592653589793
62.83185307179586
'''
#注意:此时的特性area和perimeter不能被赋值
c.area=3 #为特性area赋值
'''
抛出异常:
AttributeError: can't set attribute
'''

圆的周长和面积

将一个类的函数定义成特性以后,对象再去使用的时候obj.name,根本无法察觉自己的name是执行了一个函数然后计算出来的,这种特性的使用方式遵循了统一访问的原则

5、setter,deleter

class A:
def __init__(self):
self.__x = 10
#返回__x的值
@property
def x(self):
return self.__x
#修改__x的值
@x.setter
def x(self,new_x):
self.__x = new_x
#删除__x的值
@x.deleter
def x(self):
del self.__x
a = A()
print(a.x)
a.x = 20
print(a.x)
del a.x
print(a.x)

私有属性的查看修改删除

6、classmethod,staticmethod

class Student:
f = open('student', encoding='utf-8')
def __init__(self):
pass def func(self):
pass @classmethod #类方法:默认参数cls,可以直接用类名调用,可以与类属性交互
def show_student_info_class(cls):
for line in cls.f:
name, sex = line.strip().split(',')
print(name, sex) @staticmethod #静态方法 : 让类里的方法直接被类调用,就像正常的函数一样
def show_student_info_static():
f = open('student', encoding='utf-8')
for line in f:
name, sex = line.strip().split(',')
print(name, sex) Student.show_student_info_class()
Student.show_student_info_static()

类方法和静态方法

海宝,男
海博,女
海娇,男
海燕,女
海东,男
海峰,男

student文件

相同:都可以直接被类调用,不需要实例化

不同:类方法必须有一个cls参数表示这个类,可以使用类属性;静态方法不需要,静态方法不能直接使用

普通方法 默认有一个self对象传进来,并且只能被对象调用——绑定到对象

类方法 默认有一个cls传进来表示本类,并且可以被类和对象(不推荐)调用——绑定到类

静态方法 没有默认参数,并且可以被类和对象(不推荐)调用——非绑定

python进阶(6):多态,封装的更多相关文章

  1. Python进阶(十六)----面向对象之~封装,多态,鸭子模型,super原理(单继承原理,多继承原理)

    Python进阶(十六)----面向对象之~封装,多态,鸭子模型,super原理(单继承原理,多继承原理) 一丶封装 , 多态 封装:            将一些东西封装到一个地方,你还可以取出来( ...

  2. Python进阶-XVII 非python的接口类、多态、python自己的封装

    1.python模拟java中的接口类 python中是没有接口类的概念的,因为它支持多继承,但是java不能,所以就提出一个接口类的概念 java : 面向对象编程 设计模式 —— 接口 接口类 : ...

  3. python面向对象之继承/多态/封装

    老师说,按继承/多态/封装这个顺序来讲. 子类使用父类的方法: #!/usr/bin/env python # coding:utf-8 class Vehicle: def __init__(sel ...

  4. python面向对象 : 抽象类(接口类),多态,封装(私有制封装)

    一. 抽象类(接口类) 与java一样, python也有抽象类的概念但是同样需要借助模块实现,抽象类是一个特殊的类, 它的特殊之处在于只能被继承, 不能被实例化. 从设计角度去看, 如果类是从现实对 ...

  5. python进阶01 面向对象、类、实例、属性封装、实例方法

    python进阶01 面向对象.类.实例.属性封装.实例方法 一.面向对象 1.什么是对象 #一切皆对象,可以简单地将“对象”理解为“某个东西” #“对象”之所以称之为对象,是因为它具有属于它自己的“ ...

  6. Python进阶之面向对象编程

    面向对象编程——Object Oriented Programming,简称OOP,是一种程序设计思想.OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数. 面向过程的程序设计把计算机 ...

  7. Python进阶(十五)----面向对象之~继承(单继承,多继承MRO算法)

    Python进阶(十五)----面向对象之~继承 一丶面向对象的三大特性:封装,继承,多态 二丶什么是继承 # 什么是继承 # b 继承 a ,b是a的子类 派生类 , a是b的超类 基类 父类 # ...

  8. Python进阶(十三)----面向对象

    Python进阶(十三)----面向对象 一丶面向过程编程vs函数式编程vs面向对象编程 面向过程: ​ 简而言之,step by step 一步一步完成功能,就是分析出解决问题所需要的步骤,然后用函 ...

  9. 组合&多态&封装

    目录 组合&多态&封装 一.组合 1.1什么是组合 1.2 为什么要用组合 1.3 如何使用组合 1.4 继承和组合都在什么时候用 二.多态与多态性 2.1 什么是多态 2.2 如何用 ...

随机推荐

  1. 【LeetCode】89. Gray Code

    题目: The gray code is a binary numeral system where two successive values differ in only one bit. Giv ...

  2. 【Android Developers Training】 9. 覆盖于布局之上的Action Bar

    注:本文翻译自Google官方的Android Developers Training文档,译者技术一般,由于喜爱安卓而产生了翻译的念头,纯属个人兴趣爱好. 原文链接:http://developer ...

  3. Android --> 常见控件

    1.TextView  主要用于界面上显示一段文本信息 2.Button  用于和用户交互的一个按钮控件 //为Button点击事件注册一个监听器public class Click extends ...

  4. MySQL慢查询日志

    实验环境: OS X EI Captian + MySQL 5.7 一.配置MySQL自动记录慢查询日志 查看变量,也就是配置信息 show (global) variables like '%slo ...

  5. spring +springmvc+mybatis组合springmvc.xml文件配置

    <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.spr ...

  6. database.properties数据源

    jdbc.driver_class=oracle.jdbc.driver.OracleDriverjdbc.connection.url=jdbc:oracle:thin:@localhost:152 ...

  7. Symbol() 的使用方法

    简介:ES5的对象属性名都是字符串,这就很容易造成属性名的冲突,比如一个项目很庞大,又不是一个人开发 的,就有可能会造成变量名冲突,如果有一个独一无二的名字就好了,这样就能从根本上防止属性名冲突.这就 ...

  8. vue.js 组件之间传递数据

    前言 组件是 vue.js 最强大的功能之一,而组件实例的作用域是相互独立的,这就意味着不同组件之间的数据无法相互引用.如何传递数据也成了组件的重要知识点之一. 组件 组件与组件之间,还存在着不同的关 ...

  9. CSS自定义动画

     动画名称  animation-name   :   (动画名称必须与@keyfarmes的名称相对应,因为动画名称是由@keyfarmes定义的) 例如:animation-name:AA:则 @ ...

  10. 百度BAE环境搭建

    一.申请 1.http://bce.baidu.com/index.html 2.购买应用引擎BAE需要实名认证:http://console.bce.baidu.com/qualify/#/qual ...