1.对象的概念
面向对象编程是一种编程思想 , 一种编程范式 , 不是只有python才有 , 所有的编程语言都可以用面向对象的思想去写代码 , 那么面向对象到底是什么???
面向对象
的核心是对象
二字,而对象的精髓在于整合
,什么意思?
所有的程序都是由 "数据" 与 "功能" 组成,因而编写程序的本质就是定义出一系列的数据,然后定义出一系列的功能来对数据进行操作。在学习 "对象"之前,程序中的数据与功能是分离开的,如下
# 数据:name、age、sex
name='lili'
age=18
sex='female'
# 功能:tell_info
def tell_info(name,age,sex):
print('<%s:%s:%s>' %(name,age,sex))
# 此时若想执行查看个人信息的功能,需要同时拿来两样东西,一类是功能tell_info,另外一类则是多个数据name、age、sex,然后才能执行,非常麻烦
tell_info(name,age,sex)
在学习了“对象”之后,我们就有了一个容器,该容器可以盛放数据与功能,所以我们可以说:对象是把数据与功能整合到一起的产物,或者说”对象“就是一个盛放数据与功能的容器
面向对象就是把专门的数据和专门的功能整合在一起, 整个文件解耦合, 分割成一个一个小容器(对象)
2.类与对象
从面向对象的字面上看是对象为核心 , 但是实际上编写代码的时候 , 是先有的类 , 再有的对象
那什么是类? 类和对象是什么关系?
类即类别/种类,是面向对象分析和设计的基石,如果多个对象有相似的数据与功能,那么该多个对象就属于同一种类。有了类的好处是:我们可以把同一类对象相同的数据与功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需存自己独有的数据即可,极大地节省了空间。所以,如果说对象是用来存放数据与功能的容器,那么类则是用来存放多个对象相同的数据与功能的容器。
综上所述,虽然我们是先介绍对象后介绍类,但是需要强调的是:在程序中,必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)。产生对象的类与对象之间存在关联,这种关联指的是:对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是去使用对象。
3.面向对象编程
3.1类的定义与实例化
采用一个学生选课需求来讲解面向对象编程 , 要求每个学生有自己的基本信息 , 名字 , 年龄 , 性别 , 学校 , 一个选课功能 。当你拿到这个需求的时候 ,不是直接class就开始写了 , 我们简单分析下 ,名字 , 年龄 , 性别 , 学校是属于数据 , 选课属于功能 , 由于学校都是一个 , 那么学校就是一个非独有的数据值 ,选课是每一个学生都有的功能, 那么我们就可以用类来把相同的数据和功能存在一起
class Student: # 类的命名应该使用“驼峰体”
stu_school='北京大学' # 数据
def choose(self): # 功能
print('%s is choosing a course' %self.name)
类体最常见的是变量的定义和函数的定义,但其实类体可以包含任意Python代码,类体的代码在类定义阶段就会执行
,因而会产生新的名称空间用来存放类中定义的名字,可以打印Student.__dict__
来查看类这个容器内盛放的东西
调用类的过程称为将类实例化,拿到的返回值就是程序中的对象,或称为一个实例
stu1_obj=Student() # 每实例化一次Student类就得到一个学生对象
stu2_obj=Student()
stu3_obj=Student()
如此stu1、stu2、stu3全都一样了(只有类中共有的内容,而没有各自独有的数据),我们以前的知识可以这样做
# 为对象定制自己独有的属性
stu1_obj.stu_name = 'egon' # stu1_obj.__dict__['stu_name']='egon'
stu1_obj.stu_age = 18 # stu1_obj.__dict__['stu_age']=18
stu1_obj.stu_gender = 'male' # stu1_obj.__dict__["stu_gender"]= 'male'
print(stu1_obj.__dict__)
# 再实例化2个对象
stu2_obj.stu_name = 'alex' # stu1_obj.__dict__['stu_name']='egon'
stu2_obj.stu_age = 38 # stu1_obj.__dict__['stu_age']=18
stu2_obj.stu_gender = 'male' # stu1_obj.__dict__["stu_gender"]= 'male'
print(stu2_obj.__dict__)
stu3_obj.stu_name = 'wusir' # stu1_obj.__dict__['stu_name']='egon'
stu3_obj.stu_age = 28 # stu1_obj.__dict__['stu_age']=18
stu3_obj.stu_gender = 'female' # stu1_obj.__dict__["stu_gender"]= 'male'
print(stu3_obj.__dict__)
但是代码比较冗余 , 可以使用函数减少代码的冗余
#定义一个函数
def init(obj, x, y, z):
obj.stu_name = x
obj.stu_age = y
obj.stu_gender = z
return obj
stu1_obj = Student()
stu1_obj = init(stu1_obj,'egon',18,"man")
print(stu1_obj.__dict__)
stu2_obj = Student()
stu2_obj = init(stu2_obj,'alex',20,"man")
print(stu2_obj.__dict__)
stu3_obj = Student()
stu3_obj = init(stu3_obj,'wusir',18,"feman")
print(stu3_obj.__dict__)
#但是出现了一个问题,就是这个函数,在类外面,但是呢又是所有对象都要调用的功能,即相同的功能
#能不能在类中写上这样一个方法呢?哎python给你提供了中便捷的语法
class Student:
# 变量的定义
stu_school = "old_boy"
def __init__(obj, x, y, z):
obj.stu_name = x
obj.stu_age = y
obj.stu_gender = z
# 二:再调用类产生对象
# 调用类的过程又称之为实例化(就是实际的一个案例),发生了三件事
# 1、先产生一个空对象
# 2、python会自动调用类中的__init__方法然后将空对象已经调用类时括号内传入的参数。
# 3、返回初始完的对象
stu1_obj = Student('egon', 18, "man")
stu2_obj = Student('alex', 38, "man")
stu3_obj = Student('wusir', 48, "feman")
# 总结__init__方法
# 1、会在调用类时自动触发执行,用来为对象初始化自己独有的数据
# 2、__init__内应该存放是为对象初始化属性的功能,但是是可以存放任意其他代码,想要在#类调用时就立刻执行的代码都可以放到该方法内
# 3、__init__方法必须返回None
# 3、__init__方法是定制对象独有的数据属性的功能, 对象属性最好在__init__中定义,如果实例化不赋值数据,可以定义为空
至此,我们造出了三个对象与一个类,对象存放各自独有的数据,类中存放对象们共有的内容
存的目的是为了用,那么如何访问对象或者类中存放的内容呢
3.2属性的访问
在类中定义的名字,都是类的属性,细说的话,类有两种属性:数据属性和函数属性,可以通过__dict__访问属性的值,比如Student.__dict__['stu_school]
,但Python提供了专门的属性访问语法
操作类属性
# 1、访问数据属性
print(Student.stu_school) # Student.__dict__['stu_school']
# 2、访问函数属性
print(Student.choose) # Student.___dict__[ 'choose ']
# 3.增加属性
Student.x = 1111 # Student.__dict__['x']=1111
print(Student.__dict__)
操作对象属性
print(stu1_obj.name)
stu2_obj.choose
stu3_obj.age=12
对象的名称空间里只存放着对象独有的属性,而对象们相同的属性是存放于类中的。对象在访问属性时,会优先从对象本身的__dict__
中查找,未找到,则去类的__dict__
中查找
1、类中定义的变量是类的数据属性,是共享给所有对象用的,指向相同的内存地址
# id都一样print(id(Student.school)) # 4301108704print(id(stu1_obj.school)) # 4301108704print(id(stu2_obj.school)) # 4301108704print(id(stu3_obj.school)) # 4301108704# 查找共有的数据属性指向的都是一个地址 , 但是赋值的话是不相互影响的stu2_obj.school = 111print(id(stu2_obj.school)) # 111print(id(stu3_obj.school)) # 北京大学
补充: 如果想要全对象生效的话 , 把这个数据定义到类中共有的里面 , 比如实例化一个对象 , count自增1 , 每个对象都能获取到实例化了多少对象
class Student: # 变量的定义 stu_school = "old_boy" count = 0 def __init__(obj, x, y, z): Student.count += 1 obj.stu_name = x obj.stu_age = y obj.stu_gender = zobj1 = ('alex',19,'man')obj2 = ('alex1',19,'man')obj3 = ('alex2',19,'man')print(obj1.count) # 3print(obj1.count) # 3
2、类中定义的函数是类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数
Student.choose(stu1_obj) # eggon is choosing a courseStudent.choose(stu2_obj) # alex is choosing a courseStudent.choose(stu3_obj) # wusir is choosing a course
但其实类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
print(id(Student.choose)) # 4335426280print(id(stu1_obj.choose)) # 4300433608print(id(stu2_obj.choose)) # 4300433608print(id(stu3_obj.choose)) # 4300433608
绑定到对象的方法特殊之处在于,绑定给谁就应该由谁来调用,谁来调用,就会将’谁’本身当做第一个参数自动传入(方法1__init__
也是一样的道理)
stu1_obj.choose() # 等同于Student.choose(stu1_obj)stu2_obj.choose() # 等同于Student.choose(stu2_obj)stu3_obj.choose() # 等同于Student.choose(stu3_obj)
绑定到不同对象的choose技能,虽然都是选课,但eggon选的课,不会选给alex,这正是”绑定“二字的精髓所在。
#注意:绑定到对象方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但命名为self是约定俗成的。
补充 :
isinstance('对象','类') #判断对象类型 , 返回布尔值 , 以后不要在用type做类型判断issubclass('子类','父类') # 判断子类属不属于父类 , 返回布尔值
4.小结
在上述介绍类与对象的使用过程中,我们更多的是站在底层原理的角度去介绍类与对象之间的关联关系,如果只是站在使用的角度,我们无需考虑语法“对象.属性"中”属性“到底源自于哪里,只需要知道是通过对象获取到的就可以了,所以说,对象是一个高度整合的产物,有了对象,我们只需要使用”对象.xxx“的语法就可以得到跟这个对象相关的所有数据与功能,十分方便且解耦合程度极高。