今天我们继续学习python中类。
目录
1. 面向过程 VS 面向对象
2. 创建类和对象
3. 面向对象的特性
1. 面向过程 VS 面向对象
1.1 编程范式
编程范式:(一组指令的集合,实现方式)
编程是程序员用特定的语法+数据结构+算法组成的代码来告诉计算机如何执行任务的过程。一个程序是程序员为了得到一个任务结果而编写的一组指令的集合,正所谓条条大路通罗马,实现一个任务的方式有很多种不同的方式,对这些不同的编程方式的特点进行归纳总结得出来的编程方式类别,即为编程范式。
1.2面向过程编程(Procedural Programming)
面向过程,使用一系列的指令告诉计算机一步一步的做什么。就是程序从上到下一步步执行,一步步从上到下,从头到尾的解决问题。基本设计思路就是程序一开始是要着手解决一个大的问题,然后把一个大问题分解成很多个小问题或子过程,这些子过程再执行的过程再继续分解直到小问题足够简单到可以在一个小步骤范围内解决。
1.3面向对象编程
面向对象介绍
世界万物, 皆可分类。世界万物,皆为对象,只要是对象,就肯定属于某种品类,只要是对象,就肯定有属性。现实生活中就是这个样子的,面向对象就是按照现实生活的这个规则。
面向对象是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。
面向对象特性
Class类: (属性)# 例如:鸟的属性, 类即是对一类相同属性的对象的抽象、蓝图、原形。
Object对象:(实例)# 例如:一个具体的鸟, 一个对象即是一个类实例化后的实例。
三大特性:
封装:
再类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法。比如人的消化系统、心脏、等封装
继承:一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承
多态:是面向对象的重要特性,简单点说:“一个接口,多种实现”,提供统一接口
面向过程编程与面向的主要区别就是面向对象可以使程序更加容易扩展和易更改。
2. 创建类和对象
2.1 创建类和对象
定义类:(object 所有类的父类)
1classDog(object):# 定义一个类, class是定义类的语法,Role是类名,(object)是新式类的写法
2n =123# 类变量
3
4def__init__(self,name):# 构造函数--传参数用
5# 作用:在实例化时做一些累的初始化的工作。
6self.name = name# 实例变量(静态属性),赋给实例
7# 实例变量作用域就是实例本身
8
9defbulk(self)# 类的方法(动态属性),功能
10print("%s wang wang!"%self.name)
创建实例(对象):
1# 通过一个类创建一个具体对象的过程叫 实例化
2dog1 = Dog("wangcai")# 创建实例,并赋值给变量
3# dog1 是对象,又叫Dog这个类的实例
4
5dog1.bulk()# 调用内部方法
2.2 参数self
初始化一个实例,就需要调用这个类一次:
1dog1 = Dog("wangcai")# 生成一个实例,会自动把参数传给Dog下面的__init__(...)方法
上面创建dog1对象的时候,并没有给self传值,程序也没未报错,是因为,类在调用它自己的init构造函数时已经帮你给self参数赋值了
1dog1= Dog("wangcai") # 此时self 相当于 dog1 , dog1 = Dog(dog1,"wangcai")
当执行dog1 = Dog(“wangcai”)时,python的解释器其实干了两件事:
在内存中开辟一块空间指向dog1这个变量名
调用Dog这个类并执行其中的init__(…)方法,相当于Dog.__init(dog1, “wangcai”), 这么做是为什么呢?是为了把”wangcai”这个值跟刚开辟的dog1关联起来,因为关联起来后,你就可以直接dog1.name这样来调用啦。
所以,为实现这种关联,在调用init__方法时,就必须把dog1这个变量自己也传进去,否则_init_不知道要把那参数跟谁关联呀。 所以这个_init_(…)方法里的,self.name = name等等的意思就是要把这几个值 存到dog1的内存空间里。 把变量dog1也传进去了,再赋值给dog1,这就是self的作用 __init(…)懂了,但后面的那几个函数,噢 不对,后面那几个方法 为什么也还需要self参数么? 不是在初始化实例的时候,就已经把属性跟dog1绑定好了么?
先来看一下上面类中的一个bulk的方法:
1defbulk(self)
2print("%s wang wang!"%self.name)
上面这个方法通过类调用的话要写成如下:
1dog1 = Dog("wangcai")
2dog1.bulk()# #python 会自动帮你转成 dog1.bulk(dog1)
依然没给self传值 ,但Python还是会自动的帮你把dog1 赋值给self这个参数, 为什么呢? 因为,你在bulk(..)方法中可能要访问dog1的一些其它属性呀, 比如这里就访问了dog1的名字,怎么访问呢?你得告诉这个方法呀,于是就把dog1传给了这个self参数,然后在bulk里调用 self.name 就相当于调用dog1.name
总结一下
上面的这个dog1 = Dog(“wangcai”)动作,叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了, 这个对象就叫做实例
刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容
2.3 在谁的内存里?(实例化过程)
上面定义了类Dog,并创建了一个实例dog1,那么类变量和实例变量是什么区别和联系?
类变量没实例化就能打印,存在类的内存里。先找实例本身,实例本身没有就去类里找.实例化只拷贝构造函数,不拷贝类变量和其他方法,那些还只在类的内存中。
所以类变量可以存放所有实例一些共同的属性,以节省空间。比如存放用户信息,用户姓名等属性可以放在实例变量里,国籍都是中国,可以放在类变量里。
但是,类变量和类方法方法不在实例中,调用怎么调用呢?
1dog1.bulk()# 实际是 :Dog.bulk(dog1)
2
3>>> dog1 = Dog('wangcai')
4>>> dog1.bulk()
5wangcai wang wang
6>>> Dog.bulk(dog1)
7wangcai wang wang
所以类里每个函数都必须至少有个self方法
2.4 实例属性的增删改
1dog1.age =3# 增加属性
2deldog1.name# 删除属性
3
4dog1.n ="111"
5# 改类变量,实际不是改类变量,而是在实例里添加一个属性
6
7Dog.n ="ccc"#改变类变量
2.5 析构函数、私有属性和私有方法
析构函数:跟构造函数相反。在实例释放、销毁的时候执行的,通常用于做一些收尾工作,如关闭一些数据库连接、打开的临时文件。如在上面的Dog类中添加一个析构函数
1def__del__(self):
2print("%s 挂了"%self.name)
类的私有属性和私有方法:
现在类的私有属性和私有方法,外边可以直接调用,比如Dog.n。如果不想被调用,在前面加两个下划线,就变成了私有属性或私有方法
3. 面向对象的特性
3.1 封装
前面提到:封装,就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容封装是面向对象的特征之一,是对象和类概念的主要特性。
封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。
3.2 继承
作用:省代码
面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。
通过继承创建的新类称为“子类”或“派生类”。被继承的类称为“基类”、“父类”或“超类”。
继承的过程,就是从一般到特殊的过程。
要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。
示例:
1# class SchoolMember: 经典类写法
2classSchoolMember(object):# 新式类
3def__init__(self,name,age,sex):
4self.name = name
5self.age = age
6self.sex = sex
7deftell(self):
8pass
9def__del__(self):
10'''析构方法'''
11print("\033[31;1mmember [%s] left the school\033[0m"%self.name)
12
13classTeacher(SchoolMember):# 继承父类SchoolMember
14def__init__(self,name,age,sex,salary,course):
15# 如何继承父类的构造函数?直接定义则重构了父类的构造函数
16# 继承父类的构造函数,如下两种写法:
17# SchoolMember.__init__(self,name,age,sex) # 经典类写法
18super(Teacher,self).__init__(name,age,sex)# 新式类写法
19self.salary = salary
20self.course = course
21
22deftell(self):
23print('''
24---- info of Teacher:%s ----
25Name:%s
26Age:%s
27Sex:%s
28Salary:%s
29Course:%s
30'''%(self.name,self.name,self.age,self.sex,self.salary,self.course))
31
32defteach(self):
33print("%s is teaching course [%s]"%(self.name,self.course))
34
35classStudent(SchoolMember):
36def__init__(self,name,age,sex,stu_id,grade):
37super(Student,self).__init__(name,age,sex)
38self.stu_id = stu_id
39self.grade = grade
40
41deftell(self):
42print('''
43---- info of Student:%s ----
44Name:%s
45Age:%s
46Sex:%s
47Stu_id:%s
48Grade:%s
49'''% (self.name, self.name, self.age, self.sex, self.stu_id, self.grade))
50
51defpay_tuition(self,amount):
52print("%s has paid tution for $%s"% (self.name,amount) )
多继承
上面提到了经典类和新式类:
经典类写法:
1class类名:# 定义类
2父类名.__init__(self,*args)# 继承父类构造函数
新式类写法:
1class类名(object):# 定义类
2super(类名,self).__init__(name,*args)# 继承父类
经典类和新式类区别主要体现在多继承上顺序问题,现在规范写法用新式类
继承顺序
在python2和3版本中测试以下代码,分别注释不同类中的构造函数,查看D的实例继承谁的构造函数
1classA:
2def__init__(self):
3print("A")
4classB(A):
5# pass
6def__init__(self):
7print("B")
8classC(A):
9# pass
10def__init__(self):
11print("C")
12classD(B,C):
13# pass
14def__init__(self):
15print("D")
16
17obj = D()
1A# 父类
2/ \
3/ \
4B C# B类和C类继承A
5\ /
6\ /
7D# D多继承B、C
继承查找策略:
广度优先:D->B->C->A(横向先都查完)
继承顺序从左到右,找到构造函数就停下来,注意方法只是定义在内存中
深度优先:D->B->A, D->C->A
python 3: 都是统一广度优先
python 2: 经典类是按深度优先继承,新式类是按广度优先继承的
深度没有广度效率高
多继承示例:
1classPeople(object):# 新式类
2def__init__(self,name,age):
3self.name = name
4self.age = age
5self.friends = []
6defeat(self):
7print("%s is eating..."%self.name)
8deftalk(self):
9print("%s is talking..."%self.name)
10defsleep(self):
11print("%s is sleeping..."%self.name)
12
13classRelation(object):
14# def __init__(self,n1,n2):
15# print("init in relation")
16defmake_friends(self,obj):#w1
17print("%s is making friends with %s"% (self.name,obj.name))
18self.friends.append(obj.name)
19classMan(Relation,People):
20def__init__(self,name,age,money=10):
21# People.__init__(self,name,age)
22super(Man,self).__init__(name,age)# 新式类写法
23self.money = money
24print("%s 一出生就有%s $"%(self.name,self.money))
25defsleep(self):
26People.sleep(self)
27print("man is sleeping ")
28classWoman(People,Relation):
29defget_birth(self):
30print("%s is born a baby...."%self.name)
31
32m1 = Man("XiaoMing",22)
33w1 = Woman("XiaoHong",20)
34
35m1.make_friends(w1)
组合
另外一种继承方式,严格意义上说不叫继承
1classTeacher(SchoolMember):
2def__init__(self,name,age,sex,sourse, school_obj):
3super(Teacher,self).__init__(name,age,sex)
4# SchoolMember.__init__(self,name,age,sex)
5self.school = school_obj# 组合方式,“继承”
6self.course = course
3.3 多态
一种接口,多种形态。比如打印每类动物的叫声,每次都调用不同的类名,不仅麻烦,而且要记住不同的类名,因此:
1classAnimal:
2def__init__(self, name):# Constructor of the class
3self.name = name
4
5deftalk(self):# Abstract method, defined by convention only
6pass#raise NotImplementedError("Subclass must implement abstract method")
7
8@staticmethod
9defanimal_talk(obj):
10obj.talk()
11
12classCat(Animal):
13deftalk(self):
14print('%s Meow!'%self.name)
15
16
17classDog(Animal):
18deftalk(self):
19print('%s Woof! Woof!'%self.name)
20
21
22d = Dog("GreyHound")
23#d.talk()
24
25c = Cat("Ocelot")
26#c.talk()
27
28# def animal_talk(obj):
29# obj.talk()
30
31Animal.animal_talk(c)
32Animal.animal_talk(d)
声明:本文系网络转载,版权归原作者所有。如涉及版权,请联系删除!
领取专属 10元无门槛券
私享最新 技术干货