面向对象、类
一、面向对象
什么是面向对象?
面向对象是一门编程思想!
面向过程编程思想与面向对象编程思想对比:
- 面向过程编程思想:
核心是 “过程”二字,过程是指解决问题的步骤,即先干什么再干什么!
基于该编程思想编写程序,就好比在设计一条工厂流水线,一种机械式的思维方式
优点:
将复杂的问题流程化,进而简单化
缺点:
牵一发而动全身,程序的可扩展性差
- 面向对象编程思想:
核心是 “对象” 二字,对象指的是“特征与技能”的结合体。
基于该编程思想编写程序,就好比把自己当做一个上帝在创造世界,一种“上帝式”的思维方式
优点:
可扩展性高
缺点:
面向对象编写程序的复杂程度比面向过程高
注意:编程思想仅仅是一门思想,与任何技术无关
二、类
1、什么是类?
类指的是类型、类别。
在两种角度去看待类:
- 现实世界中:
- 先有一个个对象,经过社会的文明发展,随之总结出类,对象是实际存在的,而类是抽象产生的
- python程序中:
- 必须先由类,再通过调用类产生对象
对象指的是“特征与技能”的结合体, 类指的是一系列“对象之间相同的特征与技能”的结合体
2、如何定义类?
如何写类并产生对象:
- 先从现实世界中通过一个个对象总结出类
- 然后再定义类,后调用产生对象。
比如 “选课系统”:
选课系统 - 学生类:
- 学生对象1:
特征:
- 姓名:tank
- 性别:female
- 年龄:95
- 学校:家里蹲
技能:
- 技术:python
- 学习:learn
- 选课:course
- 学生对象2
特征:
- 姓名:jason
- 性别:female
- 年龄:99
- 学校:家里蹲
技能:
- 技术:python
- 学习:learn
- 选课:course
3、定义类的语法
语法:
class 关键字:帮你产生类
class 类名:
- 对象之间相同的特征
- 学校
school = "家里蹲"
- 对象之间相同的技能
- python
def Python():
pass
- learn
def Learn():
pass
- course
def Course():
pass
类名的规范:
- 驼峰命名法(推荐)
定义一个学生类并对属性进行增删查改:
# 定义一个家里蹲学生类
class JldStudent: # 类名指向的是类的内存地址
# 学生相同的特征
# 在类中的特征(即变量)称之为“属性”
school = "OldBoy"
# 注意:在类内部定义函数,会默认产生一个参数self
# 学生相同的技能(即函数)称之为“方法”
def learn(self): # self此处当做一个形参
print("learning...")
# 查看类的名称空间
print(JldStudent.__dict__)
print(JldStudent.__dict__["school"]) # 可调用属性
print(JldStudent.__dict__["learn"]) # 获取learn方法对象
JldStudent.__dict__["learn"](123) # 方法对象+(),相当于执行learn函数(方法)
# 类提供了一种特殊获取名字的方式 “类名.名字”的方式
# 查
print(JldStudent.school)
JldStudent.learn(12)
# 改
JldStudent.school = "Cld"
print(JldStudent.school)
# 删
del JldStudent.school
# print(JldStudent.school) # AttributeError: type object 'JldStudent' has no attribute 'school'
# 增
JldStudent.age = 18
print(JldStudent.age)
执行结果:
{'__module__': '__main__', 'school': 'OldBoy', 'learn': <function JldStudent.learn at 0x0000026D35CD97B8>, '__dict__': <attribute '__dict__' of 'JldStudent' objects>, '__weakref__': <attribute '__weakref__' of 'JldStudent' objects>, '__doc__': None}
OldBoy
<function JldStudent.learn at 0x0000026D35CD97B8>
learning...
OldBoy
learning...
Cld
18
4、类的名称空间
- 类的名称空间:
在定义阶段时产生,会将类中所有的名字,扔进类的名称空间
- 函数的名称空间:
在调用函数时产生,函数调用结束后销毁
三、对象
1、对象的产生
对象名=类名() ---> 调用类产生对象
类的名称空间在定义时产生,对象的名称空间在调用类时产生
调用类产生对象的过程称之为类的实例化,对象称之为一个类的实例
定义一个类:
class Student:
# 学校 ---> 属性
school = "Jld"
# 技能 ---> 方法
def learn(self):
print(self) # 打印输出为 <__main__.Student object at 0x00000000021B6248>,是一个对象
print("learning...")
# 获取对象,产生对象
obj = Student()
print(obj) # 打印输出为 <__main__.Student object at 0x00000000021B6248>,也是一个对象
# 对象调用类内部的名字(属性)
print(obj.school)
# 对象调用类内部的函数(方法),无需传入参数:会将对象当做第一个参数传给该函数(方法)
obj.learn() # 对象的绑定方法,无需传参
执行结果:
<__main__.Student object at 0x000001BE191C6908>
Jld
<__main__.Student object at 0x000001BE191C6908>
learning...
由对象来调用类内部的函数,称之为对象的绑定方法。
对象的绑定方法特殊之处:会将对象当做第一个参数自动传给该函数(方法),所以对象调用类内部的函数时无需传参
2、为对象添加独有的属性
定义一个类:
class Student: # 学校 --> 属性 school = "Jld" def __init__(self, name, sex, age): # 参数:obj1, "张三", "female", 84 print(self.__dict__) # 给对象添加新的属性 self.name = name # stu1.name = "张三" self.sex = sex # stu1.sex = "female" self.age = age # stu1.age = 84 print(self.__dict__) # 技能 ---> 方法 def learn(self): print("learning...") # obj1 = Student() # Student(obj1) ---> __init__(self) ---> self==obj1 # obj2 = Student() # Student(obj2) ---> __init__(self) ---> self==obj2 # obj3 = Student() # Student(obj3) ---> __init__(self) ---> self==obj3
# 给对象添加独有的属性: # 第一种方法(很麻烦):调用类后,为对象传入对象独有的特征(属性) obj1.name = "张三" obj1.sex = "female" obj1.age = 84 # 第二种方法:调用类时,为对象传入对象独有的特征(属性) # __init__(self, name, sex, age):obj1 --> self "张三" --> name "female" --> sex 84 --> age # 调用类时:会将对象当做第一个参数,并与括号内所有的参数一并传给__init__() obj1 = Student("张三", "female", 84)
执行结果:
{}
{'name': '张三', 'sex': 'female', 'age': 84}
注意:凡是在类内部定义的.__或者__结尾的方法都有特殊意义
__init__():在类内部定义的方法,在调用类时触发,会自动将对象当做第一个参数自动传入并执行
3、对象名字的查找顺序
对象名字的查找顺序:
① 对象.属性,会先找对象自己的
② 若对象没有,会去找类的
③ 对象没有,类也没有,就会报错
定义一个类:
class people:
country = "China"
name = "李四"
def __init__(self, name, sex, age):
self.name = name
self.sex = sex
self.age = age
def run(self):
print("running...")
obj1 = people("张三", "female", 95)
print(obj1.name) # 张三 找对象自己的name属性
print(obj1.country) # China 对象没有,找类中的属性
# 报错:AttributeError: 'people' object has no attribute 'json'
print(obj1.json) # 对象没有,类也没有,就报错
4、一切皆对象
在python中一切皆对象
比如:python的八大数据类型都是类
定义数据类型时,内部会自动调用相应的类,然后产生对象
"""
在python中一切皆对象
"""
class foo:
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
# 产生对象
# 调用类产生对象的过程称之为类的实例化,对象称之为一个类的实例
foo_obj = foo(10, 20, 30)
print(foo_obj)
# 查看 字符串 类型
print(type(str)) # <class 'type'>
# 查看 列表 类型
print(type(list)) # <class 'type'>
# 查看 字典 类型
print(type(dict)) # <class 'type'>
# 查看 元组 类型
print(type(tuple)) # <class 'type'>
# 查看 浮点型 类型
print(type(float)) # <class 'type'>
# 查看 整型 类型
print(type(int)) # <class 'type'>
# 查看 布尔值 类型
print(type(bool)) # <class 'type'>
# 除了这些还有很多都是类,既然是类,就可以是对象,所以在python中一切皆对象
执行结果:
<__main__.foo object at 0x0000014471D71C88>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
<class 'type'>
四、根据面向对象编程编写一个“人狗大作战”例子
"""
需求:有一个人对象,狗对象,狗咬人,人打狗
- 对象人1
- 特征
- 生命:1000
- 名字:name = "张三"
- 攻击力:arg
- 技能
- 打:hit
- 对象人2
- 特征
- 生命:950
- 名字:name = "李四"
- 攻击力:arg
- 技能
- 打:hit
- 抽象出类
- 人类:
- 相同特征
- 生命
- 相同特征
- 打
- 狗对象1
- 特征
- 生命值:150
- 名字:name = "二哈"
- 品种:dog_type = "哈士奇"
- 攻击力:arg
- 技能
- 咬:bite
- 狗对象2
- 特征
- 生命值:250
- 名字:name = "小短腿"
- 品种:dog_type = "柯基"
- 攻击力:arg
- 技能
- 咬:bite
- 抽象出类
- 狗类:
- 相同特征
- 生命
- 相同特征
- 咬
"""
import time
# 人类
class People:
def __init__(self, name, life, arg):
self.name = name
self.life = life
self.arg = arg
# 人对象调用hit时,传入狗对象
def hit(self, dog_obj):
print(f"[{self.name}]要开始打[{dog_obj.name}]了")
# 减掉狗对象的生命值 值为人对象的攻击力
dog_obj.life -= self.arg
print(f"[{dog_obj.name}]的生命值减掉:[{self.arg}],剩余血量为:[{dog_obj.life}]")
if dog_obj.life <= 0:
print(f"[{dog_obj.name}]已经没了")
return True
# 狗类
class Dogs:
def __init__(self, name, life, dog_type, arg):
self.name = name
self.life = life
self.dog_type = dog_type
self.arg = arg
# 狗对象调用bite时,传入人对象
def bite(self, p_obj):
print(f"[{self.name}]要开始咬[{p_obj.name}]了")
# 减掉人对象的生命值 值为狗对象的攻击力
p_obj.life -= self.arg
print(f"[{p_obj.name}]的生命值减掉:[{self.arg}],剩余血量为:[{p_obj.life}]")
if p_obj.life <= 0:
print(f"[{p_obj.name}]已经没了")
return True
p1 = People("张三", 1000, 50) # 人对象
d1 = Dogs("二哈", 150, "哈士奇", 200) # 狗对象
while True:
# 开始狗咬人,人打狗
res1 = d1.bite(p1)
if res1:
break
time.sleep(1) # 休眠1秒
res2 = p1.hit(d1)
if res2:
break
time.sleep(1) # 休眠1秒
执行结果:
[二哈]要开始咬[张三]了
[张三]的生命值减掉:[200],剩余血量为:[800]
[张三]要开始打[二哈]了
[二哈]的生命值减掉:[50],剩余血量为:[100]
[二哈]要开始咬[张三]了
[张三]的生命值减掉:[200],剩余血量为:[600]
[张三]要开始打[二哈]了
[二哈]的生命值减掉:[50],剩余血量为:[50]
[二哈]要开始咬[张三]了
[张三]的生命值减掉:[200],剩余血量为:[400]
[张三]要开始打[二哈]了
[二哈]的生命值减掉:[50],剩余血量为:[0]
[二哈]已经没了
五、面向对象总结
面向对象:
核心是“对象”,对象指的是特征与技能的结合体
基于该编程思想编写程序,就好比在创造世界,一种上帝式的思维方式
优点:
可扩展性强
缺点:
编写复杂难度较面向过程高
1、类的实例化:调用类的过程称之为类的实例化,产生的对象也可以称之为类的一个实例
调用类产生对象发生的事情:
① 会产生一个空对象的名称空间
② 会自动触发__init__,并且会将对象当做第一个参数传入
③ 会将调用类括号内的参数一并传给__init__()
# 定义一个类
class People:
national = "han"
def __init__(self, name, age, sex):
self.name = name
self.age = age
self.sex = sex
# 注意:看到self就应该知道是对象本身,在这里就是下面代码中的p_obj对象
def learn(self):
print("learning...")
p_obj = People("张三", 184, "female")
print(p_obj.name, p_obj.age, p_obj.sex)
执行结果:
张三 184 female
2、查看类与对象的名称空间:类.__dict__ 对象.__dict__
# 使用上面People类执行下列代码
print(People.__dict__) # 查看类的名称空间
print(p_obj.__dict__) # 查看对象的名称空间
执行结果:
{'__module__': '__main__', 'national': 'han', '__init__': <function People.__init__ at 0x000001B4D9BC96A8>, 'learn': <function People.learn at 0x000001B4D9BC9620>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
{'name': '张三', 'age': 184, 'sex': 'female'}
3、类或对象的属性操作:增、删、查、改
# 使用上面People类执行下列代码
# 类的属性操作
People.sal = 3000 # 增
People.number = 1 # 增
del People.number # 删
People.age = 60 # 改
print(People.age) # 查
# 对象的属性操作
p_obj.county = "China" # 增
del p_obj.sex # 删
p_obj.name = "李四" # 改
print(p_obj.name) # 查
执行结果:
4500
李四
4、类中数据属性(类中的变量):类中的属性是给对象使用的,对象引用类中的属性,指向的都是类中同一个内存地址
# 使用上面People类执行下列代码
p_obj1 = People("王五", 84, "female")
p_obj2 = People("赵六", 85, "male")
p_obj3 = People("孙七", 86, "female")
print(p_obj1.national, id(p_obj1))
print(p_obj2.national, id(p_obj2))
print(p_obj3.national, id(p_obj3))
执行结果:
han 2612115777632
han 2612115777744
han 2612115777800
5、类中的方法(类中的函数):类中的方法是给对象使用的,由对象来调用就会将方法绑定给不同的对象,并且会将对象当做第一个对象传入
# 使用上面People类执行下列代码
p_obj1 = People("王五", 84, "female")
p_obj2 = People("赵六", 85, "male")
p_obj3 = People("孙七", 86, "female")
print(People.learn)
print(p_obj1.learn)
print(p_obj2.learn)
print(p_obj3.learn)
执行结果:
<function People.learn at 0x0000022590CA9620>
<bound method People.learn of <__main__.People object at 0x0000022590CBA860>>
<bound method People.learn of <__main__.People object at 0x0000022590CBA8D0>>
<bound method People.learn of <__main__.People object at 0x0000022590CBA908>>
6、对象属性的查找顺序:先从对象自己的名称空间找 ---> 类的名称空间查找
# 使用上面People类执行下列代码
print(p_obj1.national) # 从类中查找
print(p_obj1.national2) # 类中也没有,报错
7、对象绑定方法的特殊之处
① 会将对象当做第一个参数传入
② 若对象的绑定发放中还有其他参数,会一并传入。
8、一切皆对象:在python3中,类即类型
# 使用上面People类执行下列代码
print(type(p_obj)) # <class '__main__.People'>
str1 = "orange"
print(type(str1)) # <class 'str'>
list1 = [1, 2, 3]
print(type(list1)) # <class 'list'>
执行结果:
<class '__main__.People'>
<class 'str'>
<class 'list'>