一、对象之间的交互

现在我们已经有一个人类了,通过给人类一些具体的属性我们就可以拿到一个实实在在的人。

现在我们要再创建一个狗类,狗就不能打人了,只能咬人,所以我们给狗一个 bite 方法。

有了狗类,我们还要实例化一只实实在在的狗出来。

然后人和狗就可以打架了。现在我们就来让他们打一架吧!

创建一个狗类

  1. class Person:
  2. role = 'person' # 静态属性
  3. def __init__(self, name, sex, hp, ad):
  4. self.name = name # 对象属性 属性
  5. self.sex = sex
  6. self.hp = hp
  7. self.ad = ad
  8. def attack(self):
  9. print('%s 发起了一次攻击' % self.name)
  10. class Dog:
  11. role = 'person' # 静态属性
  12. def __init__(self, name, kind, hp, ad):
  13. self.name = name # 对象属性 属性
  14. self.kind = kind
  15. self.hp = hp
  16. self.ad = ad
  17. def bite(self):
  18. print('%s 咬了人一口' % self.name)
  19. alex = Person('a_sb', '不详', 1, 5)
  20. boss_jin = Person('金老板', '女', 20, 50)
  21. teddy = Dog('笨笨', 'teddy', 50, 10)
  22. alex.attack() # 相当于执行 Person.attack(alex)
  23. boss_jin.attack() # 相当于执行 Person.attack(boss_jin)
  24. teddy.bite()

执行输出:

a_sb 发起了一次攻击

金老板发起了一次攻击

笨笨咬了人一口

那么问题来了,人发起了一次攻击,他攻击了谁?

交互 teddy 打 alex 一下

  1. class Person:
  2. role = 'person' # 静态属性
  3. def __init__(self, name, sex, hp, ad):
  4. self.name = name # 对象属性 属性
  5. self.sex = sex
  6. self.hp = hp
  7. self.ad = ad
  8. def attack(self):
  9. print('%s 发起了一次攻击' % self.name)
  10. class Dog:
  11. role = 'person' # 静态属性
  12. def __init__(self, name, kind, hp, ad):
  13. self.name = name # 对象属性 属性
  14. self.kind = kind
  15. self.hp = hp
  16. self.ad = ad
  17. def bite(self,people): #people 是变量名,它是一个对象
  18. people.hp -= self.ad #人掉血
  19. print('%s 咬了%s 一口,%s 掉了%s 点血' % (self.name,people.name,people.name,self.ad)) #由于 people 是对象,取 name 就是 people.name
  20. alex = Person('a_sb', '不详', 1, 5)
  21. boss_jin = Person('金老板', '女', 20, 50)
  22. teddy = Dog('笨笨', 'teddy', 50, 10)
  23. teddy.bite(alex) #alex 是一个对象,把对象传进去了
  24. print(alex.hp) #查看 alex 的血

执行输出:

笨笨咬了 a_sb 一口,a_sb 掉了 10 血

-9

再完善人攻击狗的技能

  1. class Person:
  2. role = 'person' # 静态属性
  3. def __init__(self, name, sex, hp, ad):
  4. self.name = name # 对象属性 属性
  5. self.sex = sex
  6. self.hp = hp
  7. self.ad = ad
  8. def attack(self,dog):
  9. dog.hp -= self.ad
  10. print('%s 攻击了%s,%s 掉了%s 点血' % (self.name,dog.name,dog.name,self.ad))
  11. class Dog:
  12. role = 'person' # 静态属性
  13. def __init__(self, name, kind, hp, ad):
  14. self.name = name # 对象属性 属性
  15. self.kind = kind
  16. self.hp = hp
  17. self.ad = ad
  18. def bite(self,people): #people 是变量名,它是一个对象
  19. people.hp -= self.ad #人掉血
  20. print('%s 咬了%s 一口,%s 掉了%s 点血' % (self.name,people.name,people.name,self.ad)) #由于 people 是对象,取 name 就是 people.name
  21. alex = Person('a_sb', '不详', 1, 5)
  22. boss_jin = Person('金老板', '女', 20, 50)
  23. teddy = Dog('笨笨', 'teddy', 50, 10)
  24. teddy.bite(alex) #alex 是一个对象,把对象传进去了
  25. print(alex.hp) #查看 alex 的血
  26. alex.attack(teddy)
  27. print(teddy.hp) #查看 teddy 的血

