一、对象之间的交互

现在我们已经有一个人类了,通过给人类一些具体的属性我们就可以拿到一个实实在在的人。
现在我们要再创建一个狗类,狗就不能打人了,只能咬人,所以我们给狗一个bite方法。
有了狗类,我们还要实例化一只实实在在的狗出来。
然后人和狗就可以打架了。现在我们就来让他们打一架吧!

创建一个狗类

class Person:
role = 'person' # 静态属性 def __init__(self, name, sex, hp, ad):
self.name = name # 对象属性 属性
self.sex = sex
self.hp = hp
self.ad = ad def attack(self):
print('%s发起了一次攻击' % self.name) class Dog:
role = 'person' # 静态属性 def __init__(self, name, kind, hp, ad):
self.name = name # 对象属性 属性
self.kind = kind
self.hp = hp
self.ad = ad def bite(self):
print('%s咬了人一口' % self.name) alex = Person('a_sb', '不详', 1, 5)
boss_jin = Person('金老板', '女', 20, 50) teddy = Dog('笨笨', 'teddy', 50, 10) alex.attack() # 相当于执行Person.attack(alex)
boss_jin.attack() # 相当于执行Person.attack(boss_jin) teddy.bite()

执行输出:

a_sb发起了一次攻击
金老板发起了一次攻击
笨笨咬了人一口

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

交互 teddy打alex一下

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

执行输出:

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

再完善人攻击狗的技能

class Person:
role = 'person' # 静态属性 def __init__(self, name, sex, hp, ad):
self.name = name # 对象属性 属性
self.sex = sex
self.hp = hp
self.ad = ad def attack(self,dog):
dog.hp -= self.ad
print('%s攻击了%s,%s掉了%s点血' % (self.name,dog.name,dog.name,self.ad)) class Dog:
role = 'person' # 静态属性 def __init__(self, name, kind, hp, ad):
self.name = name # 对象属性 属性
self.kind = kind
self.hp = hp
self.ad = ad def bite(self,people): #people是变量名,它是一个对象
people.hp -= self.ad #人掉血
print('%s咬了%s一口,%s掉了%s点血' % (self.name,people.name,people.name,self.ad)) #由于people是对象,取name就是people.name alex = Person('a_sb', '不详', 1, 5)
boss_jin = Person('金老板', '女', 20, 50) teddy = Dog('笨笨', 'teddy', 50, 10) teddy.bite(alex) #alex是一个对象,把对象传进去了
print(alex.hp) #查看alex的血 alex.attack(teddy)
print(teddy.hp) #查看teddy的血

执行输出:

笨笨咬了a_sb一口,a_sb掉了10点血
-9
a_sb攻击了笨笨,笨笨掉了5点血
45

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

等学了模块,可以做随机事件
比如暴击,中毒,每秒掉血等等...

交互到这里就说完了
方法可以传参,参数可以是对象。

交互: 人和狗的互相残杀
类中的方法的传参与调用

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

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

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

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

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

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

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

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

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

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

有一个问题
alex是如何找到Person类的?

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

查看对象的属性

print(alex.__dict__)

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

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

那这....

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

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

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

print(Person.role)

执行输出:person

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

类代码执行,会跳过某些步骤
类是感知不到它有多少个方法
每当实例化之后,对象和类是单向关系,只能是对象取找类

看下面一张图

代码执行顺序:

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属性试试

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

执行输出:

a_sb发起了一次攻击
{'ad': 5, 'age': 18, 'name': 'a_sb', 'sex': '不详', 'hp': 1}

输出结果里面有age变量了
这就说明,可以从外面向里面添加属性

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

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

执行输出:

a_sb发起了一次攻击
{'sex': '不详', 'name': 'a_sb', 'hp': 1, 'hobby': 'girl', 'age': 18, 'ad': 5}

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

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

方法和静态属性都存储在类的内存空间中
为了节省内存,让多个对象去恭喜类中的资源
当类空间不够时,会自动扩容

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

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

人狗大战里面再升级一下
人物类型里面有:
输出类:攻击高,血低
坦克类:血低,攻击低
治疗类:攻击低,治疗高

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

假如治疗类里面有医生和护士
医生, 只能做手术
护士,只能包扎

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

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

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

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

执行报错:

TypeError: 'str' object is not callable

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

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

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

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

执行输出:

dog
person

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

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

比如模拟人生的游戏

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

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

class Person:
money = 0
def __init__(self,name):
self.name = name def work(self):
self.money += 1000
print(self.name,'工作,赚了%s快钱' %self.money) father = Person('father')
mother = Person('mother')
father.work()
mother.work()
print(Person.money) #查看钱

执行输出:

father 工作,赚了1000快钱
mother 工作,赚了1000快钱
0

但是钱没有涨?为什么?

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

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

怎么解决这个问题呢?

直接用类属性修改

