1 面向对象和面向面向过程
1.1 面向对象
- 面向对象是一种编程思想
- 核心关注的是参与解决问题的对象以及他们的行为,把构成问题的各个事物分解成各个对象,将参与的对象抽取成类型,通过类型创建对应的对象
- 最终通过对象之间的互相关联完成问题的解决
- 实现方式:类和对象
- 特点:复用性、扩展性特别好
- 适用于拓展性要求较高的场景
1.2 面向过程
- 面向过程是一种编程思想
- 核心关注的是解决问题的步骤,将每个步骤封装成独立的函数,通过函数的依次调用执行,完成问题的处理,得到处理结果
- 实现方式:函数式编程
- 特点:稳定性好
- 适用于稳定性要求较高的场景
2 类和对象
- 类:类型的简称,是一系列对象的抽象概念,类型中包含了对象的特征属性的描述以及对象 的行为方法的声明
- 对象:实际存在的物体,包含具体的属性特征和行为方法
- 类和对象的关系:类是对象的模板,对象是类的实例,如人是一种类型,唐玄宗和杨贵妃是对象
- 属性:事物具备的特征,如人的性别,年龄等,有类属性和对象属性
- 方法:事物具备的行为,如人的吃饭睡觉行为,有类方法和对象方法
2.1 类的声明
语法:
class 类型名称: # 声明类型即声明一种数据类型
"""类型的文档注释"""
def __init__(self): # 对象的特征属性
"""声明属性的方法"""
pass
def 类型中的方法(self): # 对象的方法行为
pass
- 类型名称命名遵循标识符的规则(字母、数字、下划线组成,数字不能开头),见名知意,使用帕斯卡命名法(大驼峰命名法)
- 属性名称、方法名称遵循标识符规则,变量命名规则(见名知意),使用下划线命名法
- self关键字:在类型的声明中,可以通过 self描述当前的某个对象,类似生活中的“我”,每个对象都有一个自己的 self 关键字,self是约定俗成的用法,也可以用其他字符代替
# 声明人类型:
class Person:
"""人的类型"""
# 初始属性,姓名,性别,身份
def __init__(self, name, gender, position): # 初始属性
self.name = name
self.gender = gender
self.position = position
# 娱乐的行为
def entertainment(self, item):
if item == "drum":
print("渔阳鼙鼓动地来")
elif item == "dance":
print("惊破《霓裳羽衣曲》")
else:
print("骊宫高处入青云,仙乐风飘处处闻")
2.2 对象的创建和操作
语法:
引用变量 = 类型名称( 属性参数列表)
创建了一个指定类型的对象,将对象在内存中的地址赋值给一个引用变量。
# 创建对象(续2.1中代码)
# 引用变量 = 类型名称(属性参数)
emperor = Person("李隆基", "男", "皇帝", "唐玄宗")
emperor_wife = Person("杨玉环", "女", "贵妃", "杨贵妃")
3 实例属性和实例方法
3.1 实例属性
- 声明位置:声明在类型中的__init__()方法中,是每个对象独有的特征数据
- 数据获取:对象的引用变量.属性名称
- 数据修改(或新增):对象的引用变量.属性名称 = 新的数据(尽量避免用该方法新增数据)
# 操作对象的属性(续2.2代码):
# 获取数据: 引用变量.属性名称
print(emperor.name) # 李隆基
print(emperor_wife.nickname) # 杨贵妃
# 修改(或新增)数据:对象的引用变量.属性名称 = 新的数据
# 尽量避免用该方法新增数据
emperor.name = "唐明皇"
print(emperor.name) # 唐明皇
3.2 实例方法
- 声明在类型的内部的函数,函数的第一个参数是:self,是每个对象独有的行为
- 类型中编写的self是一种固定语法,该类型创建的每个对象,都会有各自的self存在,对象通过self调用当前对象的属性/方法
- 当函数一旦声明在类型中,就被称为类型的方法
- 使用:对象的引用变量.方法名称(参数)
# 操作对象的方法(续3.1代码):
# 引用变量.方法名称([参数])
emperor_wife.grow_up() # 杨家有女初长成,养在深闺人未识。
emperor.entertainment("drum") # 渔阳鼙鼓动地来
emperor.entertainment("dance") # 惊破《霓裳羽衣曲》
4 类属性
- 声明在类型的内部,方法的外部,遵循变量的命名规范
- 类属性属于类型的特征,可以被类型名称直接访问,可以被当前类型的所有对象访问
- 类属性只能被类型名称修改
- 类属性不能被对象修改(赋值)
访问:
- 通过类型名称访问:类名.类属性变量
- 通过对象直接访问:对象变量.类属性变量
修改(新增):
- 通过类型名称修改(没有该变量则会新增属性):类名.类属性变量 = 数据
对象实例属性操作类属性(避免此操作):
- 对象变量.类属性变量 = 数据
- 对象变量.新属性变量 = 数据
- 以上两种方式只改变该对象中的数据,类属性没有被修改
- 不可变类型数据:
都是给对象添加了属性,而不是修改类属性 - 可变数据类型:
如果对象是修改可变类型中的数据,是真正的修改(类属性也被修改)
如果是重新给可变类型变量赋值,则是给对象添加属性
class Person:
"""人的类型"""
# 类属性
nationality = "唐"
# 可变数据类属性
entertainment = ["渔阳鼙鼓动地来"]
# 创建对象
emperor = Person()
emperor_wife = Person()
# 可以通过类型名称访问:类名.类属性变量
print(Person.nationality) # 唐
# 可以通过对象直接访问:对象变量.类属性变量
print(emperor.nationality) # 唐
# 修改(没有改变量则会新增)
# 只能通过类型名称修改:类名.类属性变量 = 数据
Person.nationality = "大唐"
print(Person.nationality) # 大唐
print(emperor.nationality) # 大唐 # 实例中的类属性也被修改
# 对象实例属性扩展(避免此操作)
# 对象变量.类属性变量 = 数据
# 对象变量.新属性变量 = 数据
# 以上两种方式只改变该对象中的数据,类属性没有被修改
emperor.nationality = "唐朝"
print(emperor.nationality) # 唐朝(只是添加属性)
print(emperor_wife.nationality) # 大唐 # 其他对象的类属性不会被修改
print(Person.nationality) # 大唐 # 类属性没有被修改
emperor.country = "唐朝" # 只是新增和类属性值相等的对象属性,不修改原来类属性
print(emperor.country) # 唐朝
print(Person.nationality) # 大唐
# print(emperor_wife.country) # 没有该属性,会报错
# print(Person.country) # 没有该属性,会报错
emperor.entertainment.append("惊破《霓裳羽衣曲》") # 实例中修改可变类型中的数据
print(Person.entertainment) # ['渔阳鼙鼓动地来', '惊破《霓裳羽衣曲》'] # 类属性也实际被修改
emperor.entertainment = ["缓歌慢舞凝丝竹", "尽日君王看不足"] # 实例给可变类型重新赋值
print(Person.entertainment) # ['渔阳鼙鼓动地来', '惊破《霓裳羽衣曲》'] # 只是给实例对象添加属性,类属性没有被修改
5 魔术方法
魔术方法是类型中从object继承(继承见下一篇文章)过来的,使用双下划线开头和结尾,具有特殊功能的方法
5.1 对象的打印: __str__()和__repr__()
- 对象一旦创建完成,是将对象在内存中的地址临时存储在引用变量中的通过引用变量才能访问到对象
- 在程序中print()直接打印可以找到对象的引用变量时,就会得到引用变量中存储的内存地址数据
- __str__(self)方法,用于 print()直接打印对象时展示自定义数据
- 但当对象包含在组合数据类型中时,直接打印数据类型,此时的对象又会变成了内存地址的形式
- __repr__(self)方法,用于当对象包含在组合数据类型中时,按照自定义的方式展示对象的数据
- 两种方法返回的必须是字符串,不能是数字
- 如果没有__str__()方法,会寻找执行__repr__()方法,有同样输出结果(无论对象是否在组合数据中)
直接打印前面创建好的对象,会得到地址:
print(emperor) # <__main__.Person object at 0x000002B9BA53DC88>
在4的类定义的代码上,添加__str__(self)方法:
class Person:
"""人的类型"""
# 类属性
nationality = "唐"
# 初始属性,姓名,性别,身份,称呼
def __init__(self, name, gender, position, nickname): # 初始属性
self.name = name
self.gender = gender
self.position = position
self.nickname = nickname
# __str__()方法:
def __str__(self):
"""对象打印的方法"""
return f"这是对象实例 {self.name}"
# 成长的行为
def grow_up(self):
print("杨家有女初长成,养在深闺人未识。")
# 娱乐的行为
def entertainment(self, item):
if item == "drum":
print("渔阳鼙鼓动地来")
elif item == "dance":
print("惊破《霓裳羽衣曲》")
else:
print("骊宫高处入青云,仙乐风飘处处闻")
再打印对象:
# 打印对象
print(emperor) # 这是对象实例 李隆基
print(emperor_wife) # 这是对象实例 杨玉环
此时如果对象在组合数据中,打印组数据又会显示内存地址:
lst = [emperor, emperor_wife]
print(lst) # [<__main__.Person object at 0x000002C675DCDDC8>, <__main__.Person object at 0x000002C675E42108>]
继续上面类定义的代码上,添加__repr__(self)方法:
class Person:
"""人的类型"""
# 类属性
nationality = "唐"
# 初始属性,姓名,性别,身份,称呼
def __init__(self, name, gender, position, nickname): # 初始属性
self.name = name
self.gender = gender
self.position = position
self.nickname = nickname
# __str__()方法:
def __str__(self):
"""对象打印的方法"""
return f"这是对象实例 {self.name}"
# __repr__()方法
def __repr__(self):
"""对象包含在组合数据类型中的打印方法"""
return f"这是对象实例 {self.name}"
# 成长的行为
def grow_up(self):
print("杨家有女初长成,养在深闺人未识。")
# 娱乐的行为
def entertainment(self, item):
if item == "drum":
print("渔阳鼙鼓动地来")
elif item == "dance":
print("惊破《霓裳羽衣曲》")
else:
print("骊宫高处入青云,仙乐风飘处处闻")
再次打印,则会按照自定义方式展示:
# 对象包含在组合数据类型中打印
lst = [emperor, emperor_wife]
print(lst) # [这是对象实例 李隆基, 这是对象实例 杨玉环]
5.2 其他魔术方法
对象构建:
- __new__(cls) 创建对象,(创建对象时)自动调用,定义类时一般不需要自己写
- __init__ 初始化实例,给创建的对象赋值,(创建对象成功后)自动调用
对象打印:
- __str__(self) 使用print(对象引用变量) 输出自定义字符串数据
- __repr__(self) 当其他对象引用使用当前对象时(当对象包含在组合数据类型中
时),打印展示自定义字符传数据
对象比较:
- __cmp__(self, other) 大于 other 对象数返回1、等于 other 对象其他数返回0、小于 other 对象数返回-1(分别设置)
- __gt__(self, other) 判断当前对象是否大于 other 对象
- __lt__(self, other) 判断当前对象是否大于 other 对象
- __ge__(self, other) 判断当前对象是否大于等于 other 对象
- __le__(self, other) 判断当前对象是否大于 other 对象
- __eq__(self, other) 判断当前对象是否等于 other 对象
- __ne__(self, other) 判断当前对象是否不等于 other 对象
四则运算:
- __add__(self, other) +
- __sub__(self, other) -
- __mul__(self, other) *
- __divmod__(self, other) 获取整除结果和余数
- __floordiv__(self, other) //, 下取整
- __pow__(self, other) 幂运算**
增量运算:
- __iadd__(self, other) +=
- __isub__(self, other) -=
- __imul__(self, other) *=
- __idiv__(self, other) /=
- __ifloordiv__(self, other) //=
- __ipow__(self, other) **=
函数式调用:
- __call__(self) 让对象可以类似函数的方式直接调用执行
对象删除:
- __del__(self) 对象将要被删除的操作(执行删除命令,先执行里面的代码后再进行删除
查看属性:
- __dict__() 查看属性(包括私有属性)
部分魔术方法举例:
class Person:
"""人的类型"""
# 初始属性
def __init__(self, name, age): # 初始属性
self.name = name
self.age = age
def __le__(self, other):
"""小于等于other返回结果"""
if self.age <= other.age:
return True
return False
def __cmp__(self, other):
if self.age > other.age:
return 1
elif self.age < other.age:
return -1
else:
return 0
def __floordiv__(self, other):
"""除法,向下取整"""
return self.age // other.age
def __call__(self, *args, **kwargs):
"""函数式调用"""
print(args, kwargs)
print(f"{self.name} 年龄 {self.age}")
li = Person("李隆基", 52)
yang = Person("杨玉环", 18)
print(li.__cmp__(yang)) # 1
print(li >= yang) # True
print(li // yang) # 2
yang("回眸一笑百媚生,六宫粉黛无颜色。")
# ('回眸一笑百媚生,六宫粉黛无颜色。',) {}
# 杨玉环 年龄 18