执行输出:

笨笨咬了 a_sb 一口,a_sb 掉了 10 点血

-9

a_sb 攻击了笨笨,笨笨掉了 5 点血

45

这款游戏,还可以丰富一点,比如玩家有 4 个技能,可以选择

等学了模块,可以做随机事件

比如暴击,中毒,每秒掉血等等…

交互到这里就说完了

方法可以传参,参数可以是对象。

交互: 人和狗的互相残杀

类中的方法的传参与调用

二、类命名空间与对象、实例的命名空间

创建一个类就会创建一个类的名称空间,用来存储类中定义的所有名字,这些名字称为类的属性

而类有两种属性:静态属性和动态属性

  • 静态属性就是直接在类中定义的变量
  • 动态属性就是定义在类中的方法

其中类的数据属性是共享给所有对象的

而类的动态属性是绑定到所有对象的

创建一个对象/实例就会创建一个对象/实例的名称空间,存放对象/实例的名字,称为对象/实例的属性

在 obj.name 会先从 obj 自己的名称空间里找 name,找不到则去类中找,类也找不到就找父类…最后都找不到就抛出异常

上面的例子,看下面 2 句话

  1. alex = Person('a_sb', '不详', 1, 5)
  2. alex.attack(teddy)

有一个问题

alex 是如何找到 Person 类的?

因为是 Person 是实例化了 alex,那么实例化过程中,发生了什么呢?

查看对象的属性

  1. print(alex.__dict__)

{‘hp’: -9, ‘sex’: ‘不详’, ‘name’: ‘a_sb’, ‘ad’: 5}

昨天的内容讲到,调用属性,需要dict中存在,才可以以调用

那这….

在面向过程中,代码执行时从上向下执行

类并不是完全这样执行的,有些步骤跳过了。

直接 Person.role 就可以调用类表里

  1. print(Person.role)

执行输出:person

所以说类里面的 方法,在实例化之前,就已经加载到内存中了

类代码执行,会跳过某些步骤

类是感知不到它有多少个方法

每当实例化之后,对象和类是单向关系,只能是对象取找类

看下面一张图

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597646918914-f4ef8e32-bd3f-4072-9113-49d20e033669.png)

代码执行顺序:

1.加载类名 Person

2.加载类静态属性 role=’person’

3.加载类初始函数init,注意,它不执行代码

4.加载类方法 attack,注意,它不执行代码

5.实例化对象 alex

6.执行类方法 attack

7.打印对象 alex 的属性

执行 1~4 之后,会有一个类命名空间 Person,它存储了 3 个变量

执行第 5 步时,开辟一个实例命名空间 alex,它存储了 4 个变量,里面还有一个隐藏的变量,类对象指针。

它指向了类命名空间 Person 以及自己的实例命名空间

在初始化方法里面的 self,代码实例本身,也就是实例命名空间 alex

执行第 6 步。先找自己的内存空间,再找到类对象指针,再根据类对指针找到类 再通过类找到 attack

第 7 步,打印实例命名空间的属性。

对象的内存空间里 是不是只包含 init 方法里面创建的所有属性?

加一个 age 属性试试

  1. alex = Person('a_sb', '不详', 1, 5)
  2. alex.attack()
  3. alex.age = 18 #此时 init 没有 age
  4. #Person 实例化 alex
  5. print(alex.__dict__)

执行输出:

a_sb 发起了一次攻击

{‘ad’: 5, ‘age’: 18, ‘name’: ‘a_sb’, ‘sex’: ‘不详’, ‘hp’: 1}

输出结果里面有 age 变量了

这就说明,可以从外面向里面添加属性

类方法,也可以给 self 加参数

  1. class Person:
  2. role = 'person' # 静态属性
  3. def __init__(self, name, sex, hp, ad):
  4. self.name = name # 对象属性 属性
  5. self.sex = sex
  6. self.hp = hp
  7. self.ad = ad
  8. def attack(self):
  9. self.hobby = 'girl' #增加属性
  10. print('%s 发起了一次攻击' % self.name)
  11. alex = Person('a_sb', '不详', 1, 5)
  12. alex.attack()
  13. alex.age = 18 #此时 init 没有 age
  14. #Person 实例化 alex
  15. print(alex.__dict__)

