一、封装
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)怎样定义枚举类?