文章目录
第一节:关于类
面向对象
- 面向对象
之前我们介绍过函数,函数是对程序语句的封装与复用;
类是则是对变量
和函数
的封装和复用,封装有机关联的变量和函数为类,变量和函数,称为类的属性
和方法
;
由于类比函数的封装又提高了一个层次,因此复用性也得到进一步提升;(试想一下维护100个函数直观容易,还是维护5个类直观容易呢?)
面向过程的编程是以函数
为核心的,而面向对象的编程是以类
为核心的,一切功能的实现,都是通过创建一个司职该功能的类的实例,进而调用其方法去予以实现的;
以类为核心的、面向对象的编程,提高了代码的模块化程度,便于大规模协作的开展;
在面向对象的程序开发(Object-Oriented-Programming或OOP)中,架构师的工作,往往只是模块拆分、接口定义、实例组装,类内部的具体方法接口的实现,则交由其他人去完成;
现如今的高级语言,基本都是面向对象的;
面向对象的三大特性:封装
、继承
、多态
;
另一种提法是四大特性,即三大特性的基础上,再加上一个抽象
;
- 封装
封装就是将常用的代码段封装为函数,常用的函数封装为类,常用的类封装为模块与类库;
封装提高了代码的可维护性和复用性,同时也更利于开源与传播;
封装性催生了模块化,便于大规模协作开发的开展;
- 继承
正如自然界中,动物=>人=>程序员的关系,人是动物类的一个分支,程序员是人的一个分支,在编程中,我们可以通过继承来表达这样的关系;
动物类是人类的父类(又叫超类),人是程序员的父类;
动物类的共同特征,如都有生命、都会新陈代谢、都会死亡等等,在定义人这个类时,是无需重复声明的,这样就在一层层的继承中,节省了大量的代码;
有继承就有发展,否则继承就没有意义;
【编程中的继承】
在编程中,发展体现为:
①在父类的基础上增加新的属性与方法;
②重写(或者叫覆写、覆盖)父类方法;
对父类方法的覆写,又可以分为【颠覆式】和【改良式】两种;
【颠覆式】覆写是指子类全盘否定父类方法实现,由子类自己做一个全新的实现;
【改良式】覆写是指子类先将父类的实现拿过来,再进行新的拓展;
- 多态
在继承的基础上,一个父类会有不同的子类实现,如战士父类可以有骑兵、步兵、弓弩手等不同子类;
多态,即【一个父类可以有多种不同的子类形态】;
【多态的共性与个性】
在多态中,同源子类之间,既存在共性,又存在个性,共性与个性各有其用处;
例如骑兵、步兵、弓弩手都是战士的子类,因此他们都有进攻方法与防守方法,这就是【共性】;
而他们的具体进攻方法各自有不同的实现,骑兵冲锋、步兵肉搏、弓弩手射箭,这就是【个性】;
在一支由不同兵种组成的军队中(即战士实例的集合),当总司令下达全体进攻的命令时,不同兵种做何种具体形式的进攻(个性)是不重要的,重要的是共性;
当司令想要采用一些细腻的战术时,比如先进行一轮齐射,再由骑兵进行一轮踩踏,最后上步兵打扫战场,此时不同兵种的差异化进攻方式则展现其价值,此时个性变得重要;
共性是由父类所带来的,个性是由子类覆写所带来的;
- 抽象
在继承和多态中,如果父类不对某个方法做任何实现,只是留白,具体的实现交由子类自己去完成,这时我们称该方法是抽象的;
如果一个父类中的所有方法都是抽象的,我们就称该类为一个【接口】;
抽象是一种艺术,架构师就是这种艺术的玩弄者;
架构师所做的工作,就是拆分模块,定义接口,完成预组装,形成一具没有血肉的架,然后将填充的工作下放到团队中的不同程序员,填充完成之日即是项目大厦落成之时;
类的封装
例:
-
封装一个人的类Person,需求如下:
1、封装以下属性:姓名、年龄、存款
2、封装自我介绍方法,陈述以上属性
3、创建一个人,设置其基本信息
4、打印此人信息
5、令此人进行自我介绍
# 封装一个Person类,将与人有关的属性、方法组合在一起,以便将来复用
class Person:
# 属性定义和默认值
name = "林阿华"
age = 20
rmb = 50
# 构造方法:外界创建类的实例时会调用
# 构造方法是初始化实例属性的最佳时机
def __init__(self,name,age,rmb):
print("__init__的方法被调用了")
self.name = name
self.age = age
self.rmb = rmb
# 自我介绍方法
# self = 类的实例
def tell(self):
print("我是%s,我%d岁了,我有存款%.2f万元"%(self.name,self.age,self.rmb))
- 创建实例p,并调用Person的tell()方法
# 创建Person类的实例
p = Person("易阿天",60,500)
# 调用实例的tell方法
p.tell()
执行结果:
注意将Person的属性和方法使用一个
标准制表符
缩进在Person的类定义以内;
“_ init _”
是类的构造方法,用于创建类的实例,左右各有两个下划线;
使用PyCharm输入完def __init时系统弹出提示,IDE会自动完成方法的定义;
每个方法在定义时,系统会自动加上一个self
参数在第一个参数位,这个参数代表将来创建的实例本身;
再调用方法时,self
是不必亲自传入的,self是系统用来标识实例本身的;
构造方法的调用形式为:Person(self以外的其它参数)
;
类的私有成员
【成员】就是指类的
属性
和方法
;
【私有】,即不能再类的外界进行访问;
目的是为了保障安全
,如涉及隐私的属性、核心方法实现等;
例:
- 封装一个人的类Person,需求如下:
1、创建一个Person类,添加存款信息
2、保护存款信息,将其设置为私有
3、为存款信息添加保护,使其不能被直接访问
4、增加设置密码功能 ·增加存款查询功能
5、只有输入密码正确的情况下才能查询存款信息
class Person:
# 普通属性与私有属性
name = "林阿华"
age = 20
__rmb = 1000 #(须通过公有方法来访问)
# 私有方法:设置存款
def __setrmb(self,rmb):
self.__rmb = rmb
# 通过普通方法访问私有方法进行存款设置
def setrmb(self,rmb):
pwd = input("请输入设置密码:")
if (pwd == "123456"):
self.__setrmb(rmb)
else:
print("您没有权限")
# 公开一个普通方法,共外界访问私有属性self.__rmb
def getrmb(self):
pwd = input("请输入查询密码:")
if (pwd == "123456"):
return self.__rmb
else:
return "您没有访问权限"
# 普通方法
def tell(self):
print("大家好,我是%s"%(self.name))
- 创建Person实例,并通过公有方法访问私有成员
p = Person()
#通过实例访问类的普通属性
print(p.name)
print(p.age)
# 私有成员不能被直接访问
# print(p.__rmb) # AttributeError: 'Person' object has no attribute '__rmb'
#通过实例访问类的普通方法
p.tell()
#通过普通方法访问私有属性
rmb = p.getrmb()
print("我的存款是:",rmb)
# 通过普通方法访问私有方法
p.setrmb(500)
rmb = p.getrmb()
print("我的存款是:",rmb)
执行结果:
- 注意的几个问题
1、代码中的__rmb
属性、__setrmb
方法都是私有的,在类的外部是无法p.__rmb
进行直接访问的;
2、任何前置两个下划线的成员(属性与方法)都是私有的,只能在类的内部进行访问;
外界访问私有成员的方法是,使用公有方法对外界提供私有成员访问接口
,但在内部先行进行权限校验,如输入密码等,如本例中的setrmb
方法和getrmb
方法;
类的专有方法
- 概述
当类没有继承于任何类时,它默认继承的是 【系统的object】 类
在object类中,定义了许多专有方法,它们的方法名是这样的:_ xxx _
,左右各有两个下划线;
专有方法不是用来给实例直接调用的,而是有其特定的用途,比如_ init _
是在外界调用类名创建实例时调用的;
再比如,当外界print(obj)时,其输出的字符串其实是来源于obj对应的类的_ str _
方法的返回值的;
-
常见专有方法
___init___
: 构造函数,在生成对象时调用
___del___
: 析构函数,释放对象时使用
___str___
:实例的打印样式
___len___
: 获得长度
___ gt___
: 比较大小(对象之间进行算术运算后,应返回一个计算后的结果)
___add___
: 加运算
___sub___
: 减运算
___mul___
: 乘运算
___mod___
: 求余运算
___pow___
: 乘方
例:封装一个更加标准化的Person,需求如下:
在创建对象时打印日志
在对象销毁时打印日志
自定义对象的打印样式
设法统计人的“长度”
# 封装一个Person类,将与人有关的属性、方法组合在一起,以便将来复用
class Person:
# 初始默认的属性
name = "某某某"
age = 0
rmb = 0
# 构造方法:外界创建类的实例时调用 ,构造方法是初始化实例属性的最佳时机
def __init__(self, name, age, rmb):
print("__init__:我被创建了")
self.name = name
self.age = age
self.rmb = rmb
# 析构方法,在对象被删除时调用
def __del__(self):
print("__del__:我被删除了")
# 在对象被打印时,提供一个供打印的字符串
def __str__(self):
return "{name:%s;age:%d;rmb:%.2f}" % (self.name, self.age, self.rmb)
# 返回对象的长度,如何计算长度是自定义的
def __len__(self):
return int(self.rmb)
# 比较当前实例是否大于另一个同类的other实例,比较方法自定义
def __gt__(self, other):
if self.rmb > other.rmb:
return True
else:
return False
# 定义与另一个实例相加的结果
def __add__(self, other):
return Person(self.name + "-" + other.name, min(self.