执行输出:

a_sb 发起了一次攻击

{‘sex’: ‘不详’, ‘name’: ‘a_sb’, ‘hp’: 1, ‘hobby’: ‘girl’, ‘age’: 18, ‘ad’: 5}

对象内存空间里: 只存储对象的属性,而不存储方法和静态属性

查询对象内存空间,使用dict

为什么不存储类方法呢?假如类有 50 方法,都存一份?太浪费空间了。

为了节省空间,所以不存储类方法

方法和静态属性都存储在类的内存空间中

为了节省内存,让多个对象去恭喜类中的资源

当类空间不够时,会自动扩容

静态属性和方法是共享的

对象属性是独有的

对象不能有自己特有的方法

人狗大战里面再升级一下

人物类型里面有:

输出类:攻击高,血低

坦克类:血低,攻击低

治疗类:攻击低,治疗高

那么每一个类,都是一个单独的类。

假如治疗类里面有医生和护士

医生, 只能做手术

护士,只能包扎

所以医生和护士,必须是不同的类才行。不能用同一个类,因为方法不通。

对象和对象之间,方法不同时,那么它们一定是不同的类

在 init 里面增加和方法名同名的属性

  1. class Person:
  2. role = 'person' # 静态属性
  3. def __init__(self, name, sex, hp, ad):
  4. self.name = name # 对象属性 属性
  5. self.sex = sex
  6. self.hp = hp
  7. self.ad = ad
  8. self.attack = 'haha' #增加和方法名同名的属性
  9. def attack(self):
  10. print('%s 发起了一次攻击' % self.name)
  11. alex = Person('a_sb', '不详', 1, 5)
  12. alex.attack()
  13. print(alex.__dict__)

执行报错:

TypeError: ‘str’ object is not callable

为什么呢?因为 alex 实例命名空间里面找不到 attack,所以它从类命名空间中找,但是此时 attack 变成字符串了,它是被不能执行的

对象查找顺序:先找自己内存空间,再找类的内存空间中的

2 个对象,alex 和 boss_jin。当 alex 修改类静态属性时,boss_jin 调用时,也会更改吗?

  1. class Person:
  2. role = 'person' # 静态属性
  3. def __init__(self, name, sex, hp, ad):
  4. self.name = name # 对象属性 属性
  5. self.sex = sex
  6. self.hp = hp
  7. self.ad = ad
  8. def attack(self):
  9. print('%s 发起了一次攻击' % self.name)
  10. alex = Person('a_sb', '不详', 1, 5)
  11. boss_jin = Person('金老板', '女', 50, 20)
  12. alex.role = 'dog' #修改类静态属性
  13. print(alex.role) #查看类静态属性
  14. print(boss_jin.role)

执行输出:

dog

person

what?为什么没有改变,不是说类命名空间,是共享的吗?

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597646918945-87a0ffba-45e7-409c-b375-82eb8eb9840a.png)

但凡是对象操作类属性,它是没有权利的,它只能存储在自己内存空间里面

比如模拟人生的游戏

里面有一家人,爸爸妈妈出去工作赚钱,女儿和儿子上学,要花钱。

赚的钱,是共享的,大家都可以使用。

  1. class Person:
  2. money = 0
  3. def __init__(self,name):
  4. self.name = name
  5. def work(self):
  6. self.money += 1000
  7. print(self.name,'工作,赚了%s 快钱' %self.money)
  8. father = Person('father')
  9. mother = Person('mother')
  10. father.work()
  11. mother.work()
  12. print(Person.money) #查看钱

执行输出:

father 工作,赚了 1000 快钱

mother 工作,赚了 1000 快钱

0

但是钱没有涨?为什么?

因为对象获取了类属性 money,直接使用 self.money += 1000,就相当于在本实例空间操作了。

但是类命名空间并没有改变,,它还是 0

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597646918911-d9b41f2a-4f55-4b54-96a2-a8776f101f87.png)