class Person:
money = 0
def __init__(self,name):
self.name = name def work(self):
Person.money += 1000 #类属性修改
print(self.name,'工作,赚了%s快钱' %self.money) father = Person('father')
mother = Person('mother')
father.work()
mother.work()
print(Person.money) #查看钱

执行输出: 

father 工作,赚了1000快钱
mother 工作,赚了2000快钱
2000

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

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

代码如下:

class Foo:
count = 0
def __init__(self):
Foo.count += 1 f1 = Foo()
f2 = Foo()
f3 = Foo()
print(Foo.count)

执行输出: 3

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

f1 = Foo()
f1 = Foo()
f1 = Foo()
print(Foo.count)

执行输出: 3

此时此刻,只有一个f1,之前的被覆盖了。
实例化一次,就会有一个对象。

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

class Person:
money = [0]
def __init__(self,name):
self.name = name def work(self):
self.money[0] += 1000
print(self.name,'工作,赚了1000快钱') father = Person('father')
mother = Person('mother')
father.work()
mother.work()
print(Person.money) #查看钱

执行输出:

father 工作,赚了1000快钱
mother 工作,赚了1000快钱
[2000]

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

what?

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

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

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

最终结果为2000

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

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

那么money是0还是2000?

class Person:
money = [0]
def __init__(self,name):
self.name = name def work(self):
self.money = [Person.money[0] + 1000]
print(self.name,'工作,赚了1000快钱') father = Person('father')
mother = Person('mother')
father.work()
mother.work()
print(Person.money) #查看钱

执行输出:

father 工作,赚了1000快钱
mother 工作,赚了1000快钱
[0]

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

城市套路深我想回农村...

请注意表达式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表示圆周率

from math import pi
print(pi)

执行输出:

3.141592653589793

代码如下:

from math import pi

class Circle:
def __init__(self, r):
self.r = r def cal_area(self):
'''
计算圆面积的方法
:return:返回值是float数据类型的面积
'''
return pi * self.r ** 2 def cal_perimeter(self):
'''
计算圆周长的方法
:return:返回值是float数据类型的周长
'''
return pi * self.r * 2 for i in range(1, 10, 2): #隔一个取一个,得到1,3,5,7,9
c1 = Circle(i) #实例化
print(c1.cal_area())
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²)π,

代码如下:

from math import pi

class Ring:
def __init__(self, R, r):
self.r = r
self.R = R def cal_area(self):
'''
计算圆环面积的方法
:return:返回值是float数据类型的面积
'''
return 2 * pi * (self.R + self.r) def cal_perimeter(self):
'''
计算圆环周长的方法
:return:返回值是float数据类型的周长
'''
return (self.R ** 2 - self.r ** 2) * pi c1 = Ring(10, 6) # 实例化
print(c1.cal_area()) #面积
print(c1.cal_perimeter()) #周长

执行输出:

100.53096491487338
201.06192982974676

2.默认交互

class Person:
role = 'person' # 静态属性 def __init__(self, name, sex, hp, ad):
self.name = name # 对象属性 属性
self.sex = sex
self.hp = hp # 血量
self.ad = ad # 攻击力 def attack(self, d):
d.hp -= self.ad
print('%s攻击了%s,%s掉了%s点血' % (self.name, d.name, d.name, self.ad)) class Dog:
role = 'person' # 静态属性 def __init__(self, name, kind, hp, ad):
self.name = name # 对象属性 属性
self.kind = kind
self.hp = hp
self.ad = ad def bite(self, p):
p.hp -= self.ad # 人掉血
print('%s咬了%s一口,%s掉了%s点血' % (self.name, p.name, p.name, self.ad)) alex = Person('a_sb', '不详', 1, 5)
boss_jin = Person('金老板', '女', 20, 50)
teddy = Dog('笨笨', 'teddy', 50, 10) teddy.bite(alex)
print(alex.hp) boss_jin.attack(teddy)
print(teddy.hp)

3.题目需求链接

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

