一. 对象的概念
1. 从"过程"过度到"对象"的概念介绍
# 前言: 了解面向过程
"""
面向过程:
核心是"过程"二字
目的: 过程的终极奥义就是将程序流程化
作用: 过程是"流水线",用来分步骤解决问题的
"""
# 面向对象
"""
面向对象:
核心是"对象"二字
目的: 对象的终极奥义就是将程序"整合"
作用: 对象是"容器",用来盛放数据与功能的
面向对象编程的好处:
提高程序的解耦合性, 进而提升程序的可扩展性(注意: 面向对象解决的仅仅是程序的可扩展性问题, 它并不是万能的.)
"""
2. 了解"对象"之前我们的数据和功能是分开的
# 在"了解到"对象之前, 程序的数据与功能是分离开的
# 数据: stu_name, stu_age, stu_sex
stu_name = 'egon'
stu_age = 18
stu_sex = 'male'
# 功能(函数): tell_info, set_info
# 此时若想执行查看个人信息的功能,需要同时拿来两样东西,一类是功能tell_info,另外一类则是多个数据name、age、sex,然后才能执行,非常麻烦
def tell_info():
print('名字:%s 性别:%s 年龄:%s' % (stu_name, stu_age, stu_sex))
def set_info():
stu_name = 'EGON'
stu_age = 20
stu_sex = "female"
3. 了解了"对象"以后我们把数据和功能进行整合
# 学生的容器 = 学生的数据 + 学生的功能
def tell_info():
print('名字:%s 性别:%s 年龄:%s' % (stu1_obj['stu_name'], stu1_obj["stu_age"], stu1_obj['stu_sex']))
def set_info(name, age, sex):
stu1_obj['stu_name'] = name
stu1_obj['stu_age'] = age
stu1_obj['stu_sex'] = sex
stu1_obj = {
'stu_name': 'EGON',
'stu_age': 20,
'stu_sex': 'female',
'tell_info': tell_info,
'set_info': set_info,
}
二. 类与对象
# 什么是类?
"""
类也是"容器",该容器用来存放同类对象共有的数据与功能
"""
# 对象归类的好处
"""
极大地节省了空间和减少代码冗余: 把同一类对象相同的数据与功能存放到类里,而无需每个对象都重复存一份,这样每个对象里只需存自己独有的数据即可.
"""
三. 面向对象编程
1. 类的定义与实例化 + 属性访问之类属性与对象属性
"""
# 提示: 类是同类对象们相似功能的集合体
# 类体代码的范围:
类体代码最常见的是变量与函数名的定义, 但是类体代码其实是可以包含任意其它代码的.
# 类体代码的生效时间(注意): 类体代码在定义阶段就会立即执行, 会产生类的名称空间. 我们可以是用 类名.__dict__ 访问到产生的类体代码的名称空间.
"""
# 一. 类的定义
class Student:
# 变量的定义
stu_school = 'oldboy'
# 函数功能的定义
def tell_stu_info(stu_obj):
print('学生信息:名字:%s 年龄:%s 性别:%s' % (
stu_obj['stu_name'],
stu_obj['stu_age'],
stu_obj['stu_gender']
))
def set_info(stu_obj, x, y, z):
stu_obj['stu_name'] = x
stu_obj['stu_age'] = y
stu_obj['stu_gender'] = z
print('========>') # 类体代码在定义阶段就会立即执行
# 1. 类名.__dict__ 访问到产生的类体代码的名称空间.
print(Student.__dict__)
# 2. 属性访问(属性访问的语法)
# 访问数据属性
print(Student.stu_school) # 等同于Student.__dict__['stu_school']
# 访问函数属性
print(Student.set_info) # 等同于Student.__dict__['set_info']
# 3. 属性修改与添加(Student类体代码中 没有如下x数据属性就添加 有就修改)
Student.x = 1111 # Student.__dict__['x']=111
print(Student.__dict__)
# 二. 调用类产生对象
stu1_obj = Student()
stu2_obj = Student()
stu3_obj = Student()
# 因为这三个对象没有定制自己独有的属性, 因此都是空对象, 它们的名称空间都是{}
print(stu1_obj.__dict__)
print(stu2_obj.__dict__)
print(stu3_obj.__dict__)
# 为对象定制自己独有的属性
# 问题1: 代码冗余
# 问题2: 属性的查找顺序
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__)
stu2_obj.stu_name = 'lili'
stu2_obj.stu_age = 19
stu2_obj.stu_gender = 'female'
print(stu2_obj.__dict__)
stu3_obj.stu_name = 'jack'
stu3_obj.stu_age = 20
stu3_obj.stu_gender = 'male'
print(stu2_obj.__dict__)
# 解决问题一: 既然是代码冗余, 我们可以定义函数, 解决这个问题
def init(stu_obj, name, age, sex):
stu_obj.stu_name = name
stu_obj.stu_age = age
stu_obj.stu_sex = sex
init(stu1_obj, 'egon', 18, 'male')
init(stu2_obj, 'lili', 19, 'female')
init(stu3_obj, 'jack', 20, 'male')
print(stu1_obj.__dict__) # 通过以上我们定义的init函数我们成功为3者对象创建了属于它们独有的属性
print(stu2_obj.__dict__)
print(stu3_obj.__dict__)
# 解决问题二:
class Student:
# 在类Student中使用__init__方法会在调用阶段就会执行__init__的功能并进行如下方式把参数传入.
# 空对象, 'egon', 18, 'male'
def __init__(stu_obj, name, age, sex):
stu_obj.stu_name = name # 空对象.__dict__['stu_name'] = 'egon'
stu_obj.stu_age = age # 空对象.__dict__['stu_age'] = 18
stu_obj.stu_sex = sex # 空对象.__dict__['stu_sex'] = 'male
def tell_stu_info(stu_obj):
print('学生信息:名字:%s 年龄:%s 性别:%s' % (
stu_obj['stu_name'],
stu_obj['stu_age'],
stu_obj['stu_gender']
))
def set_info(stu_obj, x, y, z):
stu_obj['stu_name'] = x
stu_obj['stu_age'] = y
stu_obj['stu_gender'] = z
# 三. 再调用类产生对象
"""
# 调用类的过程中又称之为实例化, 这里发生的3件事
1. 先产生一个空对象
2. python会自动调用__init__方法, 然后将空对象和调用类时时括号内的参数以一同当作实参传给__init__方法
3. 最后返回初始化完成的对象
"""
stu_obj = Student('egon', 18, 'male') # Student.__init__(空对象, 'egon', 18, 'male')
stu_obj1 = Student('lili', 19, 'female')
stu_obj2 = Student('jack', 20, 'male')
print(stu_obj.__dict__)
print(stu_obj1.__dict__)
print(stu_obj2.__dict__)
# 总结__init__方法
"""
1. 会在调用类时自动触发执行, 用来为对象初始化自己独有的数据
2. __init__内应该存放的是为对象初始化属性的功能, 但是是可以存放任意其他代码, 想要在类调用是就立刻执行的代码都可以放到该方法内
3. __init__方法的返回值必须返回值None(提示: 无需指定, 默认返回None)
"""
2. 属性访问之属性查找顺序与绑定方法
class Student:
stu_school = 'oldboy'
count = 0
# 注意:绑定到对象方法的这种自动传值的特征,决定了在类中定义的函数都要默认写一个参数self,self可以是任意名字,但命名为self是约定俗成的。
# 空对象,'egon',18,'male'
def __init__(self, name, age, sex):
Student.count += 1
self.stu_name = name # 空对象.__dict__['stu_name'] = 'egon'
self.stu_age = age # 空对象.__dict__['stu_age'] = 18
self.stu_sex = sex # 空对象.__dict__['stu_sex'] = 'male
def tell_stu_info(self):
print('学生信息:名字:%s 年龄:%s 性别:%s' % (
self.stu_name,
self.stu_age,
self.stu_sex
))
def set_info(self, name, age, sex):
self.stu_name = name
self.stu_age = age
self.stu_sex = sex
def choose(self, x):
print(f'{self.stu_name}正在选课:{x}')
self.course = x
stu1_obj = Student('egon', 18, 'male') # Student.__init__(空对象, 'egon', 18, 'male')
stu2_obj = Student('lili', 19, 'female')
stu3_obj = Student('jack', 20, 'male')
# 这里统计被实例化的的次数。我们看到上面实例化了三次,所以下面的访问结果都是: 3
print(stu1_obj.count)
print(stu1_obj.count)
print(stu1_obj.count)
# 类中存放的是对象共有的数据和功能
# 一:类可以访问:
# 1、类的数据属性
print(Student.stu_school)
# 2、类的函数属性
print(Student.tell_stu_info)
print(Student.set_info)
# 二:但其实类中的东西是给对象用的
# 1、类中定义的数据属性是共享给所有对象用的,大家访问的地址都一样.
print(id(Student.stu_school))
print(id(stu1_obj.stu_school))
print(id(stu2_obj.stu_school))
print(id(stu3_obj.stu_school))
# 类的数据属性只能通过类改变, 一旦改变类的数据属性, 所有对象共用的类的数据属性也将会被改变
print(stu1_obj.stu_school) # oldboy
print(stu2_obj.stu_school) # oldboy
print(stu3_obj.stu_school) # oldboy
Student.stu_school = 'OLDBOY'
print(stu1_obj.stu_school) # OLDBOY
print(stu2_obj.stu_school) # OLDBOY
print(stu3_obj.stu_school) # OLDBOY
# 但是对象想改变类中的属性是不被允许的, 对象对默认不存在的属性会在自己局部中申明, 并不会影响其他对象, 更不会修改类的数据属性.
stu1_obj.stu_school = 'XXX'
print(stu1_obj.stu_school) # XXX
print(Student.stu_school) # OLDBOY
print(stu2_obj.stu_school) # OLDBOY
print(stu3_obj.stu_school) # OLDBOY
# 2. 类中定义的函数主要是给对象使用的,而且是绑定给对象的,虽然所有对象指向的都是相同的功能,但是绑定到不同的对象就是不同的绑定方法,内存地址各不相同
# 类调用自己的函数属性必须严格按照函数的用法来
print(Student.tell_stu_info) # <function Student.tell_stu_info at 0x00000209620C8DC0>
print(Student.set_info) # <function Student.set_info at 0x00000209620C8EE0>
Student.tell_stu_info(stu1_obj) # 学生信息:名字:egon 年龄:18 性别:male
Student.tell_stu_info(stu2_obj) # 学生信息:名字:lili 年龄:19 性别:female
Student.tell_stu_info(stu3_obj) # 学生信息:名字:jack 年龄:20 性别:male
Student.set_info(stu1_obj, 'EGON', 99, 'female')
Student.tell_stu_info(stu1_obj) # 学生信息:名字:EGON 年龄:99 性别:female
# 绑定方法的特殊之处在于:谁来调用绑定方法就会将谁当做第一个参数自动传入
print(Student.tell_stu_info) # <function Student.tell_stu_info at 0x000002126A568DC0>
print(stu1_obj.tell_stu_info) # <bound method Student.tell_stu_info of <__main__.Student object at 0x000002126A53A6A0>>
print(stu2_obj.tell_stu_info) # <bound method Student.tell_stu_info of <__main__.Student object at 0x000002126A5504F0>>
print(stu3_obj.tell_stu_info) # <bound method Student.tell_stu_info of <__main__.Student object at 0x000002126A550D90>>
stu1_obj.tell_stu_info() # tell_stu_info(stu1_obj)
stu2_obj.tell_stu_info() # tell_stu_info(stu2_obj)
stu3_obj.tell_stu_info() # tell_stu_info(stu3_obj)
stu1_obj.choose('python从入门到放弃')
stu2_obj.choose('python爬虫之从入门到入狱')
stu3_obj.choose('python自动化开发到迎娶白富美')
# 拓展: Python中一切皆为对象,且Python3中"类"与"类型"是一个概念,因而绑定方法我们早就接触过
li1 = [1, 2, 3]
li2 = list([1, 2, 3]) # 上面其实就是在调用类list实例化了一个对象li1
# 每个对象下面都可以访问到类"list"中的公共的函数属性, 基于对象的角度我们称之为绑定方法, 且绑定到不同的对象就是不同的绑定方法, 内存地址个不相同
print(li1.append) # <built-in method append of list object at 0x000002E2C8070C80>
print(li2.append) # <built-in method append of list object at 0x000002E2C8129140>
# 每个实例化出来的对象都是独立的个体, 因此我们往li1对象中添加值, 并不会影响到li2
li1.append(4)
print(li1) # [1, 2, 3, 4]
print(li2) # [1, 2, 3]
# Python3中"类"与"类型"是一个概念
print(type(li1)) # <class 'list'>
print(type(li2)) # <class 'list'>
3. 小结
"""
在上述介绍类与对象的使用过程中, 我们更多是站在底层原理的角度去介绍类与对象之间的关联关系, 如果知识站在使用的角度, 我们无需考虑语法"对象.属性"中的属性到底来自哪里, 只需要直到通过对象获取到的就可以了, 其实通过上面的刨析我们知道如果是数据属性,有对象自己独有的,也有所有对象共有的也就是在类中定义的数据属性. 如果是函数属性, 一般都是在类中定义的, 且通过"对象.函数属性()"调用, 如果是对象调用, 这个函数属性就是改对象的绑定方法, 就会把对象作为第一个参数传入, 而如果是类调用我们它就是一个普通的函数, 函数怎么传值的, 类就必须按照这中方式传值, 这个时候就没有绑定方法这么一说了. 所以说对象是一个由数据+功能高度整合后的产物, 有了对象, 我们就只需要使用"对象.XXX"的语法就可以得到跟这个对象相关的所有数据与功能, 十分方便. 且对于整个程序来说, 提升了解耦合性, 进而极大的提高了可扩展性.
"""