今天我们要介绍的是新的知识点.即我们的面向对象编程
介绍
面向对象编程
核心是对象二字,对象就一个用来盛放数据与功能的容器
基于该思想编写程序就是创造一个个的容器
优点: 扩展性强
缺点: 编程的复杂度提升
对象和类的推演过程
来个需求:我要打印出一个学生的所有信息
这个时候我们有俩种方案可以满足函数代码体可以执行
对象
方案1:将里面的姓名,年龄,性别,都传一个形参进去,即
def tell_info(name, age, gender):
print('my name is %s my age is %s my gender is %s' % (name, age, gender))
这种形参就好比一个个的数据,一个姓名,年龄,性别对应的数据,而这种数据我们是一个一个的传进去的,即没有封装
这种情况下,我们要去调用这个函数,就得给它传了三个值
tell_info('jkey',18,'male')
那么方案2:就是将这些数据,先封装再传参,即,我将数据都封装到一个字典里面,传给这个函数一个字典即可.
def tell_info1(student):
print('my name is %s my age is %s my gender is %s' % (student["name"], student["age"], student["gender"]))
这个情况下我们可以这样调用
student = {'name':'jkey', "age":18,"gender":'male'}
tell_info1(student)
有了这个封装的概念,我们就可以来介绍我们的对象了,我们都知道.一个对象本质就是一些数据和功能的容器,那我们看看下面这个字典
student1 = {
"school": "上海校区",
"name": "jkey",
"age": 18,
"gender": "male",
"tell_info": tell_info1
}
这个时候你可以发现,这个字典的key对应的value不仅有直接的数据,还有函数,函数即是我们的功能.所以这个时候,我们就可以大胆的去猜测这个字典就是一个对象
答案是这个字典就是一个对象,即student1是一个对象
那么我们要想执行student1这个对象下的功能
字典按照key取value,我们就可以去执行下面的功能,即
student1["tell_info"](student1) # student1 我们将这个字典本身传给了那个功能,那么它返回的学生信息就是你这个字典的数据信息
同样我来一个新的字典对象
student2 = {
"school": "上海校区",
"name": "tom",
"age": 19,
"gender": "female",
"tell_info": tell_info1
}
# 这里就是那着student2这个对象的数据传给了tell_info1这个功能,返回的也是student2的学生信息
student2["tell_info"](student2)
所以我们可以总结一下:
1.一个对象就是一个由数据和功能组合的容器
2.以前我们学的字典就是一个对象
但是我们可以发现,我们建这个字典对象里面的数据,有一些数据和功能是一样的(例如:school,tell_info),我是不是可以再给他放到一个新的容器里面??
是的可以,但是这个容器我们python给我们提供了一个更加鲜明的名字,这个就是我们的类.这个类就是放一些我们相似对象里面的相同的数据或者功能的一个容器
类
既然我们知道了,类的作用是什么.那我们python中怎么去使用这个类呢??
下面开始介绍类的使用
定义类发生的事
1、立刻运行类体代码
2、将运行过程中产生的名字都丢到类的名称空间中
ps:这一点区别与函数,函数在定义时不会执行里面的代码,反而有点像模块,将产生的名字都丢到一个名称空间中
实例:将之前的字典相同数据和功能放到我们的类中
class Student:
school = "上海校区" # 相同的数据
def tell_info(student):
"""相同的方法"""
print('my name is %s my age is %s my gender is %s' % (student["name"], student["age"], student["gender"]))
print('======>') # 为了验证第一点,定义时立即执行类体代码
那我们定义的类,类里面的数据都放到哪去了呢???
其实类的本质就是一个字典 只是这个字典被python给封装了一下,我们要想看这个类下面有的数据和方法,可以通过__dict__来访问
print(Student.__dict__) # 打印一下看看
"""
返回的结果:
{'__module__': '__main__', 'school': '上海校区', 'tell_info': <function Student.tell_info at 0x000002AFB28375E8>,
'__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>,
'__doc__': None}
其他的我们先别管,我们看看里面是不是有我们的数据和功能:
'上海校区', 'tell_info': <function Student.tell_info at 0x000002AFB28375E8>
我们可以找到这俩个数据和功能
"""
调用类发生的事情
1、创造一个对象的字典,用来存放对象独有的数据
2、将对象与类建立好关联
语法: 类名加()
返回的是一个对象 并且是一个空字典
obj1 = Student() # 创建对象 并绑定相关的类
print(obj1) # <__main__.Student object at 0x000001DE64FFB148>
print(obj1.__dict__) # {}
即是一个字典,我们就可以通过字典的方式对其进行赋值
obj1.__dict__["name"] = "jkey"
obj1.__dict__["age"] = 18
obj1.__dict__["gender"] = "male"
print(obj1.__dict__) # {'name': 'jkey', 'age': 18, 'gender': 'male'}
但是这个通过字典的方法去定义是不是 使代码不够精简,既然要封装,那么就要封装的简单一点,所以我们python就提供了.来给这个对象字典赋值,即
obj1.name = "jkey" # obj1.__dict__["name"] = "jkey"
obj1.age = 18 # obj1.__dict__["age"] = 18
obj1.gender = "male" # obj1.__dict__["gender"] = "male"
print(obj1.__dict__) # {'name': 'jkey', 'age': 18, 'gender': 'male'}
俩种方法的效果是一样的,推荐使用第二种
注意,每次调用该类.都会产生一个全新的对象,即
obj2 = Student()
print(obj2) # <__main__.Student object at 0x000001C0DF040B88>
print(obj2.__dict__) # {}
obj2.name = "tom" # obj2.__dict__["name"] = "tom"
obj2.age = 19 # obj2.__dict__["age"] = 19
obj2.gender = "female" # obj2.__dict__["gender"] = "female"
print(obj2.__dict__) # {'name': 'tom', 'age': 19, 'gender': 'female'}
到这.我们再进行依次总结:
1.我们可以使用python内置的class关键字来定义一个类
1.1定义一个类会发生俩个事,一是执行类内的代码
1.2将产生的名字都丢到该类的名称空间中
2.在调用该类时
2.1 先创建一个空字典,用来存放对象自己独有的数据
2.2 将类和对象之间建立好关联
3:这里引发的一个问题! 对象在绑定自己独有的数据时,来一个对象,就要赋值一下,就产生了代码的冗余
所以就有了我们的第二种版本:将对象赋值的过程写进一个函数内,将他们独有的数据和他们对象当作形参,内部就可以进行赋值
def init(self,x,y,z):
self.name = x
self.age = y
self.gender = z
init(obj1,"jkey",18,"male")
init(obj2,"tom",19,"female")
print(obj1.__dict__) # {'name': 'jkey', 'age': 18, 'gender': 'male'}
print(obj2.__dict__) # {'name': 'tom', 'age': 19, 'gender': 'female'}
这时候,就可以实现,来一个对象,我就可以之间进行独有的数据赋值了.
但是还是不够好,你将这个函数定义到了类的外面?那我可以直接定义到类的内部吗?答案是可以的
所以python呢,又给你想到了,并且给你封装到了类的内部即这个时候就产生了第三版本:
类内可以定义一个名为__init_的功能,做的就是你刚刚的自己定义的函数的功能,注意这个函数名必须为_init
所以你现在的代码就变成了
class Student:
school = "上海校区"
# 空对象, "egon", 18, "male"
def __init__(self, x, y, z):
self.name = x # 空对象.name = "egon"
self.age = y # 空对象.age = 18
self.gender = z # 空对象.gender = "male"
def tell_info(student):
print('my name is %s my age is %s my gender is %s' % (student["name"], student["age"], student["gender"]))
那么既然定义过程都变了,那调用过程变成什么样了呢?
即下方的案例:调用类的过程
1、先创造一个空对象
2、自动触发类内的__init__函数的运行,将空对象当做第一个参数自动传入
3、返回一个初始化好的对象给obj1
语法: 类名() 括号内为__init__函数的参数
obj1 = Student("egon",18,"male") # Student.__init__(空对象,"egon",18,"male")
obj2 = Student("tom",19,"female")
print(obj1.__dict__) # {'name': 'egon', 'age': 18, 'gender': 'male'}
print(obj2.__dict__) # {'name': 'tom', 'age': 19, 'gender': 'female'}
print(Student.__dict__)
这时候一个简单的具有初始化功能的类和对象的实例化就已经实现了
来一个小总结:
1.类内有一个__init__的方法,该方法是为了实现对象给自己添加独有的数据的一个简单方法
1.1在类调用的时候会先创建一个空对象,将空对象本身当作第一个参数传给__init__方法
1.2 返回的是一个初始化好的对象 即 那个带有自己数据的字典
属性查找
接下来就是将类的一些属性查找,接着拿我们刚刚改好的案例,给他内部改一下
class Student:
school = 'shanghai'
# 空对象 'jkey' 18 'male'
def __init__(self, x, y, z):
self.name = x # 空对象.name = 'jkey'
# print(1, self.__dict__)
self.age = y # 对象.age = 18
# print(2, self.__dict__)
self.gender = z # 对象.gender = 'male'
def tell_info(self):
print(f'my name is {self.name} my age is {self.age} my gender is {self.gender}')
def func(self):
print('xxx')
先创建俩个对象 jkey liu
# 调用类的过程
# 1,先创建一个空对象(是一个空字典)
# 2.自动触发类内的__init__函数的运行,将空对象当作第一个参数自动传入
# 3.返回一个初始化好的对象给jkey
jkey = Student('jkey', 18, 'male')
liu = Student('liu', 19, 'female')
# print(jkey.__dict__) # {'name': 'jkey', 'age': 18, 'gender': 'male'}
# print(liu.__dict__) # {'name': 'liu', 'age': 19, 'gender': 'female'}
# print(Student.__dict__) # {'__module__': '__main__', 'school': 'shanghai', '__init__': <function Student.__init__ at 0x000001CD35AF75E8>, 'tell_info': <function Student.tell_info at 0x000001CD35AF79D8>...]
对象.属性的查找顺序:先从对象的字典里面找,再从类的字典里找
print(jkey.name) # jkey
print(jkey.school) # shanghai
print(jkey.tell_info) # <bound method Student.tell_info of <__main__.Student object at 0x000001BCF75AEC48>>
jkey.school = 'xxx' # 进行的是给对象的字典里面重新添加了一个数据
print(jkey.school) # xxx
类.属性:从类自己的字典里面找
print(Student.school) # shanghai
print(Student.__init__) # <function Student.__init__ at 0x000001BCF75A75E8>
print(Student.tell_info) # <function Student.tell_info at 0x000001BCF75A79D8>
类中的数据属性是直接共享给所有对象使用的
Student.school = 'xxxxx' # 改变了类属性的值,对象引用时也会发送改变
jkey.school = 'yyyyy' # 改变了对象属性的值,只会影响对象自己
print(Student.school, id(Student.school)) # xxxxx 1911115409328
print(jkey.school, id(Student.school)) # yyyyy 1911115409328
print(liu.school, id(Student.school)) # xxxxx 1911115409328
类中的函数类可以可用,如果类来调用就是一个普通函数,该怎么传参就怎么传
但其实类中的函数是给对象用,对象来调用就是一个绑定方法,绑定方法的特点是会将调用当做第一个参数自动传入
print(Student.tell_info) # <function Student.tell_info at 0x000001E374EC7F78>
Student.tell_info(jkey) # my name is jkey my age is 18 my gender is male
Student.tell_info(liu) # my name is liu my age is 19 my gender is female
print(jkey.tell_info) # <bound method Student.tell_info of <__main__.Student object at 0x000001E374C6F588>>
jkey.tell_info() # my name is jkey my age is 18 my gender is male
liu.tell_info() # my name is liu my age is 19 my gender is female
# Student.func() # 会报错,通过类去使用里面的函数,该怎么传参就怎么传参 所以你要传一个参数
Student.func(jkey) # xxx
jkey.func() # 而这你是通过jkey这个对象去使用的,这个函数方法现在变成了绑定方法,会将对象自己当作第一个参数传到函数中