Python 面向对象

面向过程和面向对象

一、面向过程

​ 核心点在过程二字,过程指的是解决问题的步骤,说白了就是先做什么再干什么。这种解决问题的思路就好比是工厂中的流水线。shell脚本就是典型的面向过程,按步骤做事。

  • 优点:复杂的问题流程化,进而简单化。

  • 缺点:可扩展性差,比如,一个脚本就是干一件事情的。

二、面向对象

​ 核心点是对象二字,对象指的是具有相同属性和动作的结合体叫对象。面向对象编程就好比在代码中创造一个世界,创造若干对象,就像现实世界中的万物一样,每个物体都可以有自己的属性和动作。

  • 优点:可扩展性强

  • 缺点:编程的复杂度高于面向过程

类和对象

​ 类是指具有相同属性和技能的一类事物,对象是指具体的类的表现,具体的实实在在的一个实例。比如动物时一个类,狗就是动物类的一个实例化对象。

1
2
3
4
5
6
# 定义一个类
class Animal:
pass

# 实例化一个对象
dog = Animal()

​ 类体一共有两个部分,定义在类中变量叫做静态变量(又称变量或者静态字段),定义在类中的方法叫做动态变量(又称方法或者函数)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Person:
# 静态变量,又叫变量或静态字段
animal = "高级动物"
soul = "有灵魂"
language = "有语言"

# 动态变量,又叫方法或者函数
def work(self):
print("人类可以工作...")

def shop(self):
print("人类可以购物...")

p1 = Person() # 实例化一个对象

从类名的角度操作类中的内容

一、操作类中的静态变量

1
2
3
4
5
6
7
8
9
10
11
12
class Person:
# 静态变量,又叫变量或静态字段
animal = "高级动物"
soul = "有灵魂"
language = "有语言"

# 动态变量,又叫方法或者函数
def work(self):
print("人类可以工作...")

def shop(self):
print("人类可以购物...")
  • 通过__dict__来查看类中所有的内容,但是只能进行查询,不能对类中的内容进行增删改的操作
1
2
3
4
5
6
7
8
9
10
11
12
print(Person.__dict__)

'''
获取到的内容是一个字典
{'__module__': '__main__', 'animal': '高级动物', 'soul': '有灵魂', 'language': '有语言', 'work': <function Person.work at 0x000001A89C34D8C8>, 'shop': <function Person.shop at 0x000001A89C34D9D8>, '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
'''

# 获取具体的值
print(Person.__dict__["soul"]) # 有灵魂

# 尝试修改值
Person.__dict__['soul'] = '行尸走肉' # TypeError: 'mappingproxy' object does not support item assignment
  • 通过 . 号来操作类中的静态变量,可以进行增删改查
1
2
3
4
5
6
print(Person.soul)  # 查
Person.mind = "有思想" # 增
Person.soul = "行尸走肉" # 改
del Person.animal # 删

print(Person.__dict__)

二、操作类中的方法

1
2
3
# 除了静态方法和类方法,一般不用类名去操作类中的方法
# Person.work() # 必须任意传一个参数
Person.work(111)

从对象角度来操作类中的内容

在类中,名为__init__的函数称为类的初始化方法,在实例化一个对象时,实际上进行了以下操作:

  1. 创建了一个对象空间(实例空间)
  2. 自动执行类中的__init__方法,并将对象空间传给self参数
  3. 执行具体的__init__代码,给对象空间封装属性。
  4. 对象空间将空间指针返回给调用者,即自己定义的对象名。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 定义一个类
class Person:
# 静态变量,又叫变量或静态字段
animal = "高级动物"
soul = "有灵魂"
language = "有语言"

# 构造方法
def __init__(self, name, age, sex):
print(self) # <__main__.Person object at 0x000001FF4D7CCD68>
self.name = name
self.age = age
self.sex = sex

# 动态变量,又叫方法或者函数
def work(self):
print("人类可以工作...")

def shop(self):
print("人类可以购物...")

p1 = Person("cdc", 18, "male") # 类名+():实例化一个对象
print(p1) # <__main__.Person object at 0x000001FF4D7CCD68>

# self的空间地址和实例化对象的空间地址一样,简单来说,self就是实例化出来的对象

一、操作对象中的静态变量

  • 通过__dict__来查看对象中所有的内容,但是只能进行查询,不能对对象中的内容进行增删改的操作
1
2
print(p1.__dict__)
# {'name': 'cdc', 'age': 18, 'sex': 'male'}
  • 通过 . 号来操作对象中的静态变量,可以进行增删改查;通过 . 号来操作类中的静态变量时,只能查询,不能进行其他操作。
