Python入门系列(四)——面向对象
Python的定位是一种解释型、面向对象、动态数据类型的高级程序设计语言,借助python你可以最大程度上体现类似JAVA面向对象的特性(封装、继承、多态)。但事实上,它同样面向过程,更现实点说,许多非专业从事开发的python使用者,把python使用的绝大部分时间停留在了面向过程上。
目录:
一、类(Class)的创建
二、面向对象的特性
一句话概括下面向对象的知识点:类(Class)是一个抽象的模板,他通过实例化具象成为一个对象。类中定义的函数叫做方法,类可以通过继承父类获取父类的属性和方法,emmmm,简单说,就这些。
一、类(Class)的创建
1、构造方法和析构方法
说起创建一个类,不得不提一下构造方法和析构方法,如果从事的行业是专业开发的话,这些细节会决定你的命运。
其实,即使你不创建构造方法或析构方法,系统也会为你自动调用。析构方法自动调用还说得过去,def __del__(self)
即使不写,你在del()一个对象是,它也会自动调用并完美删除对象。但是、构造方法的自动调用意味着什么呢?如果你是一个需要传入参数的类,在创建对象时将无法正常创建,无参数传入,值为空值。
class People(object): #这里完全可以写成class People: 如果作为子类的话,object的位置写被继承父类
#如果没有继承类,则可不写默认继承object类,这是所有类最终都会继承的类
#另外,类的名称默认大写首字母,这也是习惯约束,非规范约束
def __init__(self,a,b): #构造函数
self.name=a
self.age=b
def whoami1(self):
print('name is {},age is {}'.format(self.name,self.age))
def whoami2(self):
return self.name,self.age
rabbit=People('rabbit',18) #实例化对象rabbit
rabbit.whoami1()
(m,n)=rabbit.whoami2()
print('name is {},age is {}'.format(m,n))
#输出:
name is rabbit,age is 18
name is rabbit,age is 18
这里顺便插一句,类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self,还是那句话,只是习惯约束,不是规范约束,你可以按照喜好来,只要你记得它是什么。
2、方法/属性类型
即特殊、private、protected、public 方法/属性。其实,我看大家在python中很少提这些概念,但请允许我按照自己的理解结合其他语言的标准掰扯两句。
在python中,首先是一种以双下划线开头双下划线结尾的属性或方法,是python自带的特殊方法,即专有方法。如:__init__
private方法或属性,以双下划线开头,只允许内部调用,无法被继承,无法被外部访问,但可通过在类内自定义get()或set()方法,实现间接访问或修改。
protected方法或属性,以单下划线开头,允许被继承,无法被外部访问。
public方法或属性,允许被继承,允许外部访问,我们上面创建的就属于public方法。
纯理论的内容,不做代码演示,稍后捎带提及。
二、面向对象的特性
我们先简单梳理下概念,然后借助一个实例解读。
1、封装
封装即将属性和方法封装到一个抽象的类中,封装的意义就在于:将不需要外面调用的方法设置为私有方法,封装好逻辑操作,在调用类的方法时,只需调用公有方法即可。
2、继承
继承就是继承可以把父类的所有功能都直接拿过来,这样就不必重零做起,子类只需要新增自己特有的方法,也可以把父类不适合的方法覆盖重写,python不管是单继承还是多继承都完美支持,继承的意义在于:实现代码的重用,相同的代码不需要重复的编写
3、多态
以继承和重写父类方法为前提,多态是调用方法的技巧,不会影响到类的内部设计,意义在于:不同的子类对象调用相同的父类方法,产生不同的执行结果。
emmmm,想对你说的话,都写到注释里啦~
class People(object):
def __init__(self,a,b): #运算符重载
self.name=a
self.age=b
def whoami(self):
print('name is {},age is {}'.format(self.name,self.age))
def sex(self): #写个方法,供子类重写,重写会覆盖父类
print('I don\'t know its gender')
class Dota: #特地写了个上面也不继承的类,加深下印象
def dota(self): #什么?没有构造函数?因为我没有参数传入吖
print('Dota is more important than girls ')
#Dota五人黑!1=4!来的密!!!!!
class Boy(People,Dota): #这是一个多继承的例子
#什么?还没有构造函数?构造函数继承自父类
def sex(self): #重写方法,这就是多态实现的前提
print('Of course,he is a boy!')
class Girl(People): #这是一个单继承的例子
def __init__(self,a,b,c): #参数不够用?没关系,重写构造函数
self.name=a
self.age=b
self.say=c
def sex(self): #重写方法,这就是多态实现的前提
print('Of course,she is a girl! and she says "{}!"'.format(self.say))
rabbit=Boy('rabbit',18) #实例化对象rabbit
carrot=Girl('carrot',14,'hello') #实例化对象carrot
rabbit.whoami()
rabbit.sex() #实现多态
rabbit.dota()
print('\n')
carrot.whoami()
carrot.sex() #实现多态
#输出:
name is rabbit,age is 18
Of course,he is a boy!
Dota is more important than girls
name is carrot,age is 14
Of course,she is a girl! And she says "hello!"
emmmm,好像关于封装没有定义私有方法唉,因为例子没有涉及到复杂逻辑,但没关系,本身将方法和属性写在抽象类中这个构成,就是封装啦!
补充 : 运算符重载
关于构造函数位置提到的运算符重载,我们在稍微拓展一下,我们在下方列举了python中常见的专有方法,这个概念我们在上一小节提过了,所谓运算符重载,就是重写这些专有方法,在不改变原有功能的前提下,使它变得更加迎合自己的需求和习惯。
__init__ : 构造函数,在生成对象时调用
__del__ : 析构函数,释放对象时使用
__repr__ : 打印,转换
__setitem__ : 按照索引赋值
__getitem__: 按照索引获取值
__len__: 获得长度
__cmp__: 比较运算
__call__: 函数调用
__add__: 加运算
__sub__: 减运算
__mul__: 乘运算
__truediv__: 除运算
__mod__: 求余运算
__pow__: 乘方
如果这么说有点抽象,这里举个小例子:
class sum(object):
def __init__(self,a):
self.a=a
def __add__(self, other): #重写加法运算,变成乘法
return self.a*other
def __sub__(self, other): #重写减法运算,变成加法
return self.a+other
s=sum(5)
m=s+5
n=s-5
print('求和运算结果:%s'%m)
print('求差运算结果:%s'%n)
#输出
求和运算结果:25
求差运算结果:10
我们可以看到上面的结果,四则运算彻底被玩坏,加法变成了乘法,减法变成了加法,这个重写专有函数的过程,就叫做运算符重载,当然,我这是一个反向教材,我们正常的重载自然是朝向更好的方向,比如构造函数的重载。