怎么解决这个问题呢?

直接用类属性修改

  1. class Person:
  2. money = 0
  3. def __init__(self,name):
  4. self.name = name
  5. def work(self):
  6. Person.money += 1000 #类属性修改
  7. print(self.name,'工作,赚了%s 快钱' %self.money)
  8. father = Person('father')
  9. mother = Person('mother')
  10. father.work()
  11. mother.work()
  12. print(Person.money) #查看钱

执行输出:

father 工作,赚了 1000 快钱

mother 工作,赚了 2000 快钱

2000

对于静态属性的修改,应该使用类名直接修改,就不会出现不一致的情况

因为类属性是共享的。

小作业:

写一个类,完成一个功能,可以统计这个类有几个对象

代码如下:

  1. class Foo:
  2. count = 0
  3. def __init__(self):
  4. Foo.count += 1
  5. f1 = Foo()
  6. f2 = Foo()
  7. f3 = Foo()
  8. print(Foo.count)

执行输出:3

如果对象名字一样呢?结果也是一样的

  1. f1 = Foo()
  2. f1 = Foo()
  3. f1 = Foo()
  4. print(Foo.count)

执行输出:3

此时此刻,只有一个 f1,之前的被覆盖了。

实例化一次,就会有一个对象。

小测试,看如下代码,最终 money 会输出多少?2000 还是 0?

  1. class Person:
  2. money = [0]
  3. def __init__(self,name):
  4. self.name = name
  5. def work(self):
  6. self.money[0] += 1000
  7. print(self.name,'工作,赚了 1000 快钱')
  8. father = Person('father')
  9. mother = Person('mother')
  10. father.work()
  11. mother.work()
  12. print(Person.money) #查看钱

执行输出:

father 工作,赚了 1000 快钱

mother 工作,赚了 1000 快钱

[2000]

为什么是 2000?不是说但凡是对象操作类属性,它是没有权利的,它只能存储在自己内存空间里面

what?

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597646918927-fc61f2c6-ccbf-48de-a004-1b0c58ae6803.png)

第一次类初始化是,money[0] =0。

当 father 实例化之后,money[0]的指针就失效了,变成了 1000

当 mother 实例化之后,money[0]的指针就失效了,变成了 2000

最终结果为 2000

因为 list 是可变类型,它有自己独立的内存空间。当实例改变时,它就改变了。所以最后调用时,money 的值为 2000

再来一个小测试,还是上面的例子,修改了一下代码

那么 money 是 0 还是 2000?

  1. class Person:
  2. money = [0]
  3. def __init__(self,name):
  4. self.name = name
  5. def work(self):
  6. self.money = [Person.money[0] + 1000]
  7. print(self.name,'工作,赚了 1000 快钱')
  8. father = Person('father')
  9. mother = Person('mother')
  10. father.work()
  11. mother.work()
  12. print(Person.money) #查看钱

执行输出:

father 工作,赚了 1000 快钱

mother 工作,赚了 1000 快钱

[0]

what?怎么是 0 了?它不是列表吗?它不是改了吗?

