zoukankan      html  css  js  c++  java
  • python 全栈开发,Day18(对象之间的交互,类命名空间与对象,实例的命名空间,类的组合用法)

    一、对象之间的交互

    现在我们已经有一个人类了,通过给人类一些具体的属性我们就可以拿到一个实实在在的人。
    现在我们要再创建一个狗类,狗就不能打人了,只能咬人,所以我们给狗一个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

  • 相关阅读:
    类似吸顶功能解决ios不能实时监听onscroll的触发问题
    js 移动端识别手机号码
    H5输入框实时记录文字个数
    C语言指针和数组
    PHP变量
    PHP 的引用计数基础知识
    PHP提高效率的经验
    JS内置Function对象详解
    Javascript小细节总结
    浅析C++中内存分配的方式
  • 原文地址:https://www.cnblogs.com/tu240302975/p/12658626.html
Copyright © 2011-2022 走看看