类空间问题
从空间角度研究类
class A: address = '美丽富饶的沙河' def __init__(self, name): self.name = name def func(self): if self.name == 'dsb': self.skins = '吉利服' def func1(self): print(self.__dict__) A.aaa = '易水寒' obj = A('dsb') # 类外面可以给对象封装属性 respons = input('太白帅不帅!') if respons == '帅': obj.weapon = 'AWM' print(obj.__dict__) # 类内部封装属性 obj.func() print(obj.__dict__) # 使用类名也可以添加属性 A.teamp = '39.5' # 用类名.添加 print(A.__dict__) A.func1(obj) # 利用类里的方法添加 print(A.__dict__) # 属性查找顺序 class Person: mind = '有思想' language = '会使用语言' def __init__(self, name, age): self.name = name self.age = age def work(self): print('人类一般都需要工作') p1 = Person('dsb', 21) print(p1.mind) # 对象空间没有就去类空间里找 p1.mind = '无脑' # 创建一个mind属性 print(p1.mind) # 是在对象空间里创建 print(Person.mind) # 类空间里的mind不变 # 对象如果查询一个属性: 对象空间 ----> 类空间 ----> 父类空间 ---> # 类查询一个属性: 类空间 ----> 父类空间 ----> # 单向不可逆 # 对象与对象之间原则上互相独立(除去组合这种特殊的关系之外).
类与类之间的关系
依赖关系: 将一个类名或者类的对象传给另一个类的方法中.
# 举例--大象进冰箱 class Elephant: def __init__(self,name): self.name = name def open(self,ref1): print(f"大象{self.name}默念三声:芝麻开门") ref1.open_door() def close(self,ref1): print(f"大象{self.name}默念三声:芝麻关门") ref1.close_door() class Refrigerate: def __init__(self,name): self.name = name def open_door(self): print(f"{self.name}冰箱的门被打开了...") def close_door(self): print(f"{self.name}冰箱的门被关上了...") ele = Elephant('奇奇') ref = Refrigerate('海尔') ele.open(ref) ele.close(ref) '''结果: 大象奇奇默念三声:芝麻开门 海尔冰箱的门被打开了... 大象奇奇默念三声:芝麻关门 海尔冰箱的门被关上了... ''' # 依赖关系: 将一个类的类名或者对象传给另一个类的方法中.
组合关系(关联组合聚合)
# 组合:(聚合,组合,关联) # 组合: 将一个类的对象封装成另一个类的对象的属性. class Boy: def __init__(self,name): self.name = name def meet(self,girl_friend=None): self.girl_friend = girl_friend def have_diner(self): if self.girl_friend: print(f"{self.name}请{self.girl_friend.name}一起吃六块钱的麻辣烫") self.girl_friend.shopping(self) else: print('单身狗,吃什么吃') class Girl: def __init__(self,name,age): self.name = name self.age =age def shopping(self,a): print(f"{self.name},{a.name}一起去购物") wu = Boy('吴超') flower = Girl('如花',48) wu.meet(flower) wu.have_diner() # 上面例题的难点: # 一个类的方法只能有此类的对象去调用. # 一个类的方法的第一个self只接受此类的对象. # 模拟英雄联盟写一个游戏人物的类(升级题). # 要求: # 1. 创建一个 Game_role的类. # 2. 构造方法中给对象封装name,ad(攻击力),hp(血量).三个属性. # 3. 创建一个attack方法,此方法是实例化两个对象,互相攻击的功能: # 例: 实例化一个对象 盖伦,ad为10, hp为100 # 实例化另个一个对象 剑豪 ad为20, hp为80 # 盖伦通过attack方法攻击剑豪,此方法要完成 '谁攻击谁,谁掉了多少血, 还剩多少血'的提示功能. class GameRole: def __init__(self, name, ad, hp): self.name = name self.ad = ad self.hp = hp def attack(self,p1): p1.hp = p1.hp - self.ad print(f'{self.name}攻击{p1.name},{p1.name}掉了{self.ad}血,还剩{p1.hp}血') def equit_weapon(self,wea): self.weapon = wea # 组合: 给gailun这个对象封装了一个为weapon属性,属性值为wea == great_sword对象 class Weapon: def __init__(self, name, ad): self.name = name self.ad = ad def weapon_attack(self, p1, p2): # print(self) p2.hp = p2.hp - self.ad print(f'{p1.name}利用{self.name}给了{p2.name}一下,{p2.name}还剩{p2.hp}血') gailun = GameRole('草丛伦', 10, 100) jianhao = GameRole('风男', 20, 80) gailun.attack(jianhao) gailun.attack(jianhao) gailun.attack(jianhao) '''结果: 草丛伦攻击风男,风男掉了10血,还剩70血 草丛伦攻击风男,风男掉了10血,还剩60血 草丛伦攻击风男,风男掉了10血,还剩50血 ''' # 利用武器攻击 gailun = GameRole('盖伦', 10, 100) zhaoxin = GameRole('赵信', 20, 90) great_sword = Weapon('大宝剑', 30) spear = Weapon('红缨枪', 40) great_sword.weapon_attack(gailun, zhaoxin) ''' 结果: 盖伦利用大宝剑给了赵信一下,赵信还剩60血 ''' # 上面需要解决的问题: 发起武器攻击的发起者应该是人类,而不是great_sword武器对象. # 但是weapon_attack只能有Weapon类的对象去调用. gailun = GameRole('盖伦', 10, 100) zhaoxin = GameRole('赵信', 20, 90) great_sword = Weapon('大保健', 30) gailun.equit_weapon(great_sword) # 依赖关系 gailun.weapon.weapon_attack(gailun, zhaoxin) ''' 结果: 盖伦利用大宝剑给了赵信一下,赵信还剩60血 '''
继承关系
什么是继承?
专业角度: B 继承 A类, B就叫做A的子类,派生类, A叫做B的父类,基类,超类. B类以及B类的对象使用A类的所有的属性以及方法.
字面意思: 继承就是继承父母所有的资产.
继承分为单继承,多继承.
继承的优点.
- 节省代码.
- 增强的耦合性.
- 代码规范化.
单继承.
# 普通面向对象编程 class Person: def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex class Cat: def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex class Dog: def __init__(self,name,sex,age): self.name = name self.age = age self.sex = sex # 都有name,sex,age 代码重复 # 利用继承写 class Animal: def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex class Person(Animal): pass class Dog(Animal): pass class Cat(Animal): pass # 下面三个类都可以调用父类的属性,降低了代码重复性 # 如何调用类中的属性与方法 class Animal(object): live = '有生命的' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def eat(self): print(f'self----> {self}') print('动物都需要进食') class Person(Animal): pass # 1.从类名执行父类的属性. print(Person.__dict__) print(Person.live) Person.eat() # 2. 从对象执行父类的一切. # 实例化对象一定一定会执行三件事. 一定会执行__init__ 子类没有就去父类中找 p1 = Person('dsb', 21, 'laddy_boy') print(p1.live) p1.eat() Person.live = 'xxx' print(Person.live) # 注意: 子类以及子类对象只能调用父类的属性以及方法,不能操作(增删改). class Animal(object): live = '有生命的' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def eat(self): print(f'self----> {self}') print('动物都需要进食') class Person(Animal): def eat(self): print('人类需要进食') p1 = Person('dsb', 21, 'laddy_boy') # 子类将父类的方法覆盖了,(重写父类的方法) p1.eat = '李业' p1.eat()# 不能执行,以为eat已被修改为'李业' # 对象查找顺序先从对象空间找名字, 子类找名字, 父类找名字. # 如何既要执行父类方法又要执行子类方法 # 两种解决方式: class Animal: live = '有生命的' def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex def eat(self): print(f'self----> {self}') print('动物都需要进食') class Person(Animal): def __init__(self,name, age, sex, hobby): # 方法一: Animal.__init__(self, name, age, sex) # 类名直接调用 # 方法二: # super(Person, self).__init__(name, age, sex) # super().__init__(name, age, sex) # 利用super self.hobby = hobby def eat(self): print('人类需要进食') super().eat() p1 = Person('怼怼哥', 23, '不详','吹牛逼') p1.eat() # super(). 主动调用父类中的函数
多继承.
class God: def __init__(self,name): self.name = name def fly(self): print('会飞') def climb(self): print('神仙累了也需要爬树') class Monkey: def __init__(self,sex): self.sex = sex def climb(self): print('爬树') class MonkeySun(God, Monkey): pass # 多继承的难点就是继承顺序的问题 sun = MonkeySun() sun.climb() ''' 这里需要补充一下python中类的种类(继承需要): 在python2x版本中存在两种类.: ⼀个叫经典类. 在python2.2之前. ⼀直使⽤的是经典类. 经典类在基类的根如果什么都不写. ⼀个叫新式类. 在python2.2之后出现了新式类. 新式类的特点是基类的根是object类。 python3x版本中只有一种类: python3中使⽤的都是新式类. 如果基类谁都不继承. 那这个类会默认继承 object ''' # 经典类的继承顺序: 在经典类中采⽤的是深度优先,遍历⽅案. 什么是深度优先. 就是⼀条路走到头. 然后再回来. 继续找下⼀个. # 新式类的继承顺序: mro序列 # mro算法 面试中有可能会遇到 """ MRO是一个有序列表L,在类被创建时就计算出来。 通用计算公式为: mro(Child(Base1,Base2)) = [ Child ] + merge( mro(Base1), mro(Base2), [ Base1, Base2] ) (其中Child继承自Base1, Base2) 如果继承至一个基类:class B(A) 这时B的mro序列为 mro( B ) = mro( B(A) ) = [B] + merge( mro(A) + [A] ) = [B] + merge( [A] + [A] ) = [B,A] 如果继承至多个基类:class B(A1, A2, A3 …) 这时B的mro序列 mro(B) = mro( B(A1, A2, A3 …) ) = [B] + merge( mro(A1), mro(A2), mro(A3) ..., [A1, A2, A3] ) = ... 计算结果为列表,列表中至少有一个元素即类自己,如上述示例[A1,A2,A3]。merge操作是C3算法的核心。 表头和表尾 表头:列表的第一个元素 表尾:列表中表头以外的元素集合(可以为空) 示例: 列表:[A, B, C]--->表头是A,表尾是B和C 列表之间的+操作 +操作:[A] + [B] = [A, B] merge操作示例: 如计算merge( [E,O], [C,E,F,O], [C] ) 有三个列表 : ① ② ③ 1 merge不为空,取出第一个列表列表①的表头E,进行判断 各个列表的表尾分别是[O], [E,F,O],E在这些表尾的集合中,因而跳过当前当前列表 2 取出列表②的表头C,进行判断 C不在各个列表的集合中,因而将C拿出到merge外,并从所有表头删除 merge( [E,O], [C,E,F,O], [C]) = [C] + merge( [E,O], [E,F,O] ) 3 进行下一次新的merge操作 ...... """ # 计算示例 class O: pass class D(O): pass class E(O): pass class F(O): pass class B(D,E): pass class C(E,F): pass class A(B,C): pass a = A() a.func() ''' mro(A) = mro(A(B,C)) = [A] + merge(mro(B), mro(C), [B,C]) mro(B) = mro(B(D,E)) = [B] + merge(mro(D), mro(E), [D,E]) = [B] + merge([D,O], [E,O], [D,E]) = [B,D] + merge([O], [E,O], [E]) = [B,D,E,O] mro(C) = mro(C(E,F)) = [C] + merge(mro(E), mro(F),[E,F]) = [C] + merge([E,O],[F,O],[E,F]) = [C,E] + merge([O],[F,O],[F]) = [C,E,F,O] mro(A) = mro(A(B,C)) = [A] + merge([B,D,E,O], [C,E,F,O], [B,C]) = [A,B] + merge([D,E,O], [C,E,F,O], [C]) = [A,B,D] + merge([E,O], [C,E,F,O], [C]) = [A,B,D,C] + merge([E,O], [E,F,O]) = [A,B,D,C,E] + merge([O], [F,O]) = [A,B,D,C,E,F,O] ''' # 工作中用mro()方法研究新式类的继承顺序 print(A.mro())