1、类与对象的概念
类即类别、种类,是面向对象设计最重要的概念,从一小节我们得知对象是特征与技能的结合体,而类则是一系列对象相似的特征与技能的结合体。
那么问题来了,先有的一个个具体存在的对象(比如一个具体存在的人),还是先有的人类这个概念,这个问题需要分两种情况去看
(1)在现实世界中:肯定是先有对象,再有类世界上肯定是先出现各种各样的实际存在的物体,然后随着人类文明的发展,人类站在不同的角度总结出了不同的种类,比如
人类、动物类、植物类等概念。也就说,对象是具体的存在,而类仅仅只是一个概念,并不真实存在,比如你无法告诉我人类
具体指的是哪一个人。
(2)在程序中:务必保证先定义类,后产生对象
这与函数的使用是类似的:先定义函数,后调用函数,类也是一样的:在程序中需要先定义类,后调用类。不一样的是:调用
函数会执行函数体代码返回的是函数体执行的结果,而调用类会产生对象,返回的是对象
1>把一类事物的静态属性和动态可以执行的操作组合在一起所得到的这个概念就是类
2>类的一个个体就是对象,对象是具体的,实实在在的事物
3>对象是特征与技能的结合体,其中特征和技能分别对应对象的数据属性和方法属性
4>对象(实例)本身只有数据属性,但是python的class机制会将类的函数绑定到对象上,称为对象的方法,或者叫绑定方法,绑定方法唯一绑定一个对象,同一个类的方法绑定到不同的对象上,属于不同的方法,内存地址都不会一样
在类内部定义的属性属于类本身的,由操作系统只分配一块内存空间,大家公用这一块内存空间
5>创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性:而类中有两种属性:数据属性和函数属性,其中类的数据属性是共享给所有对象的,而类的函数属性是绑定到所有对象的。
6>创建一个对象(实例)就会创建一个对象(实例)的名称空间,存放对象(实例)的名字,称为对象(实例)的属性
7>在obj.name会先从obj自己的名称空间里找name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常。
8>类的相关方法:
类的相关方法(定义一个类,也会产生自己的名称空间)
类名.__name__ # 类的名字(字符串)
类名.__doc__ # 类的文档字符串
类名.__base__ # 类的第一个父类(在讲继承时会讲)
类名.__bases__ # 类所有父类构成的元组(在讲继承时会讲)
类名.__dict__ # 类的字典属性、名称空间
类名.__module__ # 类定义所在的模块
类名.__class__ # 实例对应的类(仅新式类中)
1.创建出类会产生名称空间,实例化对象也会产生名称空间。
2.用户自己定义的一个类,实际上就是定义了一个类型,类型与类是统一的。
3.用户先是从自己的命名空间找,如果找不大,在从类的命名空间找。
student1.langage = "1111"
print(student1.__dict__) ===>先是从自己的命名空间找
print(Student.__dict__) ===>然后在从类的命名空间找
4.通过类来访问,访问的是函数,通过对象来访问,访问的是方法,在类内部定义的方式实际上是绑定到对象的身上来用的。
<function Student.fun at 0x000000000267DAE8>
<bound method Student.fun of <__main__.Student object at 0x0000000002684128>>
<function Student.fun at 0x00000000025CDAE8>
<bound method Student.fun of <__main__.Student object at 0x00000000025D4160>>
<bound method Student.fun of <__main__.Student object at 0x00000000025D4198>>
5.总结:类的数据属性是大家共有的,而且大家的内部地址是一样的,用的就是一个
类的函数属性是绑定到大家身上的,内部地址不一样,绑定方法指的是绑定到对象身上。
绑定方法:绑定到谁的身上,就是给谁用的,谁来调用就会自动把自己当做第一个参数传入。
**定义在类内部的变量,是所有对象共有的,id全一样,
**定义在类内部的函数,是绑定到所有对象的,是给对象来用的,obj.fun()会把obj本身当做
一个参数来传递。
6.在类内部定义的函数虽然可以由类来调用,但是并不是为了给类用的,在类内部定义的函数的目的就是为了绑定到对象身上的。
7.在类的内部来说,__init__是类的函数属性,但是对于对象来说,就是绑定方法。
8.命名空间的问题:先从对象的命名空间找,随后在从类的命名空间找,随后在从父类的命名
空间找。
print(student1.x)
9.在定义类的时候,可以想什么先写什么。
2、定义类
按照上述步骤,我们来定义一个类
(1)在现实世界中,先有对象,再有类
对象1:李坦克 特征: 学校=oldboy 姓名=李坦克 性别=男 年龄=18 技能: 学习 吃饭 睡觉 对象2:王大炮 特征: 学校=oldboy 姓名=王大炮 性别=女 年龄=38 技能: 学习 吃饭 睡觉 对象3:牛榴弹 特征: 学校=oldboy 姓名=牛榴弹 性别=男 年龄=78 技能: 学习 吃饭 睡觉
(2)在程序中,必须先定义类,后使用类(用来产生对象)
#在Python中程序中的类用class关键字定义,而在程序中特征用变量标识,技能用函数标识,因而类中最常见的无非是:变量和函数的定义
# 先定义类
class OldboyStudent: school='oldboy' def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping')
# 后产生对象
stu1 = OldboyStudent()
stu2 = OldboyStudent()
stu3 = OldboyStudent()
print(stu1)
print(stu2)
print(stu3)
- 类中可以有任意python代码,这些代码在类定义阶段便会执行,因而会产生新的名称空间,用来存放类的变量名与函数名,可以通过OldboyStudent.__dict__查看
- 类中定义的名字,都是类的属性,点是访问属性的语法。
- 对于经典类来说我们可以通过该字典操作类名称空间的名字,但新式类有限制(新式类与经典类的区别我们将在后续章节介绍
3、类的使用
3.1、引用类的属性
class OldboyStudent:
school='oldboy'
def learn(self):
print('is learning')
def eat(self):
print('is eating')
def sleep(self):
print('is sleeping')
# 后产生对象
stu1 = OldboyStudent()
stu2 = OldboyStudent()
stu3 = OldboyStudent()
print(stu1)
print(stu2)
print(stu3)
print(OldboyStudent.school) #查 输出结果:oldboy
OldboyStudent.school='Oldboy2' #改
print(OldboyStudent.school)# 输出结果:Oldboy2
OldboyStudent.x=1 #增
print(OldboyStudent.x) #输出结果 1
del OldboyStudent.x #删
print(OldboyStudent.x)
输出结果:
AttributeError: type object 'OldboyStudent' has no attribute 'x'
3.2、调用类,或称为实例化,得到程序中的对象
stu1 = OldboyStudent()
stu2 = OldboyStudent()
stu3 = OldboyStudent()
#如此,s1、s2、s3都一样了,而这三者除了相似的属性之外还各种不同的属性,这就用到了__init__
__init__方法 # 用来为对象定制自己独特的属性 #注意:该方法是在对象产生之后才会执行,只用来为对象进行初始化操作,可以有任意代码,但一定不能有返回值
class OldboyStudent: ...... def __init__(self,name,age,sex): self.name=name self.sex=sex self.age=age ...... s1=OldboyStudent('李坦克','男',18) #先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,'李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴弹','男',78)
4、对象的使用
class OldboyStudent: school = 'oldboy' def __init__(self,name,sex,age): self.name=name self.sex=sex self.age = age def learn(self): print('is learning') def eat(self): print('is eating') def sleep(self): print('is sleeping') s1=OldboyStudent('李坦克','男',18) #先调用类产生空对象s1,然后调用OldboyStudent.__init__(s1,'李坦克','男',18) s2=OldboyStudent('王大炮','女',38) s3=OldboyStudent('牛榴弹','男',78) print(s1.__dict__) print(s2.__dict__) print(s3.__dict__) 结果: {'name': '李坦克', 'sex': '男', 'age': 18} {'name': '王大炮', 'sex': '女', 'age': 38} {'name': '牛榴弹', 'sex': '男', 'age': 78}
#执行__init__,s1.name='牛榴弹',很明显也会产生对象的名称空间可以用s2.__dict__查看,查看结果为 {'name': '王大炮', 'age': '女', 'sex': 38} s2.name #查,等同于s2.__dict__['name'] s2.name='王三炮' #改,等同于s2.__dict__['name']='王三炮' s2.course='python' #增,等同于s2.__dict__['course']='python' del s2.course #删,等同于s2.__dict__.pop('course')
5、补充说明
- 站的角度不同,定义出的类是截然不同的;
- 现实中的类并不完全等于程序中的类,比如现实中的公司类,在程序中有时需要拆分成部门类,业务类等;
- 有时为了编程需求,程序中也可能会定义现实中不存在的类,比如策略类,现实中并不存在,但是在程序中却是一个很常见的类。
悟已往之不谏,知来者之可追。