城市套路深我想回农村…

  1. ![](https://cdn.nlark.com/yuque/0/2020/png/1484428/1597646918974-385dab69-3ba7-4a32-9a70-620cc1099079.png)

请注意表达式 self.money = [Person.money[0] + 1000]

等式先计算右边的,Person.money[0]之间用类名.属性名调用,那么应该是[0 + 1000],最终结果为[1000]。

将结果赋值给 self.money,它是实例本身调用 money,所以修改的值[1000],存放在本实例空间。

类属性,并没有被改变,所以最终调用类属性时,输出结果为[0]

有一个终极方法,一招制敌

不管时类里面还是类外面,统一使用如下方法修改

类名.静态变量名

总结:

实例查找变量的顺序:

先找自己的内存空间->再找到类对象指针->再根据类对象指针找到类->再通过类找

对象的内存空间里: 只存储对象的属性,而不存储方法和静态属性

方法和静态属性都存储在类的内存空间中

为了节省内存,让多个对象去共享类中的资源

对象属性是独有的,静态属性和方法是共享的

对象使用名字:先找自己内存空间中的,再找类的内存空间中的

类名.静态变量名 :对于静态属性的修改,应该使用类名直接修改

三、面向对象的实例

计算圆的周长 2rπ (r表示半径)

计算圆的面积 r²π

现在有 5 个圆,半径分别是 1,3,5,7,9

请使用面向对象编写出来

提示:π需要用python模块math里面的pi方法

math—-数学函数

作用:提供函数完成特殊的数学运算。

pi 表示圆周率

  1. from math import pi
  2. print(pi)

执行输出:

3.141592653589793

代码如下:

  1. from math import pi
  2. class Circle:
  3. def __init__(self, r):
  4. self.r = r
  5. def cal_area(self):
  6. '''
  7. 计算圆面积的方法
  8. :return:返回值是 float 数据类型的面积
  9. '''
  10. return pi * self.r ** 2
  11. def cal_perimeter(self):
  12. '''
  13. 计算圆周长的方法
  14. :return:返回值是 float 数据类型的周长
  15. '''
  16. return pi * self.r * 2
  17. for i in range(1, 10, 2): #隔一个取一个,得到 1,3,5,7,9
  18. c1 = Circle(i) #实例化
  19. print(c1.cal_area())
  20. print(c1.cal_perimeter())

执行输出:

3.141592653589793

6.283185307179586

28.274333882308138

18.84955592153876

78.53981633974483

31.41592653589793

153.93804002589985

43.982297150257104

254.46900494077323

56.548667764616276

到这里

面向对象的基础就结束了

作业:

1.计算圆环的面积和周长

2.默写交互

3.整理校园管里系统的思路

把需求中找到所有能找到的类,属性定义出来

比如,学生类,它的属性有姓名,性别,班级,课程

方法,查看自己所在的班级,查看所学课

答案:

1.圆环面积和周长公式

大圆半径 R,小圆半径 r:

圆环的周长=大圆的周长+小圆的周长

=2Rπ+2rπ

=2π(R+r);

[圆环的面积=大圆的面积-小圆的面积

=R²π-r²π

=(R²-r²)π,

代码如下:

  1. from math import pi
  2. class Ring:
  3. def __init__(self, R, r):
  4. self.r = r
  5. self.R = R
  6. def cal_area(self):
  7. '''
  8. 计算圆环面积的方法
  9. :return:返回值是 float 数据类型的面积
  10. '''
  11. return 2 * pi * (self.R + self.r)
  12. def cal_perimeter(self):
  13. '''
  14. 计算圆环周长的方法
  15. :return:返回值是 float 数据类型的周长
  16. '''
  17. return (self.R ** 2 - self.r ** 2) * pi
  18. c1 = Ring(10, 6) # 实例化
  19. print(c1.cal_area()) #面积
  20. print(c1.cal_perimeter()) #周长

执行输出:

100.53096491487338

201.06192982974676

2.默认交互

  1. class Person:
  2. role = 'person' # 静态属性
  3. def __init__(self, name, sex, hp, ad):
  4. self.name = name # 对象属性 属性
  5. self.sex = sex
  6. self.hp = hp # 血量
  7. self.ad = ad # 攻击力
  8. def attack(self, d):
  9. d.hp -= self.ad
  10. print('%s 攻击了%s,%s 掉了%s 点血' % (self.name, d.name, d.name, self.ad))
  11. class Dog:
  12. role = 'person' # 静态属性
  13. def __init__(self, name, kind, hp, ad):
  14. self.name = name # 对象属性 属性
  15. self.kind = kind
  16. self.hp = hp
  17. self.ad = ad
  18. def bite(self, p):
  19. p.hp -= self.ad # 人掉血
  20. print('%s 咬了%s 一口,%s 掉了%s 点血' % (self.name, p.name, p.name, self.ad))
  21. alex = Person('a_sb', '不详', 1, 5)
  22. boss_jin = Person('金老板', '女', 20, 50)
  23. teddy = Dog('笨笨', 'teddy', 50, 10)
  24. teddy.bite(alex)
  25. print(alex.hp)
  26. boss_jin.attack(teddy)
  27. print(teddy.hp)

3.题目需求链接

https://www.cnblogs.com/Eva-J/articles/7747946.html