python的class类调用方式和封装思想
作者:王大兴的王兴
一、面向对象思想
面向对象编程(Object Oriented Programming,简称OOP),是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因不仅因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率。
另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
1、面向对象和面向过程
面向对象(Object Oriented,简称OO),是一种程序设计思想,如python和java语言就是一种面向对象的编程语言:
- OOP把对象作为程序的基本单元,一个对象包含了数据和操作数据的函数。
- OOP把程序看做不同对象的相互调用
- OOP的抽象程度比函数要高
面向过程(Procedure Oriented 简称PO),也是一种常见的程序设计思想,如c语言:
- 面向过程的程序设计的核心是过程(流水线式思维),过程即解决问题的步骤,面向过程的设计就好比精心设计好一条流水线,考虑周全什么时候处理什么东西。
- 面向过程编程是把函数看着程序的最基本单元,一个函数包括要处理的数据及算法逻辑
- 面向过程编程是把程序看作不同函数之间的互相调用
- 面向过程编程的抽象层度相对较低
- 优点是:极大的降低了写程序的复杂度,只需要顺着要执行的步骤,堆叠代码即可。
- 缺点是:一套流水线或者流程就是用来解决一个问题,代码牵一发而动全身。
2、面向对象的常见概念
- 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法,对象是类的实例。
- 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。
- 数据成员:类变量或者实例变量, 用于处理类及其实例对象的相关的数据。
- 方法重写:如果从父类继承的方法不能满足子类的需求,可以对其进行改写,这个过程叫方法的覆盖(override),也称为方法的重写。
- 局部变量:定义在方法中的变量,只作用于当前实例的类。
- 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。
- 实例化:创建一个类的实例,类的具体对象。
- 方法:类中定义的函数。
- 对象:通过类定义的数据结构实例。对象包括两个数据成员(类变量和实例变量)和方法。
二、类的创建及调用
1、类的创建
使用 class 关键字来创建一个新类,class 之后为类的名称()并以冒号结尾:
class ClassName(): '''类的帮助信息''' 类体,包括类的变量和方法
类的帮助信息可以通过ClassName.__doc__查看。
下面写一个动物类的案例:
class Animal(): #这些都是类变量,在类中,方法外 nicheng = "动物" #类中的方法参数中必须要有一个self,而且必须是在位置参数的第一位 #实例(对象)变量,在变量前需要有一个self. def info(self): self.age = 0 self.gender = "male" self.weight = 0 self.brand = "xxx" def eat(self): print("站着吃") def sleep(self): print("趴着睡")
2、类对象的创建及使用
A=className()
- 类对象支持两种操作:属性引用和方法引用
- 标准语法:obj.name
针对上文动物类的对象创建及使用:
# 创建对象pig,及使用对象的变量和方法 pig = Animal() cat = Animal() print(pig.age) print(cat.age) cat.sleep() cat.info() print(Animal.nichen) =
3、构造方法的使用
- 构造方法可以实现对实例变量的初始化操作
- __init__的特殊方法(构造方法),在类实例化时会自动调用
- __init__方法可以有参数,参数通过 __init__传递到类的实例化操作上
- 可以不显示地写__init__方法,会默认使用无参数的构造方法
- 如果显示地写了构造方法,则不能再使用无参数的构造方法
class studentInit(): #声明类变量 type1 = '学生' #通过构造方法实现对实例变量的初始化 def __init__(self,age,name,ID): #定义并对实例变量进行初始化 self.age = age self.name = name self.ID = ID #定义第一个方法info,实现对实例变量的初始化 def info(self):#显示对象属性的方法 print("年龄是%d、姓名是%s、学号是%s"%(self.age,self.name,self.ID)) def study(self): score=80 self.ID = "0000000" print("学生%s的学习成绩是:%d"%(self.ID,score)) def play(self): print("学习之余需要玩会游戏!") #创建类的对象(实例化:由抽象到具体) #如果你显示地写了带参数的构造方法,则不再允许使用默认的无参数的构造方法 # TypeError: __init__() missing 3 required positional arguments: 'age', 'name', and 'ID' xiaohua = studentInit(20,"xiaohua","12345678") print(xiaohua.type1) print(xiaohua.age) xiaohua.play() xiaohua.study()
4、self的使用
- 类的方法与普通的函数只有一个特别的区别——它们必须有一个额外的第一个参数名称, 按照惯例它的名称是 self。
- self不是关键字,你可以把它改成其他名称,虽然代码能正常运行,但是还是会提示:Method should have “self” as first argument
- 一般我们也不会这么干,因为你写了,别人都不一定知道是啥意思,大家都已经形成习惯了
- self 代表的是类的实例,代表当前对象的地址,而 self.class 则指向类
三、面向对象的三大特性
三大特性包括:
- 封装
- 继承
- 多态
1、封装
封装是面向对象的特征之一,是对象和类概念的主要特性。
封装也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏
2、继承
- 面向对象的编程带来的主要好处之一是代码的重用,实现这种重用的方法之一是通过继承机制
- 通过继承创建的新类称为子类或派生类,被继承的类称为基类、父类或超类
继承语法是:
class 派生类名(基类名): 类的代码 ...
在python中继承中的一些特点:
- 如果在子类中需要父类的构造方法就需要显式的调用父类的构造方法,或者不重写父类的构造方法。
- 在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。
- python中类的继承分为:单继承和多继承
#引入继承的理念:提取不同类中共有的属性和行为:年龄、性别、颜色、吃鱼、睡、玩 #把提取的属性和行为定义到一个动物类中: #引入继承的理念:提取不同类中共有的属性和行为:年龄、性别、颜色、吃鱼、睡、玩 #把提取的属性和行为定义到一个动物类中: class animal():#父类 def __init__(self,age,sex,color): self.age = age self.sex = sex self.color = color def eat(self): print("吃饭") def sleep(self): print("睡觉") def play(self): print("玩") #可以使用继承的方式来写子类 class cat(animal):#子类 #重写:前提是必须有继承关系,父类的行为再子类中不一定全部通用 #子类有自己的特性,那就把父类的行为重写一下 #方法名保持一致,参数无所谓 def eat(self,food): print("猫吃鱼") # class dog(): # pass # class bear(): # pass #创建一只小猫 c = cat(1,"male","yellow") print(c.color) c.eat("鱼") c.play()
class baba(): def property(self,car,house,money): self.car="10辆蝙蝠战车" self.house="5座大别墅" self.money="一百亿"#调用父类的方法 def smalltroble(self): print("100个wife") def largetroble(self): print("500个儿子和1000个1over") class son(baba): def property(self,car1,house1,money1): baba.car=car1 baba.house=house1 baba.money=money1 print("1辆蝙蝠战车","2座大别墅","30亿") #可换值继承上面父类的构造 def smalltroble(self):#调用子类方法 print("10个wife") def largetroble(self):#调用子类方法 print("50个儿子和10个1over") c=son() c.property("a","2","3")#参数格式必须一样,参数可以不一样 c.largetroble() c.smalltroble()
3、多态
只关心对象的实例方法是否同名,不关心对象所属的类型;
对象所属的类之间,继承关系可有可无;
多态的好处可以增加代码的外部调用灵活度,让代码更加通用,兼容性比较强;
多态是调用方法的技巧,不会影响到类的内部设计。
class Animal(): def run(self): print("Animal Running ……") class Cat(Animal): def run(self): print("Cat Running ……") class Dog(Animal): def run(self): print("Dog Running ……") def runTwice(animal): animal.run() a = Animal() c = Cat() d = Dog() runTwice(a) runTwice(c) runTwice(d) ===运行结果:====== Animal Running …… Cat Running …… Dog Running ……
鸭子多态:
当我们需要传入Dog、Cat等不同子类时,我们只需要接收Animal类型就可以了,因为Dog、Cat都是Animal类型,然后按照Animal类型进行操作即可,由于Animal有run方法,因此传入的任意类型,只要是Animal或其子类,就会自动调用实际类型的run方法
对于静态语言(Java)来说,如果需要传入Animal类型,则形参必须是Animal或其子类对象,否则无法调用run方法
对于动态语言(Python)来说,则不一定要传入Animal类型,我们只需要保证传入的对象包括run方法即可
这就是动态语言的“鸭子类型”,他并不要求严格的继承体系,一个对象只要“看起来像鸭子,走起路来像鸭子,那他就可以被看做是鸭子!”
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。