一、面向对象编程
# 1.什么是面向对象 面向过程与面向对象 面向过程编程:解决问题从过程出发,解决问题步骤化 面向对象编程:解决问题从对象出发,解决问题找对象 对象与类 类:对象的类型 => 数字 具有相同特征与行为集合的抽象 对象:类的具体表现 => 数字10 类的实例化,就是具有特征与行为实际存在的个体(每一个对象都是唯一的) # 2.为什么要面向对象编程 面向过程:开发成本高,解决问题局限性小 面向对象:开发成本低,解决问题局限于对象 问题:'abc' => {'a', 'b', 'c'} 面向过程: 自己写 面向对象:str => list => set #(引用模块) 开发:优选面向对象(找解决问题的对象), 再考虑找多个对象(面向对象与面向过程的结合), 最后自己去封装一个可以解决问题的对象(对外是面向对象的体现,内部解决问题的核心是面向过程)
# 例如:
s = 'abc' # => ['a', 'b', 'c'] # 解决方案 # re.findall() list初始化方法 # for循环append # 面向过程:解决问题步骤化 res = [] for c in s: res.append(c) print(res) # 面向对象:解决问题找对象 # -- 对象如何解决问题:对象.解决问题的方法() # 找re对象 import re res = re.findall(r'[a-z]', s) print(res) # 找list对象 res = list(s) print(res) # 模块 md.py def print_num(a): print(a) from md import print_num as pn pn(100) pn(200)
二、类的声明语法
class 类名: # 在该缩进下(在类下)定义多个函数,类名就可以整体管理所有的函数,通过点语法来调用具体的函数 def fn1(): print('fn1 run') def fn2(): print('fn2 run') def fn3(): print('fn3 run') # 类名的命名规范:采用大驼峰
三、点语法与名称空间
def fn(): pass class Fn(): def a_fn(): pass pass import md print(fn, fn.__dict__) #{} print(Fn, Fn.__dict__) #{'__module__': '__main__', 'a_fn': print(md, md.__dict__) #, '__dict__': , '__weakref__': , '__doc__': None} {'__name__': 'md', '__doc__': None, '__package__': '', '__loader__': <_frozen_importlib_external.SourceFileLoader object at 0x0000000001EA6E10>, '__spec__': ModuleSpec(name='md', loader=<_frozen_importlib_external.SourceFileLoader object at 0x0000000001EA6E10>, origin='F:\\安装包\\JetBrains\\JetBrains\\Projects\\09\\代码\\part2\\md.py'), '__file__': 'F:\\安装包\\JetBrains\\JetBrains\\Projects\\09\\代码\\part2\\md.py', '__cached__': 'F:\\安装包\\JetBrains\\JetBrains\\Projects\\09\\代码\\part2\\__pycache__\\md.cpython-36.pyc', '__builtins__': {'__name__': 'builtins', '__doc__': "Built-in functions, exceptions, and other objects.\n\nNoteworthy: None is the `nil' object; Ellipsis represents `...' in slices.", '__package__': '', '__loader__': # All Rights Reserved. ... # 访问名字的底层 print(Fn.__dict__['__module__']) # __main__ # 访问名字的语法优化 print(Fn.__module__) # __main__ # 总结:对象.名字 本质 对象.__dict__['名字'] # 赋值 fn.name = '我写的函数' print(fn.__dict__) # {'name': '我写的函数'} print(fn.__dict__['name']) # 我写的函数 print(fn.name) # 我写的函数 # 了解:对象的名称空间,与对象内部的名称空间不是同一个 print('========================================') def func(): a = 10 b = 20 print(locals()) # {'b': 20, 'a': 10} func() print(func.__dict__) # {} # 记住: # 对象.名字 = 值 是为该对象添加一个名称空间的名字, # 也只能通过 对象.名字 来使用 func.name = 'func function' print(func.name) # func function, '__spec__': ModuleSpec(name='builtins', loader= ), '__build_class__': , '__import__': , 'abs': , 'all': , 'any': , 'ascii': , 'bin': , 'callable': , 'chr': , 'compile': , 'delattr': , 'dir': , 'divmod': , 'eval': , 'exec': , 'format': , 'getattr': , 'globals': , 'hasattr': , 'hash': , 'hex': , 'id': , 'input': , 'isinstance': , 'issubclass': , 'iter': , 'len': , 'locals': , 'max': , 'min': , 'next': , 'oct': , 'ord': , 'pow': , 'print': , 'repr': , 'round': , 'setattr': , 'sorted': , 'sum': , 'vars': , 'None': None, 'Ellipsis': Ellipsis, 'NotImplemented': NotImplemented, 'False': False, 'True': True, 'bool': , 'memoryview': , 'bytearray': , 'bytes': , 'classmethod': , 'complex': , 'dict': , 'enumerate': , 'filter': , 'float': , 'frozenset': , 'property': , 'int': , 'list': , 'map': , 'object': , 'range': , 'reversed': , 'set': , 'slice': , 'staticmethod': , 'str': , 'super': , 'tuple': , 'type': , 'zip': , '__debug__': True, 'BaseException': , 'Exception': , 'TypeError': , 'StopAsyncIteration': , 'StopIteration': , 'GeneratorExit': , 'SystemExit': , 'KeyboardInterrupt': , 'ImportError': , 'ModuleNotFoundError': , 'OSError': , 'EnvironmentError': , 'IOError': , 'WindowsError': , 'EOFError': , 'RuntimeError': , 'RecursionError': , 'NotImplementedError': , 'NameError': , 'UnboundLocalError': , 'AttributeError': , 'SyntaxError': , 'IndentationError': , 'TabError': , 'LookupError': , 'IndexError': , 'KeyError': , 'ValueError': , 'UnicodeError': , 'UnicodeEncodeError': , 'UnicodeDecodeError': , 'UnicodeTranslateError': , 'AssertionError': , 'ArithmeticError': , 'FloatingPointError': , 'OverflowError': , 'ZeroDivisionError': , 'SystemError': , 'ReferenceError': , 'BufferError': , 'MemoryError': , 'Warning': , 'UserWarning': , 'DeprecationWarning': , 'PendingDeprecationWarning': , 'SyntaxWarning': , 'RuntimeWarning': , 'FutureWarning': , 'ImportWarning': , 'UnicodeWarning': , 'BytesWarning': , 'ResourceWarning': , 'ConnectionError': , 'BlockingIOError': , 'BrokenPipeError': , 'ChildProcessError': , 'ConnectionAbortedError': , 'ConnectionRefusedError': , 'ConnectionResetError': , 'FileExistsError': , 'FileNotFoundError': , 'IsADirectoryError': , 'NotADirectoryError': , 'InterruptedError': , 'PermissionError': , 'ProcessLookupError': , 'TimeoutError': , 'open': , 'quit': Use quit() or Ctrl-Z plus Return to exit, 'exit': Use exit() or Ctrl-Z plus Return to exit, 'copyright': Copyright (c) 2001-2017 Python Software Foundation.
# 可以产生名称空间的语法 def fn(): # 具有名称空间:fn.__dict__ pass class Fn(): # 具有名称空间:Fn.__dict__ pass import md # 具有名称空间:md.__dict__ # 名称空间如何为一个名字设置值,或访问一个名字对应的值 fn.__dict__[名字] = 值 # 设置值 print(fn.__dict__[名字]) # 取值 # 重点:名称空间取值赋值的语法优化:点语法 fn.名字 = 值 # 设置值 print(fn.名字) # 取值
四、类与对象的声明
class People: name = '人' p1 = People() p2 = People() # 结论1:类与每一个对象的名称空间都是独立的 print(p1.__dict__) # {} print(p2.__dict__) # {} print(People.__dict__) # {'name': '人', ...系统的} # 结论2:类与每一个对象都可以使用类中的名字 print(People.name) # 人 print(p1.name) # 人 print(p2.name) # 人 # 结论3:对象访问名字,优先访问自己的,自己没有再访问类的 p1.name = '张三' p2.user = '李四' print(People.name) # 人 print(p1.name) # 张三 print(p2.user) # 李四 print(p2.name) # 人 # 重点: # 对象操作名字,操作的是对象的,类操作名字操作的是类的,之间相互不干预 # 类只能访问类的名字 # 对象访问名字,优先访问自身的,自身没有再访问类的
五、类的初始化方法
# 可以快速为类实例化出的每一个对象,产生对象名称空间中的多个名字 class NewTeacher: def __init__(self, name, sex, age): # print(id(self)) # self就是实例化产生的对象(nt1) # print('init 被调用了') self.name = name self.sex = sex self.age = age pass # 类()就是在调用类的__init__方法 nt1 = NewTeacher('王大锤', '男', 58) # print(id(nt1)) print(nt1.name, nt1.sex, nt1.age) nt2 = NewTeacher('王小锤', '男', 48) print(nt2.name, nt2.sex, nt2.age)
演化:
class Teacher: name = '教授' def set_obj(obj, name, sex, age): obj.name = name obj.sex = sex obj.age = age t1 = Teacher() # t1.name = 'C老师' # t1.sex = "女" # t1.age = 36 set_obj(t1, 'C老师', "女", 36) print(t1.name, t1.sex, t1.age) t2 = Teacher() # 类()应该就是函数的调用,函数调用可以传参,能优化属性的赋值 # t2.name = 'W老师' # t2.sex = "女" # t2.age = 28 set_obj(t2, 'W老师', "女", 28) print(t2.name, t2.sex, t2.age) # t3 = Teacher() # set_obj(t3, '王大锤', '男', 58) # 能不能再优化 => t3 = Teacher('王大锤', '男', 58) # print(t3.name, t3.sex, t3.age)
六、类的方法分类
# 对象方法:直接定义的方法,建议由对象调用,类中内部需要使用对象的数据时的方法要定义为对象方法 # 1.对象方法对象调用,默认传入对象给第一个形参 class 类名: def fn(self, *args, **kwargs): pass # 类方法:被classmethod修饰的方法,建议由类调用,类中内部需要使用类的数据时的方法要定义为类方法 # 2.类方法由类调用,默认传入类给第一个形参 class 类名: @classmethod def fn(cls, *args, **kwargs): pass # 静态方法:被staticmethod修饰的方法,建议由类调用,类中内部不需要类相关数据时的方法要定义为静态方法 # 3.静态方法建议由类调用,默认不传入调用者 @staticmethod def fn(*args, **kwargs): pass
案例1:
class Student: name = '学生' def __init__(self, name, id_num): self.name = name self.id_num = id_num # 对象(成员)方法 def study(self): # 在类中产生的所有方法,都是属于类的,但是对象可以调用 # print('self>>', id(self)) print("%s在学习" % self.name) # 类方法: 第一个参数就是用来接收调用者,类方法的调用者一定是类,所以第一个参数命名约定为cls @classmethod def fn(cls): print(id(cls)) # 静态方法 @staticmethod def func(): print('func run') stu1 = Student('Bob', 1) print(stu1.__dict__) # {'name': 'Bob', 'id_num': 1} print(stu1.name) # Bob # stu1.__dict__.clear() # 清空stu1 # print(stu1.__dict__) # {} # print(stu1.name) # 学生 print("====================") stu2 = Student('Tom', 2) stu1.study() # Bob在学习 print(id(stu1)) # 32140984 stu2.study() # Tom在学习 print(id(stu2)) # print("====================") # 结论1:对象调用类中的方法,默认隐式将对象自身传入,在方法中,第一个参数用self来接受传入的对象 # Student.fn(Student) => Student.fn() # print(id(Student)) Student.fn() # 41758904 print(id(Student)) # 41758904 # 结论2:类中用@classmethod装饰的方法,是类方法,用类来调用,默认会将类传入给方法的第一个参数 print("====================") Student.func() # func run stu1.func() # func run # 结论3:类中用@staticmethod装饰的方法,是静态方法,可以被类和对象调用,默认不会将类或对象传入
案例二:类的不同方法应用
# 案例 class Book: name = '书' def __init__(self, name, price): self.name = name self.price = price # 书的详情信息 => 一定需要知道哪本书 # @classmethod # 类调用cls就是类,对象调用处理成 对象.__class__ def detail(self): # print(cls.name) print("%s的价格为:%s元" % (self.name, self.price)) book1 = Book('西游记', 38.8) book2 = Book('三国', 88.8) book1.detail() book2.detail() # print(book1.__class__) # 静态方法:方法的内部不需要对象及类的参与,所以定义为静态方法,但是方法必须由调用者,建议用类就可以了 class NumTool: # 工具类 => 模块 def max_two(self, n1, n2): max_num = n1 if n1 > n2 else n2 print('大数是%s' % max_num) @staticmethod def new_max_two(n1, n2): max_num = n1 if n1 > n2 else n2 print('大数是%s' % max_num) n1 = NumTool() n2 = NumTool() n1.max_two(10, 20) n2.max_two(10, 20) NumTool.new_max_two(10, 20) n1.new_max_two(10, 20) # 类方法:方法的内部需要类的参与,所以定义为类方法,第一个参数默认传类 class NewNumTool: PI = 3.14 @classmethod def new_max_two(cls, n1, n2): max_num = n1 if n1 > n2 else n2 return max_num @classmethod def new_max_three(cls, n1, n2, n3): # max_num = "想去复用new_max_two" max_num = cls.new_max_two(n1, n2) max_num = cls.new_max_two(max_num, n3) return max_num @classmethod def is_PI(cls, num): if num == cls.PI: return True return False res = NewNumTool.new_max_three(1, 5, 3) print('大数是%s' % res) print(NewNumTool.is_PI(3.149))
七、封装
# 什么是封装:将类的一下属性和方法对外隐藏,对内可见 # 为什么要封装:为属性和方法的操作添加权限,具体权限都是通过自定义逻辑来处理 # 封装的手段:在类属性方法,对象属性方法,静态方法名字前添加 __ # 只要是通过 __名字 这种命名规范,就是对外隐藏 # 本质:__名字 封装隐藏变量的本质是 将名字修饰成 _类名__名字 # 对外解决封装的方式 # 1.如果真的不想让外界访问,就不对外提供访问数据的方法 # 2.如果想让外界访问,可以对外提供访问数据的方法,方法具有逻辑,使用可以添加操作权限 class Test: def __init__(self, name): # __name只是对外隐藏,对内可见 self.__name = name def get_name(self): return self.__name def set_name(self, name): if 'sb' not in name: # 对数据的修改可能会产生数据的安全性问题,可以添加限制条件 self.__name = name
# 重点:封装的对外访问语法的优化 class User: def __init__(self, name): self.__name = name @property # 将方法伪装成属性 def name(self): return self.__name @name.setter # 能为有伪装get方法的(方法)属性,再伪装set方法 def name(self, value): self.__name = value @name.deleter def name(self): del self.__name # 总结: # 1.对象没了,对象的属性也就没了,所以不需要属性 @名字.deleter # 2.对外提供get方法是基础,@property,如果没有,外界不可读不可写 # 3.如果有@property,则可以 @名字.setter,有set,为可读可写,无set为只读 @property # 伪装的属性方法,不需要一定有 __开头 的名字与之对应 def pwd(self): return '123456' u1 = User('Owen') print(u1.name) # 如果一个方法伪装成属性,对象.方法名 就会自动调用该方法 u1.name = 'Zero' print(u1.name) # del u1.name # print(u1.name) print(u1.pwd)
#@property 将方法伪装成属性,从而可以像__init__里的属性一样调用u1.name
八、类的继承
什么是继承
继承是一种新建类的方式,继承的类称之为子类或派生类
被继承的类称之为父类或基类或超类
子类继承父类,也就意味着子类继承了父类所有的属性和方法
可以直接调用
为什么要有继承
减少代码冗余
如何使用
class Parent1: pass class Parent2: pass class Son1(Parent1): pass # python中支持多继承 class Son2(Parent1,Parent2): pass # 如何查看类的父类 print(Son1.__bases__) # (,) print(Son2.__bases__) # (, # 自定义的没有显示继承任何类的父类到底有没有偷偷继承某个类呢? print(Parent1.__bases__) # () ,) print(Parent2.__bases__) # (,)
# python2:类如果没有显示继承任何类的情况下,不继承任何类 # python3:类如果没有显示继承任何类的情况下,默认都继承object类 经典类与新式类 经典类: 不继承object或者其子类的类 叫经典类 新式类: 继承object或者其子类的类 叫新式类 # ps:经典类与新式类只在python2有区分 #python3中只有新式类
派生:
# 派生:在继承了父类的属性和方法的基础之上 自己定义了其他的属性和方法 # 如果派生出的方法与父类的同名了 那么相当于覆盖了父类的方法 # 类:一系列对象相似的特征与技能的结合体 # 类与类之间相似的特征与技能的结合体 >>> 父类 # 类:一系列对象相似的特征与技能的结合体 # 类与类之间相似的特征与技能的结合体 >>> 父类 import pickle class OldboyPeople: school = 'oldboy' def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def save(self): with open(self.name,'wb') as f: pickle.dump(self,f) class OldboyStudent(OldboyPeople): def choose_course(self): print('%s is choosing course'%self.name) class OldboyTeacher(OldboyPeople): def __init__(self,name,age,gender,level): OldboyPeople.__init__(self,name,age,gender) self.level = level def score(self): print('%s is score'%self.name) stu = OldboyStudent('simon',18,'male') print(stu.name) # simon stu.save() # 创建文件存数据 tea = OldboyTeacher('xc',18,'male',10) print(tea.name) # simon tea.save()
子类方法中调用父类的方法:
# 方法一:指名道姓 # OldboyPeople.__init__(self,name,age,gender) 跟继承一点关系都没有 # 方法二:和继承有关系 # 单继承情况下的属性查找 class B: def f1(self): print('from B f1') def f2(self): print('from B f2') self.f1() class A(B): def f1(self): print('from A f1') obj = A() obj.f2() """ from B f2 from A f1 """ # 上面的例子可以看出:类是查找顺序是先查找自己,再查找父类
# 多继承
# 无论是python2还是python3继承都遵循深度优先(菱型继承除外) # 深度优先:依次先从左边分支查找完后向右查找 # # 多继承 class D: # def test(self): # print('D') pass class E: def test(self): print('E') class F: def test(self): print('F') class A(D): # def test(self): # print('A') pass class B(E): # def test(self): # print('B') pass class C(F): def test(self): print('C') class G(A,B,C): # def test(self): # print('G') pass obj = G() obj.test() # E
# 调用方式 import pickle class OldboyPeople: school = 'oldboy' def __init__(self,name,age,gender): self.name = name self.age = age self.gender = gender def save(self): with open(self.name,'wb') as f: pickle.dump(self,f) class OldboyTeacher(OldboyPeople): def __init__(self,name,age,gender,level): # OldboyPeople.__init__(self,name,age,gender) # super(OldboyTeacher,self).__init__(name,age,gender) super().__init__(name,age,gender) # 调用 self.level = level tea = OldboyTeacher('ymc',18,'male',10) print(tea.name) # ymc tea.save()
MRO列表,C3算法
# mro列表 C3算法 class D: pass class E: pass class F: pass class A(D): pass class B(E): pass class C(F): pass class G(A,B,C): pass
print(G.mro()) """ G A D B E C F [, """, , , , , , ]
# super是严格按照mro列表的顺序调用父类的方法的!!!
class A: def f1(self): print('from a f1') def f2(self): print('from a f2') super().f1() class B: def f1(self): print('from b f1') def f2(self): print('from b f2') class C(A,B): def f1(self): print('from c f1') print(C.mro()) # [, obj = C() obj.f2() """ from a f2 from b f1 """, , ]
九、多态与多态性
# 什么是多态 一种事物的不同形态(动物:人,狗,猫),代码层面上来说其实就是继承 # 为什么要有多态 多态仅仅是一个概念 # 多态性:在不需要考虑对象具体类型的情况下 调用对象的方法 # 如何使用
class Animal: def talk(self): pass class People(Animal): def talk(self): print('hello') class Dog(Animal): def talk(self): print('wangwang') class Cat(Animal): def talk(self): print('miaomiao') people = People() dog = Dog() cat = Cat() people.talk() dog.talk() cat.talk()
# 例如:len获取长度
十、反射
# 通过字符串来操作对象的属性或方法
# 通过字符串来操作对象的属性或方法 class User: school = 'oldgirl' def __init__(self,name,age): self.name = name self.age = age def func(self): print('func') # hasattr判断对象是否有某个属性或方法 print(User.school) # oldgirl print('school' in User.__dict__) # True print('xxx' in User.__dict__) # False print(hasattr(User,'school')) # True print(hasattr(User,'xxx')) # False print(hasattr(User,'func')) # True print('func' in User.__dict__) # True d = {'name':"jason"} print(d.get('password','hahahahahaha')) # hahahahahaha # getattr 取类中的属性或方法 print(getattr(User,'school')) # oldgirl print(getattr(User,'func')) #print(User.__dict__['school']) # oldgirl print(User.__dict__['func']) # # print(getattr(User,'xxx')) # 获取没有的报错 if hasattr(User,'xxx'): getattr(User,'xxx') # setattr obj = User('simon',18) setattr(obj,'gender','male') # obj.gender = 'male' print(obj.__dict__) # delattr delattr(User,'school') print(User.__dict__)
十一、内置方法
class User: def __init__(self,name,password): self.name = name self.password = password def __str__(self): # return '%s:%s'%(self.name,self.password) return '我被打印了,自动触发' def __getattr__(self, item): print(item) def __setattr__(self, key, value): print(key,value) # __str__对象被执行了打印操作 自动触发 obj = User('simon',123) print(User) print(obj) """ name simon password 123我被打印了,自动触发 """ # __getattr__ 当对象获取一个不存在的属性时候才会触发 obj = User('simon',18) print(obj.name) """ name simon password 18 name None """ # __setattr__: obj.name = 'xxx' 固定句式 obj = User('simon',18) obj.sex = 'sexy' """ name simon password 18 sex sexy """ class Demo(dict): def __getattr__(self, item): return self.get(item) d = Demo(name='simon',password=123) print(d.name) # simon print(d.password) # 123
作业一:
1.自定义一个 Fruit 类:该类有一个 类属性: identify:值为"水果",有两个对象属性: name,price:值由实例化对象时赋值,
一个类方法: get_identify:打印类属性identify的值,
一个对象方法:get_total_price(num):打印『%s个%s值%s钱』,
一个静态方法:packing(*fruits) 静态方法(装箱)的思路分析 red_apple = Fruit("红苹果", 10) green_apple = Fruit("青苹果", 10) yellow_banana = Fruit("黄香蕉", 8) 调用:Frulit.packing(red_apple, green_apple, yellow_banana) 打印:一箱装了2个苹果1个香蕉 2.自定义一个 Person 类,该类具有 name、weight、height、sex 四个对象属性, -- 对name属性进行封装,但是外界任然可以访问name以及设置name -- 有一个方法属性bmi(标准体重),可以获取一个人的bmi,bmi只读不可写,bmi计算规则 -- 男:(身高cm-80)× 70﹪ | 女:(身高cm-70)× 60﹪ 提示:类属性就是直接写在类中的变量,对象属性就是写在__init__方法中的用self.属性 = 值 赋值的属性,方法属性就是用 @property 修饰的方法伪装成的属性 4.用面向对象实现 植物大战僵尸游戏 1).定义一个僵尸Zombie类,该类可以实例化出多种僵尸对象,僵尸对象产生默认都有 名字name、血量HP、防具armor -- 名字:普通僵尸 | 路障僵尸 | 铁桶僵尸 -- 血量:默认就是100,不需要外界提供 -- 防具:不需要外界提供,从名字中分析确定,防具的值是一个列表,从名字分析得到 -- ['无', 0] | ['路障', 5] | ['铁桶', 15] => [防具名, 防具的防御值] -- 通过@property的getter、setter方式,对外提供防具的两个访问接口armor_name与armor_count -- armor_name可以取值、赋值、删除值:通过一个 -- eg: 普通僵尸对象.armor_name = '铁桶',不仅改变了防具名 -- 普通僵尸对象的名字name也会变成 铁桶僵尸 -- armor_count只可以取值 2).定义一个角色User类,该类有名字name属性、以及打僵尸的beat方法 -- 名字:随意自定义 -- beat:该方法需要传入一个僵尸对象 -- 在方法内部可以实现:某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血 -- 每一次攻击,都固定扣除25滴血,但是不同的僵尸会被防具相应抵消掉一定的伤害值 -- 循环攻击僵尸,3s攻击一次,僵尸被击杀后,打印 某某用户击杀了某某个僵尸 并结束方法 3).定义一个Game类,该类有一个name属性,属性值为 "植物大战僵尸" ,该类中有一个start方法,通过Game.start()来启动游戏 -- 游戏一开始先显示游戏的名字 植物大战僵尸游戏 -- 会随机产生三种僵尸,总共产生三个作为要被击杀的对象 -- 生成一个有角色名的角色,依次去击杀随机产生的每一只僵尸 -- 开始击杀第一只僵尸 => 某某用户攻击了某某个僵尸,僵尸损失多少血,还剩多少血 => 某某用户击杀了某某个僵尸 => 第一只僵尸已被击杀完毕 => 开始击杀第二只僵尸 ... => 第三只僵尸已被击杀完毕
作业二:
角色:学校、学员、课程、讲师
要求:
1. 创建北京、上海 2 所学校---学校
2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开 ----课程、地点
3. 课程包含,周期,价格,通过学校创建课程-----时间、价格、
4. 通过学校创建班级, 班级关联课程、讲师---班级、课程、讲师
5. 创建学员时,选择学校,关联班级 ---学员、学校、班级
5. 创建讲师角色时要关联学校,---讲师、学校
6. 提供两个角色接口
6.1 学员视图, 可以注册, 交学费, 选择班级,---》注册、学费、班级
6.2 讲师视图, 讲师可管理自己的班级, 上课时选择班级, 查看班级学员列表 , 修改所管理的学员的成绩
6.3 管理视图,创建讲师, 创建班级,创建课程
7. 上面的操作产生的数据都通过pickle序列化保存到文件里