0
点赞
收藏
分享

微信扫一扫

python学习——【第九弹】

前言

上篇文章 ​​python学习——【第八弹】​​​中介绍了python中的类和对象;这篇文章接着介绍面向对象的三大特征——封装,继承,多态。

python是一种面向对象的语言,面向对象的三大特征就是 封装继承多态。

封装

封装的含义

在程序设计中,封装(Encapsulation)是对具体对象的一种抽象,即将某些部分隐藏起来,在程序外部看不到,可以使其他程序无法调用,可以提高程序的安全性。

封装的目的

1、封装数据:保护隐私

2、封装方法:隔离复杂度(只保留部分接口对外使用)

封装的方法

双下划线开头

  • 封装属性: ​​__attr​
  • 封装方法:​​__func​

封装的实现

封装属性:

#封装age属性
class Student:
def __init__(self,name,age):
self.name=name
self.__age=age #不希望age在类外部被使用
# 使age可以在类内部被调用
def show(self):
print('我的名字:',self.name,';我的年龄:',self.__age)
stu=Student('tom',10)
stu.show()
#我的名字: tom ;我的年龄: 10

# 在类外调用name 和 age
print('我的名字:',stu.name)
#我的名字: tom

print('我的年龄:',stu.age) #出现报错AttributeError: 'Student' object has no attribute 'age'

那应该如何调用这个封装的属性age呢,这里我们可以引入一个dir()查看实例对象可使用的方法属性:

print('实例对象stu可以使用的方法属性有:',dir(stu))   #包含  _Student__age
print('我的年龄是:',stu._Student__age)

python学习——【第九弹】_多态

封装方法:

python学习——【第九弹】_子类_02

同样的,如果我们要调用封装的方法,应该如何做:

class Student:
def __init__(self,name,age):
self.name=name
self.__age=age #不希望age在类外部被使用
# 使age可以在类内部被调用
def __show(self):
print('我的名字:',self.name,';我的年龄:',self.__age)
stu=Student('tom',10)
stu._Student__show()

python学习——【第九弹】_多态_03

由此可见:

Python 的封装并不是真正意义上的外部无法调用,与java ,PHP等语言不同,Python若调用封装的属性或方法,需要在方法和属性前加_类名  

父类与子类的封装

子类的封装属性及方法并不会覆盖父类的封装属性或方法

# 子类封装的方法或属性不会覆盖父类的方法或属性
class A:
__name = 'A' # 变形为_A__name
def get_x(self):
print('父类A:',self.__name)


class A1(A): # A1 继承了 A 的方法和属性
__name='我是父类A的子类A1' #变形为_A1__name
def __say(self):
print('这是子类A1的实例方法')
# A1的实例对象
obj = A1()
# 调用父类A的实例方法
obj.get_x() #父类: A

小结

通过以上的例子我们得出:

  • 类的封装不是真正意义上的“私有化”,而是一个语法上的变形
  • 类的封装属性或方式,其实在定义阶段就已经进行了语法上的变形
  • 类的调用阶段,再进行封装语法已经没有任何用处了

继承

继承的含义

python中的继承机制经常用于创建和现有类功能类似的新类,又或是新类只需要在现有类基础上添加一些成员(属性和方法),但又不想直接将现有类代码复制给新类。也就是说,当我们需要进行类的重复使用时,就可以使用这种继承机制来实现。

继承的实现

Python 中,实现继承的类称为子类被继承的类称为父类;

子类继承父类时,只需在定义子类时,将父类(可以是多个)放在子类之后的圆括号里即可。语法格式如下:

class 类名(父类1, 父类2, ...)
#类定义部分

python中如果类没有指定继承某个类,那么就默认继承的是object类,我们看这个例子:

# 编写Person类,以及继承Person类的两个字类Student和Teacher
class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age
def info(self):
print('姓名:',self.name,'年龄:',self.age)
# Student类
class Student(Person):
def __init__(self,name,age,stu_no):
super().__init__(name,age)
self.stu_no=stu_no
# Teacher 类
class Teacher(Person):
def __init__(self,name,age,teach_year):
super().__init__(name,age)
self.teach_year=teach_year
# 创建学生对象和教师对象
stu1=Student('小明',17,1001)
teacher1=Teacher('张丽',30,6)
stu1.info()
teacher1.info()
'''
姓名: 小明 年龄: 17
姓名: 张丽 年龄: 30
'''

这个例子的继承关系并不复杂,定义一个Person类,没有指定继承某个类默认继承object类(object 类是 Python 中所有类的父类,即要么是直接父类,要么是间接父类);然后定义Person类的两个子类:Student类和Teacher类,分别继承Person类。

python学习——【第九弹】_父类_04

方法重写

如果子类对继承自父类的某个属性或方法不满意,可以在子类中对其(方法体)进行重新编写;子类重写后的方法中可以通过super().xxx()调用父类中被重写的方法:

