第12课 封装 继承 多态(2019-12-09)

一、封装

1. 什么是封装?它指的是将对象的状态信息隐藏在对象内部,不允许外部程序直接访问对象的内部信息,而是通过类所提供的方法来实现对内部信息的操作和访问。

2. 封装是面向对象三大特征之一,另外两人是继承和多态。实际在客观世界里,对象的状态都是隐藏在对象内部的,外界无法直接操作和修改。

3. 封装的好处有几点:

1)隐藏类的实现细节。

2) 只能使用预定的方法来访问数据,限制对属性的不合理访问。

3)保证对相信的完整性。

4)便于修改提高代码的可维护性。

4. 封装只要做到以下两点:

1)把对象的属性和实现的细节隐藏起来。

2)把方法暴露出来,让方法为控制对属性和细节的访问。

5 )封装机制范例:Python并没有真正的隐藏机制,其定义的所有成员默认都是公开的,想要隐藏某些成员,只要让该成员的名字以比下划线(__)开头即可。例如:

>>> class User:

def __hide(self):

print("这个方法被隐藏了")

def getname(self):

 return self.__name

def setname(self,name):

if len(name)<3 or len(name)>8:

raise ValueError('用户名长度要在3到8之间')

self.__name=name

name =property(getname,setname)

def setage(self,age):

 if age<18 or age>70:

raise ValueError('用户年龄要在18到70之间')

self.__age =age

def getage(self):

return self.__age

age =property(getage,setage)

>>> u=User()

>>> U.name='ab' # 结果:ValueError: 用户名长度要在3到8之间

>>> u.age=8 #结果:ValueError: 用户年龄要在18到70之间

程序中__name和__age两个变量被隐藏起来了,方法hide也被隐藏起来了。

程序就无法直接访问它们,只能通过setname、getname()、setage()、getage()这些方法进行访问,尝试调用User的hide方法也是会报错的:

>>> u.User__hide()

#结果:AttributeError: 'User' object has no attribute 'User__hide'

二、继承

1. 继承(inheritance)是面向对象的三大特征之一。

1)如果一个类别A“继承自”另一个类别B,就把这个A称为“B的子类别”,而把B称为“A的父类别”也可以称“B是A的超类”。

2)继承可以使得子类别具有父类别的各种属性和方法,而不需要再次编写相同的代码。

3)在令子类继承父类的同时,可以重新定义某些属性,并重写某些方法,即覆盖父类别的原有属性和方法,使其获得与父类别不同的功能。

4)可以为子类别追加新的属性和方法也是常见的做法。 

5)Python的继承是多继承机制,即一个子类可以同时有多个直接父类。

6) 从子类的角度,子类扩展(extend)了父类;从父类的角度,父类派生(derive)了子类。

2. 继承的语法:在定义子类时,将多个父类放在子类之后的圆括号里,前的一个优先级比后一个高。例如:

class subclass (superclass1,superclass2,......)

#定义一个水果的类

>>> class Fruit:

def info(self):

print("这是一个水果,重%g克" %self.weight )

#定义一个食物类

>>> class Food:

def taste(self):

print("不同食物的口感不同")

#定义一个苹果类,继承了水果和食物类

>>> class Apple(Fruit,Food):

pass

#创建一个苹果类的对象

>>> a=Apple()

>>> a.weight =5.6

#调用苹果对象的info()方法,调用苹果对象的taste()方法

>>> a.info()

这是一个水果,重5.6克

>>> a.taste()

不同食物的口感不同

3.  重写父类的方法:大部分情况下,子类都是以父类为基础,额外增加自己的方法。但也有时例外,比如鸟类有飞翔的方法,但是鸵鸟属于鸟类,但是它没有飞翔的方法,这时就需要重写父类的方法。例如:

#定义一个父类Bird

>>> class Bird:

def fly(self):

print("我在天空自由地飞翔...")

#定义一个子类Ostrich

>>> class Ostrich(Bird):

def fly(self):

