面向对象编程

面向对象编程

一、对象的概念

面向对象是一门编程思想

​ 核心是“对象”二字

对象指的是“特征与技能”的结合体

  • 优点
    • 可扩展性高
  • 缺点
    • 编写程序的复杂程度比面向过程高

所有的程序都是由“数据”与“功能”组成,因而编写程序的本质就是定义出一系类的数据,然后定义出一系列的功能来对数据进行操作。在学习“对象”之前,程序中的数据与功能是分离开的

# 数据name、age、sex
name = 'tank'
age = 18
sex = 'female'

# 功能:tell_info
def tell_info(name, age, sex):
    print(f'<{name}:{age}:{sex}>')

# 此时若想执行查看个人信息的功能,需要同时拿来两样东西,一类是功能tell_info,另外一类则是多个数据name、age、sex,才能执行,非常麻烦
tell_info(name, age, sex)

二、 类与对象

类就是类别/种类,是面向对象分析和设计的基石

如果说对象是用来存放数据与功能的容器,那么类则是用来存放多个对象相同的数据与功能的容器

从两种角度去看待

  • 现实世界中
    • 现有一个个的对象,经过社会的文明发展,随之总结出类(人类)
  • 在程序中
    • 必须要先有类,再通过“调用类,产生对象”

强调:在程序中,必须要事先定义类,然后再调用类产生对象(调用类拿到的返回值就是对象)

产生对象的类与对象之间存在关联,这种关联指的是:对象可以访问到类中共有的数据与功能,所以类中的内容仍然是属于对象的,类只不过是一种节省空间、减少代码冗余的机制,面向对象编程最终的核心仍然是取使用对象

  • 如何定义类(如何写类并产生对象)
    • 先从现实世界中通过一个个对象总结出类
    • 然后再定义类,后调用类产生对象

三、面向对象编程

3.1 类的定义与实例化

我们以开发一个清华大学的选课系统为例,来介绍基于面向对象的思想如何编写程序

面向对象的基本思路就是把程序中要用到的、相关联的数据与功能整合到对象里,然后再去使用,但程序中要用到的数据以及功能那么多,我们需要先提取选课系统里的角色:学生、老师、课程等,然后显而易见的是:学生有学生先关的数据与功能,老师有老师相关的数据与功能

# 学生的数据有
学校
名字
年龄
性别
# 学生的功能有
选课

详细的

# 学生1
	数据:
    	学校 = 清华大学
        姓名 = 苟汪汪
        性别 = 女
        年龄 = 28
    功能:
    	选课
# 学生2
	数据:
    	学校 = 清华大学
        姓名 = 毛喵喵
        性别 = 女
        年龄 = 25
    功能:
    	选课
# 学生3
	数据:
    	学校 = 清华大学
        姓名 = 牛莽莽
        性别 = 男
        年龄 = 30
	功能:
    	选课

我们可以总结出一个学生类,用来存放学生们相同的数据与功能

# 学生类
	相同的特征:
    	学校 = 清华大学
    相同的功能:
    	选课

基于上述的分析,我们接下来要做的就是在程序中定义出类,然后调用类产生对象

class Student:  # 类的命名应该使用“驼峰体”
    school = '清华大学'  # 数据

    def choose(self):  # 功能
        print('%s is choosing a course' % self.name)

类体最常见的就是变量的定义和函数的定义,但其实类体可以包含任意Python代码,类体的代码在类定义阶段就会执行,因为会产生新的名称空间来存放类中定义的名字,可以打印Student.__dict__来查看类这个容器内盛放的东西

print(Student.__dict__)
{'__module__': '__main__', 'school': '清华大学', 'choose': <function Student.choose at 0x0000017AE8879A60>, '__dict__': <attribute '__dict__' of 'Student' objects>, '__weakref__': <attribute '__weakref__' of 'Student' objects>, '__doc__': None}

调用类的过程称为将类实例化,拿到的返回值就是程序中的对象,或称为一个实例

stu1 = Student()	#每实例化一次就得到一个学生对象
print(stu1)
stu2 = Student()
print(stu2)
stu3 = Student()
print(stu3)

<__main__.Student object at 0x0000026C62037BA8>
<__main__.Student object at 0x0000026C6203F198>
<__main__.Student object at 0x0000026C6203F358>