class Person(object):
def __init__(self,name,age):
self.name=name
self.age=age
def info(self):
print('名字:',self.name,'年龄:',self.age)
class Student(Person):
def __init__(self,name,age,stu_no):
super().__init__(name,age)
self.stu_no=stu_no
#方法重写
def info(self):
super().info() #调用父类的info()
print('学号是:',self.stu_no)
class Teacher(Person):
def __init__(self,name,age,teach_year):
super().__init__(name,age)
self.teach_year=teach_year
#方法重写
def info(self):
super().info()#调用父类的info()
print('教龄是:',self.teach_year)
stu1=Student('小华',17,1001)
teacher1=Teacher('赵丽',40,10)
stu1.info()
'''
名字: 小华 年龄: 17
学号是: 1001
'''
print('\n---------------教职工-------------\n')
teacher1.info()
'''
名字: 赵丽 年龄: 40
教龄是: 10
'''

如果一个类是空类,但是继承了含有方法和属性的类,那么这个类也同样含有父类的方法和属性:

class People:
def __init__(self,name):
self.name =name
def say(self):
print("People类",self.name)

class Animal:
def __init__(self):
self.name = Animal
def say(self):
print("Animal类",self.name)
#People类是Person父类中最近的父类,因此People中的name属性和say()方法会遮蔽 Animal 类中的
class Person(People, Animal):
pass

zhangsan=Person('张三')
zhangsan.say() # People类 张三

可以看到,当 Person 同时继承 People 类和 Animal 类时,People 类在前,因此如果 People 和 Animal 拥有同名的类方法,实际调用的是 People 类中的;由此也可以看出,python支持多继承

object类

前面我们提到:object类是所有类的父类 ,因此所有类都有object类的属性和方法,这里我们再了解一下有关此类的一些方法。

1:内置函数 dir()可以查看指定对象所有的属性

2:object有一个__str__()方法,用于返回一个对于对象的描述 , 对应于内置函数str()经常用于print()方法,帮我们查看对象的信息  所以我们经常会对__str__()进行重写。

内置函数dir()

python学习——【第九弹】_多态_05

__str__()方法 

# 定义方法再输出实例对象
class Student():
def __init__(self,name,age):
self.name=name
self.age=age
# 进行方法重写
def __str__(self):
return '名字是{},年龄是{}'.format(self.name,self.age) #格式化字符串
stu=Student('luky',18)
print(stu,type(stu)) #默认调用__str__()方法,返回的类型就是该方法的内容
# 名字是luky,年龄是18 <class '__main__.Student'>

多态

python是弱类型语言,其最明显的特征就是在使用变量时,无需为其指定具体的数据类型。这会导致一种情况,即同一变量可能会被先后赋值不同的类对象。

class Animal:
def say(self):
print("赋值的是Animal类的实例对象")
class Plant:
def say(self):
print("赋值的是Plant类的实例对象")
a = Animal()
a.say()
# 赋值的是Animal类的实例对象

a = Plant()
a.say()
# 赋值的是Plant类的实例对象

在这个例子中我们看到a 可以被先后赋值为Animal类和Plant类的对象,但这并不是多态。类的多态特性,还要满足以下 2 个前提条件:

1:继承:多态一定是发生在子类和父类之间;

2:重写:子类重写了父类的方法。

但python作为一种动态语言不需要关心该类的继承关系 ,只需要关心该类中是否定义了某些属性方法,我们对上面的例子再进行编写:

class Animal:
def say(self):
print("调用的是 Animal 类的say方法")
class Plant(Animal):
def say(self):
print("调用的是 Plant 类的say方法")
class People(Animal):
def say(self):
print("调用的是 People类的say方法")
a = Animal()
a.say()
# 调用的是 Animal 类的say方法

a = Plant()
a.say()
# 调用的是 Plant 类的say方法

a = People()
a.say()
# 调用的是 People类的say方法

可以看到,Plant类和People都继承Animal类,且各自都重写了父类的 say() 方法。从运行结果可以看出,同一变量 a 在执行同一个 say() 方法时,由于 a 实际表示不同的类的实例对象,因此 a.say() 调用的并不是同一个类中的 say() 方法,这就是多态

我们也可以在类外定义一个函数,通过不同类的实例对象对这一函数的调用,动态的决定调用哪个对象的方法:

class Flower:
def color(self):
print('花是五颜六色的')
class Rose(Flower):
def color(self):
print('玫瑰是红色的')
class Moli(Flower):
def color(self):
print('茉莉是白的的')
class Plant:
def color(self):
print('花属于植物')
# 定义一个函数
def fun(obj):
obj.color()
# 调用函数
fun(Flower()) #花是五颜六色的
fun(Rose()) #玫瑰是红色的
fun(Moli()) #茉莉是白的的
print('-----------无任何继承关系的植物类-------\n')
fun(Plant()) #花属于植物 虽然植物类和其他定义了的类无继承关系,但是在该类中定义了color实例方法,因此会在调用时执行该方法

由此:

多态可以简单理解为:具有多种形态  ,它指的是即便不知道一个变量所引用的对象到底是什么类型, 仍然可以通过这个变量调用方法; 在运行过程中根据变量所引用的对象的类型 , 动态的决定调用哪个对象中的方法:

每篇一语

星光不负赶路人!

如有不足,感谢指正!




举报

相关推荐

0 条评论