print("我只能在地上奔跑...")

#创建一个Ostrich类的对象

>>> os=Ostrich()

#调用对象os的fly()方法

>>> os.fly()

#结果:我只能在地上奔跑...

所以就是Ostrich类的fly()方法

4. Python的动态性

1)Ptyhon的动态性就是:类、对象的属性和方法都是可以动态增加、删除和修改的。

2)动态性实例: 如果给一个对象动态添加方法,只是对当前对象有效,如果希望为所有实例都添加该方法,则可通过为类添加方法来实现。

>>> class Cat():

def __init__(self,name):

self.name=name

>>> def walk_func(self):

print('%s慢慢走在一片草地上' %self.name)

>>> d1=Cat('Coco')

>>> d2=Cat('Kitty')

>>> d1.walk() # AttributeError: 'Cat' object has no attribute 'walk'

#为Cat类动态添加walk方法,该方法的第一个参数自动绑定

>>> Cat.walk=walk_func

>>> d1.walk()

#结果: Coco慢慢走在一片草地上

>>> d2.walk()

#结果: Kitty慢慢走在一片草地上

3)__slots__属性:任意事物都有其两面性,Python的动态性固然有其优势,但也给程序带来一定的隐患:程序定义好的类,完全有可能在后面被其它程序修改,如果程序要限制为某个类动态添加属性和方法,则可以使用__slots__属性来指定。

 __slots__属性的值是一个元组,该元组中列出了允许为该类的实例动态添加的属性和方法名。

#定义一个Dog类

>>> class Dog:

#只允许为实例动态添加walk、age、name三个属性或方法

__slots__=('walk','age','name')

def __init__(self,name):

self.name=name

#创建Dog类的对象d

>>> d= Dog('Snoopy')

>>> from types import MethodType

#直接赋值就动态添加了属性

>>> d.walk = MethodType(lambda self:print('%s正慢慢地走在一片草地上...' % self.name),d)

>>> d.age=5

#调用对象的walk属性

>>> d.walk()

Snoopy正慢慢地走在一片草地上...

#如果为对象添加其它的属性,就会报错

>>> d.foo =30# 报错:AttributeError: 'Dog' object has no attribute 'foo'

备注:__slots__属性并不通过类来动态清加属性和方法,因些下面的代码是合法的。

>>>Dog.bar = lambda self: print('abc')

>>>d.bar()

5. 使用type()函数定义类

1)使用Type()函数来查看类的类型:所在Class定义的类,都是type类的实例。

>>> print(type(d1))

#查看变量的类型结果: <class '__main__.Cat'>

>>> print(type(Cat))

#查看类的类型结果:<class 'type'>

2)实际上Python完全允许使用type()函数来创建type对象,它相当于type类的构造器函数。由于Type类的实例就是类,因此Python可以使用type()函数来动态创建类。例如:

>>> def fn(self):

print('我跑得很快')

#使用type()函数定义了一个Dog类

>>> Dog=type('Dog',(object,),dict(walk=fn,age=6))

#创建一个Dog类的对象

>>> d=Dog()

>>> print(type(Dog))

#结果:<class 'type'>

>>> print(type(d)) 

#结果:<class '__main__.Dog'>

>>> d.walk()

fn函数

>>> print(Dog.age)

6.使用metaclass : 如果我们希望创建一批类,全部具有某些特征,可以通过metaclass来实现。这个功能在开发基础性框架时非常有用,程序可以通过metaclass为一批类添加方法。

三、多态

1. 多态性 :对于弱类型的语言来说,变量并没有声明类型,因些同一个变量完全可以在不同的时间表引用不同的对象。当一个这量在调用同一个方法时,完全可能呈现出多种不同的行为。具体呈现哪种行为由变量所引用的对象来决定的。这就是多态(Polymorphism)。例如:

#定义一个Bird类,添加一个move方法,该方法有一个参数field

>>> class Bird():

def move(self,field):

print('鸟在%s上自由自在的飞翔'%field)