如此stu1、 stu2、 stu3全都一样了(只有类中有的内容,而没有各自独有的数据),想在实例化的过程中就位三位学生定制各自独有的数据:姓名,性别,年龄,需要我们在类内部新增一个__init__方法

class Student:
    school = '清华大学'

    # 该方法会在对象产生之后自动执行,专门为对象进行初始化操作,可以有任意代码,但一定不能返回非None的值
    def __init__(self, name, sex, age):
        self.name = name
        self.sex = sex
        self.age = age

    def choose(self):
        print('%s is choosing a course' % self.name)

然后我们重新实例出三位学生

stu1 = Student('苟汪汪', '女', 28)
stu2 = Student('毛喵喵', '女', 25)
stu3 = Student('牛莽莽', '男', 30)

单拿stu1的产生过程来分析,调用类会先产生一个空对象stu1,然后将stu1连同调用类时括号内参数一起传给Student.__init__(stu1,'苟汪汪','女',28)

def __init__(self, name, sex, age):
    self.name = name	# = '苟汪汪'
    self.sex = sex		# = '女'
    self.age = age		# = 28

会产生对象的名称空间,同样可以用__dict__查看

print(stu1.__dict__)
{'name': '苟汪汪', 'sex': '女', 'age': 28}
3.2 属性访问

3.2.1 类属性与对象属性

在类中定义的名字,都是类的属性,细说的话,类有两种属性:数据属性和函数属性,可以通过__dict__访问属性的值,比如Student.__dict__['school']

print(Student.school) # 访问数据属性,等同于Student.__dict__['school']
print(Student.choose) # 访问函数属性,等同于Student.__dict__['choose']

清华大学
<function Student.choose at 0x00000207292599D8>

操作对象的属性也是一样的

print(stu1.name) # 查看,等同于obj1.__dict__['name']
'苟汪汪'
stu1.course = 'python' # 新增,等同于obj1.__dict__['course'] = 'python'
stu1.age = 38 # 修改,等同于obj1.__dict__['age'] = 38
del stu1.course  # 删除,等同于del obj1.__dict__['course']

3.2.2 属性查找顺序与绑定方法

对象的名称空间里只存放着对象独有的属性,而对象们相似的属性是存放于类中的。对象在访问属性时,会优先从对象本身的__dict__中查看,未找到,则取类中的__dict__中查找

1、类中定义的变量是类的数据属性,是共享给所有对象用的,指向相同的内存地址

print(id(Student.school))	#1450119990160

print(id(stu1.school))	#1450119990160
print(id(stu2.school))	#1450119990160
print(id(stu3.school))	#1450119990160
# id 都一样

2、 类中定义的函数时类的函数属性,类可以使用,但必须遵循函数的参数规则,有几个参数需要传几个参数

print(Student.choose(stu1)) #苟汪汪 is choosing a course
print(Student.choose(stu2))	#毛喵喵 is choosing a course
print(Student.choose(stu3))	#牛莽莽 is choosing a course

但其实类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同

print(id(Student.choose)) # 1748242242008

print(id(stu1.choose))	# 1748211873224
print(id(stu2.choose))	# 1748211873224
print(id(stu3.choose))	# 1748211873224

绑定到对象的方法特殊之处在于,绑定给谁就应该由谁来调用,谁来调用,就会将谁本身当做第一个参数自动传入(方法__init__也是一样的道理)

stu1.choose() # 等同于Student.choose(stu1)
stu2.choose() # 等同于Student.choose(stu2)
stu3.choose() # 等同于Student.choose(stu3)

绑定到不同对象的choose技能,虽然都是选课,但苟汪汪选的课,不会选给毛喵喵,这正是“绑定”的精髓

print(list)
<class 'list'>

# 三个对象都有绑定方法append,是相同的功能,但内存地址不同
print(id(l1.append))
print(id(l2.append))
print(id(l3.append))

# 操作绑定方法l1.append(4),就是往l1添加4,绝对不会将4添加到l2或l3
l1.append(4)
print(l1)	# [1, 2, 3, 4]
print(l2)	# ['a', 'b', 'c']
print(l3)	# ['x', 'y']
posted @ 2019-11-29 19:09  YGZICO  阅读(109)  评论(0编辑  收藏  举报