python 全栈开发,Day18(对象之间的交互,类命名空间与对象,实例的命名空间,类的组合用法)的更多相关文章

  1. python 全栈开发,Day124(MongoDB初识,增删改查操作,数据类型,$关键字以及$修改器,"$"的奇妙用法,Array Object 的特殊操作,选取跳过排序,客户端操作)

    一.MongoDB初识 什么是MongoDB MongoDB 是一个基于分布式文件存储的数据库.由 C++ 语言编写.旨在为 WEB 应用提供可扩展的高性能数据存储解决方案. MongoDB 是一个介 ...

  2. python 全栈开发,Day47(行级块级标签,高级选择器,属性选择器,伪类选择器,伪元素选择器,css的继承性和层叠性,层叠性权重相同处理,盒模型,padding,border,margin)

    一.HTML中的行级标签和块级标签 块级标签 常见的块级标签:div,p,h1-h6,ul,li,dl,dt,dd 1.独占一行,不和其他元素待在同一行2.能设置宽高3.如果不设置宽高,默认为body ...

  3. Python全栈开发【面向对象进阶】

    Python全栈开发[面向对象进阶] 本节内容: isinstance(obj,cls)和issubclass(sub,super) 反射 __setattr__,__delattr__,__geta ...

  4. Python全栈开发【面向对象】

    Python全栈开发[面向对象] 本节内容: 三大编程范式 面向对象设计与面向对象编程 类和对象 静态属性.类方法.静态方法 类组合 继承 多态 封装 三大编程范式 三大编程范式: 1.面向过程编程 ...

  5. Python全栈开发【模块】

    Python全栈开发[模块] 本节内容: 模块介绍 time random os sys json & picle shelve XML hashlib ConfigParser loggin ...

  6. Python全栈开发【基础三】

    Python全栈开发[基础三]  本节内容: 函数(全局与局部变量) 递归 内置函数 函数 一.定义和使用 函数最重要的是减少代码的重用性和增强代码可读性 def 函数名(参数): ... 函数体 . ...

  7. python 全栈开发之路 day1

    python 全栈开发之路 day1   本节内容 计算机发展介绍 计算机硬件组成 计算机基本原理 计算机 计算机(computer)俗称电脑,是一种用于高速计算的电子计算机器,可以进行数值计算,又可 ...

  8. python全栈开发中级班全程笔记(第二模块、第四章)(常用模块导入)

    python全栈开发笔记第二模块 第四章 :常用模块(第二部分)     一.os 模块的 详解 1.os.getcwd()    :得到当前工作目录,即当前python解释器所在目录路径 impor ...

  9. python 全栈开发,Day99(作业讲解,DRF版本,DRF分页,DRF序列化进阶)

    昨日内容回顾 1. 为什么要做前后端分离? - 前后端交给不同的人来编写,职责划分明确. - API (IOS,安卓,PC,微信小程序...) - vue.js等框架编写前端时,会比之前写jQuery ...

  10. python全栈开发之正则表达式和python的re模块

    正则表达式和python的re模块 python全栈开发,正则表达式,re模块 一 正则表达式 正则表达式(Regular Expression)是一种文本模式,包括普通字符(例如,a 到 z 之间的 ...

随机推荐

  1. bzoj千题计划301:bzoj4259: 残缺的字符串

    https://www.lydsy.com/JudgeOnline/problem.php?id=4259 令通配符=0 f[i+m-1]=Σ (a[i+j]-b[m-1-j])^2 * a[i+j] ...

  2. Jquery中click事件重复执行的问题

    平常没注意事件绑定问题,在此注意一下: function testClick(obj){ $("select").off().on("click", funct ...

  3. 搭建Linux下Android程序开发环境

    从AndroidStudio中文社区下载SDK压缩包,http://dl.google.com/android/android-sdk_r24.2-linux.tgz. 解压到某个目录,比如我的~/p ...

  4. 集大软件工程15级个人作业Week2

    集大软件工程15级个人作业Week2 快速通读教材<构建之法>,并参照提问模板,提出5个问题. 在每个问题后面,请说明哪一章节的什么内容引起了你的提问,提供一些上下文 列出一些事例或资料, ...

  5. 20155332 2006-2007-2 《Java程序设计》第4周学习总结

    20155332 2006-2007-2 <Java程序设计>第4周学习总结 教材学习内容总结 理解封装.继承.多态的关系 理解抽象类与接口的区别 掌握S.O.L.I.D原则 了解模式和设 ...

  6. js正则匹配table,img及去除各种标签问题

    //获取公示栏内容 s = "$row.detail$"; mainContent =s; //如果有多个table使用下面注释的正则只会匹配成一个table //var tabR ...

  7. Postfix邮件服务 - DNS配置

    DNS 域名系统服务器 IP 与 域名之间解析 :提供分层的域名解析 服务:bing 伯克利加州大学 应用最广的域名服务系统: bind 主要分为 主配置文件 和 域数据记录文件 yum 安装: yu ...

  8. luogu P1268 树的重量

    一开始把这题想复杂了,,, 这里记\(di[i][j]\)表示\(i\)到\(j\)的距离 首先如果\(n=2\),答案显然为\(di[1][2]\) 如果\(n=3\) 懒得画图了盗图过来 那么3号 ...

  9. Java基础中的一些概念理解

    同步 和 异步区别 同步和异步通常用来形容一次方法的调用. 同步方法调用一旦开始,调用者必须等到方法调用返回后,才能继续后续的行为.而异步方法调用更像一个消息传递,一旦开始,方法调用就会立即返回,调用 ...

  10. Android中实现延时执行操作的几种方法

    1.使用线程的休眠实现延时操作 new Thread() { @Override public void run() { super.run(); Thread.sleep(3000);//休眠3秒 ...