1
2
3
4
5
print(p1.name)  # 查
p1.high = 175 # 增
del p1.name # 删
p1.age = 73 # 改
print(p1.__dict__)
1
2
p1.language = "说汉语"
# 这一步操作只能给对象增加一个language属性,而无法修改类中的静态变量language

二、操作对象的方法

1
2
3
# 不需要额外传参数,调用方法时,对象会将自己的空间地址传给方法中的self
p1.work()
p1.shop()

类的名称空间和查询顺序

在定义一个类,到实例化一个对象,再到调用对象相关的静态变量或方法这个过程中,实际上经历了以下过程:

第一步,python解释器从上往下读取类的定义代码,读取完后会在内存中开辟一个存储类的名称空间;

第二部,执行实例化对象代码,会产生一个含有对象指针的名称空间,自动执行类中的__init__方法,并将对象空间传给self参数;

第三步,将实例化对象时传入的参数传给类中的构造函数;

第四步,执行具体的构造函数代码,给对象空间封装属性。

第五步,执行 print(p1.animal) 时,会先在对象的名称空间内进行查找 animal 属性,查找不到,再从类空间找,再找不到,再从父类找….

注意:

  • 直接从类中查找属性时,先从本类空间找,如果找不到,再从父类找……
  • 查找属性时可以从对象空间查找到类空间,但是不能从类空间往对象空间查找;
  • 对象之间相互独立,对象空间之间相互对立。

组合

​ 有这么一个需求,创建两个游戏角色,角色有名字、攻击力、生命值三个属性,角色可以互相攻击,此方法要完成谁攻击谁,谁掉了多少血, 还剩多少血’的提示功能。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class GameRole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp

def attack(self, obj):
obj.hp = obj.hp - self.ad
print(f"{self.name}攻击了{obj.name}{obj.name}掉了{self.ad}血,还剩{obj.hp}血")


p1 = GameRole("那鲁多", 10, 100)
p2 = GameRole("萨斯给", 20, 80)

p1.attack(p2)

现在版本升级,角色可以使用武器,最后提示谁使用了哪种武器攻击了谁,谁掉了多少血还剩多少血

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
class GameRole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp

def attack(self, obj):
obj.hp = obj.hp - self.ad
print(f"{self.name}攻击了{obj.name}{obj.name}掉了{self.ad}血,还剩{obj.hp}血")


class Weapon:
def __init__(self, name, ad):
self.name = name
self.ad = ad

def fight(self, obj1, obj2):
obj2.hp = obj2.hp - self.ad
print(f"{obj1.name}使用了{self.name}攻击了{obj2.name}{obj2.name}掉了{self.ad}血还剩{obj2.hp}血")


p1 = GameRole("那鲁多", 10, 100)
p2 = GameRole("萨斯给", 20, 80)

w1 = Weapon("苦无", 20)
w2 = Weapon("手里剑", 10)

w1.fight(p1, p2)

​ 虽然功能实现了,但是这样实现不合理,人物利用武器攻击别人,你的动作发起者是人,而不是武器。所以对于这种情况,可以用组合来解决。

组合:给一个类的对象封装一个属性,这个属性是另一个类的对象。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
class GameRole:
def __init__(self, name, ad, hp):
self.name = name
self.ad = ad
self.hp = hp

def attack(self, obj):
obj.hp = obj.hp - self.ad
print(f"{self.name}攻击了{obj.name}{obj.name}掉了{self.ad}血,还剩{obj.hp}血")

def armament_weapon(self, weapon):
self.weapon = weapon


class Weapon:
def __init__(self, name, ad):
self.name = name
self.ad = ad

def fight(self, obj1, obj2):
obj2.hp = obj2.hp - self.ad
print(f"{obj1.name}使用了{self.name}攻击了{obj2.name}{obj2.name}掉了{self.ad}血还剩{obj2.hp}血")


p1 = GameRole("那鲁多", 10, 100)
p2 = GameRole("萨斯给", 20, 80)

w1 = Weapon("苦无", 20)
w2 = Weapon("手里剑", 10)

p1.armament_weapon(w1) # 给那鲁多装备了苦无这个对象
# print(p1.weapon)
# print(p1.weapon.name)
# print(p1.weapon.ad)
p1.weapon.fight(p1, p2)

Python 面向对象
https://clark-cdc.github.io/2019/05/03/0011-面向对象初识/
作者
clark
发布于
2019年5月3日
许可协议