#定义一个Dog类,添加一个move方法,该方法有一个参数field

>>> class Dog():

def move(self,field):

print('狗在%s上飞快地奔跑'%field)

#变量x被赋值为Bird类的对象

>>> x=Bird()

#调用x的move方法

>>> x.move('天空')

鸟在天空上自由自在的飞翔

#变量x被赋值为Dog类的对象

>>> x=Dog()

#调用x的move方法

>>> x.move('草地')

狗在草地上飞快地奔跑

2.检查类型:Python提供了两个函数来检查类型,还有一个__bases__属性。

1)issubclass(cls,class_or_tuple): 检查cls是否为后一个类或元组包含的多个类中任意类的子类。它的第一个参数是类名。例如:

>>> print('str是不是ruple 类的子类',issubclass(str,tuple))

#结果str是不是ruple 类的子类 False

2)isinstance(obj,class_or_tuple): 检查obj 是否为一个类或元组中包含的多个类中任意类的对象。它的第一个参数是变量名。列如:

>>> hello ='Hello'

>>> print('hello是不是str类的实例',isinstance(hello,str))

# 结果:hello是不是str类的实例True

3)__bases__: 来查看该类的所有直接父类,例如:

>>> class A:

pass

>>> class B:

pass

>>> class C(A,B):

pass

>>> print('C的所有父类',C.__bases__)

结果:C的所有父类(, )

3.枚举类

1)枚举类定义:某些情况下,类的的对象有限且固定的,比如季节只有春夏秋冬4个,星期只有7天等。这种实例有限且固定的类,在Python中被称为枚举类。

2)程序有两种方法来定义枚举类:(Enum 是enumeration列举的简写形式)

#直接使用Enum列同多个枚举值来创建枚举类:

>>> import enum

>>> Season =

enum.Enum('Season',('SPRING','SUMMER','FALL','WINTER'))

#直接访问指定的枚举

>>> print(Season.SPRING)

#访问成员的变量名

>>> print(Season.SPRING.name)

#访问成员的值

>>> print(Season.SPRING.value)

#通过枚举的变量名和值来访问对象

>>> print(Season['SUMMER'])

Season.SUMMER

>>> print(Season(3))

Season.FALL

通过继承Enum基类来派生枚举类:

>>> import enum

classOrientation(enum.Enum):

# 为序列值指定value值

EAST='东'

SOUTH='南'

WEST='西'

NORTH='北'

definfo(self):

print('这是一个代表方向【%s】的枚举'%self.value)

print(Orientation.SOUTH)

print(Orientation.SOUTH.value)

# 通过枚举变量名访问枚举

print(Orientation['WEST'])

# 通过枚举值来访问枚举

print(Orientation('南'))

# 调用枚举的info()方法

Orientation.EAST.info()

# 遍历Orientation枚举的所有成员

forname,memberinOrientation.__members__.items():

print(name,'=>',member,',',member.value)

四、本节回顾

1) 封装需要做到哪两点?

2)继承的语法是什么?

3)怎样重写父类的方法?

4)什么是多态?

5)怎样定义枚举类?

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

推荐阅读更多精彩内容

  • 初识面向对象 楔子 你现在是一家游戏公司的开发人员,现在需要你开发一款叫做<人狗大战>的游戏,你就思考呀,人狗作战...
    go以恒阅读 923评论 0 6
  • 一、Python简介和环境搭建以及pip的安装 4课时实验课主要内容 【Python简介】: Python 是一个...
    _小老虎_阅读 5,743评论 0 10
  • 参考文献: 《大话设计模式》——吴强 《Python设计模式》——pythontip.com 《23种设计模式》—...
    梁林張斌阅读 2,273评论 0 4
  • 8月22日-----字符串相关 2-3 个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消...
    future_d180阅读 968评论 0 1
  • 写在前面的话 代码中的# > 表示的是输出结果 输入 使用input()函数 用法 注意input函数输出的均是字...
    FlyingLittlePG阅读 2,753评论 0 8