zl程序教程

您现在的位置是:首页 >  后端

当前栏目

Python学习笔记 day6 面向对象编程

Python笔记学习 面向对象编程 Day6
2023-09-27 14:26:26 时间

面向对象编程(Object-Oriented Programming )

OOP编程是利用“类”和“对象”来创建各种模型来实现对真实世界的描述,使用面向对象编程的原因一方面是因为它可以使程序的维护和扩展变得更简单,并且可以大大提高程序开发效率 ,另外,基于面向对象的程序可以使它人更加容易理解你的代码逻辑,从而使团队开发变得更从容。

面向对象的几个核心特性如下

Class 类
一个类即是对一类拥有相同属性的对象的抽象、蓝图、原型。在类中定义了这些对象的都具备的属性(variables(data))、共同的方法

Object 对象
一个对象即是一个类的实例化后实例,一个类必须经过实例化后方可在程序中调用,一个类可以实例化多个对象,每个对象亦可以有不同的属性,就像人类是指所有人,每个人是指具体的对象,人与人之前有共性,亦有不同

Encapsulation 封装
在类中对数据的赋值、内部调用对外部用户是透明的,这使类变成了一个胶囊或容器,里面包含着类的数据和方法

Inheritance 继承
一个类可以派生出子类,在这个父类里定义的属性、方法自动被子类继承

Polymorphism 多态
多态是面向对象的重要特性,简单点说:“一个接口,多种实现”,指一个基类中派生出了不同的子类,且每个子类在继承了同样的方法名的同时又对父类的方法做了不同的实现,这就是同一种事物表现出的多种形态。
编程其实就是一个将具体世界进行抽象化的过程,多态就是抽象化的一种体现,把一系列具体事物的共同点抽象出来, 再通过这个抽象的事物, 与不同的具体事物进行对话。
对不同类的对象发出相同的消息将会有不同的行为。比如,你的老板让所有员工在九点钟开始工作, 他只要在九点钟的时候说:“开始工作”即可,而不需要对销售人员说:“开始销售工作”,对技术人员说:“开始技术工作”, 因为“员工”是一个抽象的事物, 只要是员工就可以开始工作,他知道这一点就行了。至于每个员工,当然会各司其职,做各自的工作。
多态允许将子类的对象当作父类的对象使用,某父类型的引用指向其子类型的对象,调用的方法是该子类型的方法。这里引用和调用方法的代码编译前就已经决定了,而引用所指向的对象可以在运行期间动态绑定
 

 

建立四个角色

#role 1
name = 'Alex'
role = 'terrorist'
weapon = 'AK47'
life_value = 100
money = 10000
 
#rolw 2
name2 = 'Jack'
role2 = 'police'
weapon2 = 'B22'
life_value2 = 100
money2 = 10000
 
#role 3
name3 = 'Rain'
role3 = 'terrorist'
weapon3 = 'C33'
life_value3 = 100
money3 = 10000
 
#rolw 4
name4 = 'Eric'
role4 = 'police'
weapon4 = 'B51'
life_value4 = 100
money4 = 10000

4个角色虽然创建好了,但是有个问题就是,每创建一个角色,我都要单独命名,name1,name2,name3,name4…,后面的调用的时候这个变量名你还都得记着,要是再让多加几个角色,估计调用时就很容易弄混啦,所以我们想一想,能否所有的角色的变量名都是一样的,但调用的时候又能区分开分别是谁

roles = {
    1:{'name':'Alex',
       'role':'terrorist',
       'weapon':'AK47',
       'life_value': 100,
       'money': 15000,
       },
    2:{'name':'Jack',
       'role':'police',
       'weapon':'B22',
       'life_value': 100,
        'money': 15000,
       },
    3:{'name':'Rain',
       'role':'terrorist',
       'weapon':'C33',
       'life_value': 100,
       'money': 15000,
       },
    4:{'name':'Eirc',
       'role':'police',
       'weapon':'B51',
       'life_value': 100,
       'money': 15000,
       },
}
 
print(roles[1]) #Alex
print(roles[2]) #Jack

这个以后调用这些角色时只需要roles[1],roles[2]就可以啦,角色的基本属性设计完了后,我们接下来为每个角色开发以下几个功能
1.被打中后就会掉血的功能
2.开枪功能
3.换子弹
4.买枪
5.跑、走、跳、下蹲等动作
6.保护人质(仅适用于警察)
7.不能杀同伴
。。。

so far so good, 继续按照这个思路设计,再完善一下代码,游戏的简单版就出来了,但是在往下走之前,我们来看看上面的这种代码写法有没有问题,至少从上面的代码设计中,我看到以下几点缺陷:

1.每个角色定义的属性名称是一样的,但这种命名规则是我们自己约定的,从程序上来讲,并没有进行属性合法性检测,也就是说role 1定义的代表武器的属性是weapon, role 2 ,3,4也是一样的,不过如果我在新增一个角色时不小心把weapon 写成了wepon , 这个程序本身是检测 不到的
2.terrorist 和police这2个角色有些功能是不同的,比如police是不能杀人质的,但是terrorist可能,随着这个游戏开发的更复杂,我们会发现这2个角色后续有更多的不同之处, 但现在的这种写法,我们是没办法 把这2个角色适用的功能区分开来的,也就是说,每个角色都可以直接调用任意功能,没有任何限制。
3.我们在上面定义了got_shot()后要减血,也就是说减血这个动作是应该通过被击中这个事件来引起的,我们调用get_shot(),got_shot()这个函数再调用每个角色里的life_value变量来减血。 但其实我不通过got_shot(),直接调用角色roles[role_id][‘life_value’] 减血也可以呀,但是如果这样调用的话,那可以就是简单粗暴啦,因为减血之前其它还应该判断此角色是否穿了防弹衣等,如果穿了的话,伤害值肯定要减少,got_shot()函数里就做了这样的检测,你这里直接绕过的话,程序就乱了。 因此这里应该设计 成除了通过got_shot(),其它的方式是没有办法给角色减血的,不过在上面的程序设计里,是没有办法实现的。
4.现在需要给所有角色添加一个可以穿防弹衣的功能,那很显然你得在每个角色里放一个属性来存储此角色是否穿 了防弹衣,那就要更改每个角色的代码,给添加一个新属性,这样太low了,不符合代码可复用的原则
上面这4点问题如果不解决,以后肯定会引出更大的坑,解决也不复杂呀,直接在每个功能调用时做一下角色判断啥就好了,没错,你要非得这么霸王硬上弓的搞也肯定是可以实现的,那你自己就开发相应的代码来对上面提到的问题进行处理好啦。 但这些问题其实能过OOP就可以很简单的解决。

之前的代码改成用OOP中的“类”来实现的话如下:

class Role(object):
    def __init__(self,name,role,weapon,life_value=100,money=15000):
        self.name = name
        self.role = role
        self.weapon = weapon
        self.life_value = life_value
        self.money = money
 
    def shot(self):
        print("shooting...")
 
    def got_shot(self):
        print("ah...,I got shot...")
 
    def buy_gun(self,gun_name):
        print("just bought %s" %gun_name)
 
r1 = Role('Alex','police','AK47’) #生成一个角色
r2 = Role('Jack','terrorist','B22’)  #生成一个角色

先不考虑语法细节,相比靠函数拼凑出来的写法,上面用面向对象中的类来写最直接的改进有以下2点:
1.代码量少了近一半
2.角色和它所具有的功能可以一目了然看出来

 

类的语法

class Dog(object):
    print("hello,I am a dog!")
 
d = Dog() #实例化这个类,
#此时的d就是类Dog的实例化对象
 
#实例化,其实就是以Dog类为模版,在内存里开辟一块空间,存上数据,赋值成一个变量名

上面的代码其实有问题,想给狗起名字传不进去。

class Dog(object):
    def __init__(self,name,dog_type):
        self.name = name
        self.type = dog_type
 
    def sayhi(self):
        print("hello,I am a dog, my name is ",self.name)
 
 
d = Dog('LiChuang',"京巴")  #实例化这个类, 
d.sayhi()
为什么有__init__? 为什么有self?

在这里插入图片描述 根据上图我们得知,其实self,就是实例本身!你实例化时python会自动把这个实例本身通过self参数传进去。

class Role(object): #定义一个类, class是定义类的语法,Role是类名,(object)是新式类的写法,必须这样写,以后再讲为什么
    def __init__(self,name,role,weapon,life_value=100,money=15000): #初始化函数,在生成一个角色时要初始化的一些属性就填写在这里
        self.name = name #__init__中的第一个参数self,和这里的self都 是什么意思? 看下面解释
        self.role = role
        self.weapon = weapon
        self.life_value = life_value
        self.money = money

上面的这个__init__()叫做初始化方法(或构造方法), 在类被调用时,这个方法(虽然它是函数形式,但在类中就不叫函数了,叫方法)会自动执行,进行一些初始化的动作,所以我们这里写的__init__(self,name,role,weapon,life_value=100,money=15000)就是要在创建一个角色时给它设置这些属性,那么这第一个参数self是干毛用的呢?

初始化一个角色,就需要调用这个类一次:

r1 = Role('Alex','police','AK47’) #生成一个角色 , 会自动把参数传给Role下面的__init__(...)方法
r2 = Role('Jack','terrorist','B22’)  #生成一个角色

我们看到,上面的创建角色时,我们并没有给__init__传值,程序也没未报错,是因为,类在调用它自己的__init__(…)时自己帮你给self参数赋值了,

r1 = Role('Alex','police','AK47’) #此时self 相当于 r1 ,  Role(r1,'Alex','police','AK47’)
r2 = Role('Jack','terrorist','B22’)#此时self 相当于 r2, Role(r2,'Jack','terrorist','B22’)

为什么这样子?
你执行r1 = Role(‘Alex’,‘police’,'AK47’)时, python的解释器其实干了两件事:
1.在内存中开辟一块空间指向r1这个变量名

2.调用Role这个类并执行其中的__init__(…)方法,相当于Role.init(r1,‘Alex’,‘police’,’AK47’),这么做是为什么呢? 是为了把’Alex’,‘police’,’AK47’这3个值跟刚开辟的r1关联起来,是为了把’Alex’,‘police’,’AK47’这3个值跟刚开辟的r1关联起来,是为了把’Alex’,‘police’,’AK47’这3个值跟刚开辟的r1关联起来,重要的事情说3次, 因为关联起来后,你就可以直接r1.name, r1.weapon 这样来调用啦。所以,为实现这种关联,在调用__init__方法时,就必须把r1这个变量也传进去,否则__init__不知道要把那3个参数跟谁关联呀。

3.所以这个__init__(…)方法里的,self.name = name , self.role = role 等等的意思就是要把这几个值 存到r1的内存空间里。


后面那几个方法 为什么也还需要self参数么? 不是在初始化角色的时候 ,就已经把角色的属性跟r1绑定好了么?

def buy_gun(self,gun_name):
    print(%s has just bought %s” %(self.name,gun_name) )

上面这个方法通过类调用的话要写成如下:

r1 = Role('Alex','police','AK47')
r1.buy_gun("B21”) #python 会自动帮你转成 Role.buy_gun(r1,”B21")

执行结果
#Alex has just bought B21 

依然没给self传值 ,但Python还是会自动的帮你把r1 赋值给self这个参数, 为什么呢? 因为,你在buy_gun(…)方法中可能要访问r1的一些其它属性呀, 比如这里就访问 了r1的名字,怎么访问呢?你得告诉这个方法呀,于是就把r1传给了这个self参数,然后在buy_gun里调用 self.name 就相当于调用r1.name 啦,如果还想知道r1的生命值 有多少,直接写成self.life_value就可以了。 说白了就是在调用类中的一个方法时,你得告诉人家你是谁

 
总结一下2点:
1.上面的这个r1 = Role(‘Alex’,‘police’,'AK47’)动作,叫做类的“实例化”, 就是把一个虚拟的抽象的类,通过这个动作,变成了一个具体的对象了, 这个对象就叫做实例
2.刚才定义的这个类体现了面向对象的第一个基本特性,封装,其实就是使用构造方法将内容封装到某个具体对象中,然后通过对象直接或者self间接获取被封装的内容

 

继承

面向对象编程 (OOP) 语言的一个主要功能就是“继承”。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。

通过继承创建的新类称为“子类”或“派生类”。
被继承的类称为“基类”、“父类”或“超类”。

继承的过程,就是从一般到特殊的过程。

要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。

在某些 OOP 语言中,一个子类可以继承多个基类。但是一般情况下,一个子类只能有一个基类,要实现多重继承,可以通过多级继承来实现。

继承概念的实现方式主要有2类:实现继承、接口继承。
Ø 实现继承是指使用基类的属性和方法而无需额外编码的能力;
Ø 接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力(子类重构爹类方法);

在考虑使用继承时,有一点需要注意,那就是两个类之间的关系应该是“属于”关系。例如,Employee 是一个人,Manager 也是一个人,因此这两个类都可以继承 Person 类。但是 Leg 类却不能继承 Person 类,因为腿并不是一个人。

抽象类仅定义将由子类创建的一般属性和方法。

OO开发范式大致为:划分对象→抽象类→将类组织成为层次化结构(继承和合成) →用类与实例进行设计和实现几个阶段。

继承实例:

# 学校是个场地,是独立的,没有人继承学校,但后面会去调用。和student和teacher是种组合关系
class School(object): 
    def __init__(self,name,addr):
        self.name = name
        self.addr = addr
        self.studens=[]
        self.teachers=[]

    def enroll(self,stu_obj):
        print("为学员%s办理注册手续"%stu_obj.name)
        self.studens.append(stu_obj)

    def hire(self,teach_obj):
        print("雇佣%s为新老师"%teach_obj.name)
        self.teachers.append(teach_obj)


# schoolmember是继承了object基类。 父类的类就叫基类
class SchoolMember(object):  # 这是新式类   经典类(没Object)
    members = 0  # 初始学校人数为0

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def tell(self):
        pass

    def enroll(self):
        '''注册'''
        SchoolMember.members += 1
        print("\033[32;1mnew member [%s] is enrolled,now there are [%s] members.\033[0m " % (
        self.name, SchoolMember.members))

    def __del__(self):
        '''析构方法'''
        print("\033[31;1mmember [%s] is dead!\033[0m" % self.name)


class Teacher(SchoolMember):
    def __init__(self, name, age, course, salary):  # 重构,用户传到这,再通过父类获取
        super(Teacher, self).__init__(name, age)  # 第二种经典类 ,第二种这种是强制SchoolMember.__init__(self,name,age)
        self.course = course
        self.salary = salary
        self.enroll()

    def teaching(self):
        '''讲课方法'''
        print("Teacher [%s] is teaching [%s] for class [%s]" % (self.name, self.course, 's12'))

    def tell(self):
        '''自我介绍方法'''
        print(''' ---------info of teacher:%s---------
            name:%s
            age:%s
            course:%s
            salary:%s
        ''' %(self.name,self.name,self.age,self.course,self.salary))


class Student(SchoolMember):
    def __init__(self, name, age, grade, sid):
        super(Student, self).__init__(name, age)
        self.grade = grade
        self.sid = sid
        self.enroll()

    def tell(self):
        '''自我介绍方法'''
        msg = '''Hi, my name is [%s], I'm studying [%s] in [%s]!''' % (self.name, self.grade, 'Oldboy')
        print(msg)


if __name__ == '__main__':
    t1 = Teacher("Alex", 22, 'Python', 20000)
    s1 = Student("Qinghua", 24, "Python S12", 1483)

    t1.teaching()
    t1.tell()

    s1.tell()
    print("\n——————————分割线——————————")
    school = School('果子哥','天地')
    school.enroll(s1)
    school.hire(t1)
    print(school.studens)
    print(school.teachers)
    school.teachers[0].teaching()

结果为:
new member [Alex] is enrolled,now there are [1] members. 
new member [Qinghua] is enrolled,now there are [2] members. 
Teacher [Alex] is teaching [Python] for class [s12]
 ---------info of teacher:Alex---------
            name:Alex
            age:22
            course:Python
            salary:20000
        
Hi, my name is [Qinghua], I'm studying [Python S12] in [Oldboy]!

——————————分割线——————————
为学员Qinghua办理注册手续
雇佣Alex为新老师
[<__main__.Student object at 0x0000020F300D9D88>]
[<__main__.Teacher object at 0x0000020F300D9D08>]
Teacher [Alex] is teaching [Python] for class [s12]
member [Alex] is dead!
member [Qinghua] is dead!

多继承
在People继承的基础上,name那些已经有了。所以在Relation不用写
在这里插入图片描述 
Python3开始继承 就是 广度优先
先查完横向的策略
在Python2上经典类是按深度优先来继承的,新式类是按广度优先来继承的
在Python3上经典类和新式类都是统一按广度优先来继承的。

Python3:
class A: # 经典类
    def __init__(self):
        print("A")
class B(A):
    def __init__(self):
        print("B")
class C(A):
    def __init__(self):
        print("C")
class D(B,C):
    pass

结果:B
解析:D是继承B,C。 先看B如果有 初始化__init__,就打印。如果B没有初始化,就会找C,如果C没初始化就会找A的初始化

class A:
    def __init__(self):
        print("A")
class B(A):
    pass
    # def __init__(self):
    #     print("B")
class C(A):
    def __init__(self):
        print("C")
class D(B,C):
    pass

test = D()
结果:C

class A:
    def __init__(self):
        print("A")
class B(A):
    pass
    # def __init__(self):
    #     print("B")
class C(A):
    pass
    # def __init__(self):
    #     print("C")
class D(B,C):
    pass

test = D()
结果:A

在这里插入图片描述 

 

多态

多态性(polymorphisn)是允许你将父对象设置成为和一个或更多的他的子对象相等的技术,赋值之后,父对象就可以根据当前赋值给它的子对象的特性以不同的方式运作。简单的说,就是一句话:允许将子类类型的指针赋值给父类类型的指针。

那么,多态的作用是什么呢?我们知道,封装可以隐藏实现细节,使得代码模块化;继承可以扩展已存在的代码模块(类);它们的目的都是为了——代码重用。多态则是为了实现另一个目的——接口重用!多态的作用,就是为了类在继承和派生的时候,保证使用“家谱”中任一类的实例的某一属性时的正确调用。 (一种接口,多种形态)

Pyhon 很多语法都是支持多态的,比如 len(),sorted(), 你给len传字符串就返回字符串的长度,传列表就返回列表长度。

class Animal(object):
    def __init__(self, name):  # Constructor of the class
        self.name = name
 
    def talk(self):              # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")
 
class Cat(Animal):
    def talk(self):
        print('%s: 喵喵喵!' %self.name)
 
class Dog(Animal):
    def talk(self):
        print('%s: 汪!汪!汪!' %self.name)
 
def animal_talk(obj): #一个接口,多种形态
    obj.talk()
 
c1 = Cat('小东')
d1 = Dog('小西')
 
animal_talk(c1)   # c1.talk()
animal_talk(d1)   # d1.talk()


升级版:~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
class Animal(object):
    def __init__(self, name):  # Constructor of the class
        self.name = name

    def talk(self):  # Abstract method, defined by convention only
        raise NotImplementedError("Subclass must implement abstract method")

    @staticmethod
    def animal_talk(obj):  # 一个接口,多种形态
        obj.talk()

class Cat(Animal):
    def talk(self):
        print('%s: 喵喵喵!' % self.name)


class Dog(Animal):
    def talk(self):
        print('%s: 汪!汪!汪!' % self.name)


# def animal_talk(obj):  # 一个接口,多种形态
#     obj.talk()


c1 = Cat('小东')
d1 = Dog('小西')

Animal.animal_talk(c1)  # c1.talk()
Animal.animal_talk(d1)   # d1.talk()

 

领域模型

学会了面向对象的语法,却依然写不出面向对象的程序,原因是什么呢?原因就是因为你还没掌握一门面向对象设计利器
答案就是:领域建模。 从领域模型开始,我们就开始了面向对象的分析和设计过程,可以说,领域模型是完成从需求分析到面向 对象设计的一座桥梁

领域模型,顾名思义,就是需求所涉及的领域的一个建模,更通俗的讲法是业务模型。
从这个定义我们可以看出,领域模型有两个主要的作用:
1.发掘重要的业务领域概念
2.建立业务领域概念之间的关系

归纳一下域建模的方法就是“从用例中找名词”。 当然,找到名词后,为了能够更加符合面向对象的要求和特点,我们还需要对这些名词进一步完善,这就 是接下来的步骤:加属性,连关系!
总结出领域建模的三字经方法:找名词、加属性、连关系。

找名词
who : 学员、讲师、管理员

加属性
在这里插入图片描述
连关系
有了类,也有了属性,接下来自然就是找出它们的关系了。
在这里插入图片描述
 
本节作业: 选课系统
角色:学校、学员、课程、讲师
要求:

  1. 创建北京、上海 2 所学校
  2. 创建linux , python , go 3个课程 , linux\py 在北京开, go 在上海开
  3. 课程包含,周期,价格,通过学校创建课程
  4. 通过学校创建班级, 班级关联课程、讲师
  5. 创建学员时,选择学校,关联班级
  6. 创建讲师角色时要关联学校,
  7. 提供两个角色接口
    6.1 学员视图, 可以注册, 交学费, 选择班级,
    6.2 讲师视图, 讲师可管理自己的班级, 上课时选择班级, 查看班级学员列表 , 修改所管理的学员的成绩
    6.3 管理视图,创建讲师, 创建班级,创建课程
  8. 上面的操作产生的数据都通过pickle序列化保存到文件里

先有学校再有课程,通过学校创建课程
class grade(object):
# 归属于学校,继承。
# 班级默认与课程相对应的
创建讲师角色时要关联学校,hire

学校视图
   选择学校
     1.创建班级
     2.创建讲师
     3.创建课程

讲师视图
   1.查看班级
   2.查看班级学员列表

学生视图
   1.注册
   2.报名缴费
   3.选择班级

https://www.cnblogs.com/lianzhilei/p/5832691.html
细品别人代码,学习

import pickle, os

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# 数据地址
__db_main = BASE_DIR + r"main_dict"
__db_teacher = BASE_DIR + r"teacher_dict"


class School(object):
    # 创建学校
    def __init__(self, name, addr):
        self.name = name
        self.addr = addr

    def cat_school(self):
        print("学校名:【%s】\t地址:【%s】" % (self.name, self.addr))

    def hire_teacher(self, dict, course, teacher, file):
        # 数据库添加讲师信息
        dict[self][course] = {"teacher": teacher}
        file_oper(file, "wb", dict)

    def create_course(self, dict, course, file):
        # 数据库添加课程资料
        dict[self][course] = {}
        file_oper(file, "wb", dict)

    def create_grade(self, dict, teacher_dict, course, grade, teacher, file1, file2):
        # 数据库添加班级信息
        dict[self][course]["grade"] = grade
        file_oper(file1, "wb", dict)
        teacher_dict[teacher] = {"grade": grade}
        file_oper(file2, "wb", teacher_dict)


class Course():
    # 创建课程
    def __init__(self, name, price, time):
        self.name = name
        self.price = price
        self.time = time

    def cat_course(self):
        # 查看课程信息
        print("课程:【%s】\t价格:【¥%s】\t周期:【%s个月】"
              % (self.name, self.price, self.time))


class Grade():
    # 创建班级
    def __init__(self, name, course, teacher):
        student = set([])
        self.name = name
        self.course = course
        self.teacher = teacher
        self.student = student

    def cat_grade(self):
        # 查看班级信息
        print("班级:【%s】\t课程:【%s】\t讲师:【%s】"
              % (self.name, self.course, self.teacher))

    def add_student(self, student_name, dict, teacher, file):
        self.student.add(student_name)
        dict[teacher] = {"grade": self}
        file_oper(file, "wb", dict)


class People():
    def __init__(self, name, age):
        self.name = name
        self.age = age


class Teacher(People):
    # 创建讲师
    def __init__(self, name, age, school, course, role="讲师"):
        super(Teacher, self).__init__(name, age)
        self.role = role
        self.school = school
        self.course = course

    def cat_teacher(self):
        # 查看老师资料和课程
        print('课程【%s】\t讲师【%s】' % (self.course, self.name))


def file_oper(file, mode, *args):
    # 数据库写入、读取操作
    if mode == "wb":
        with open(file, mode) as f:
            dict = args[0]
            f.write(pickle.dumps(dict))

    if mode == "rb":
        with open(file, mode) as f:
            dict = pickle.loads(f.read())
            return dict


def information(dict, mode, *args):
    '''通过匹配mode模式,打印相应的输出信息'''
    if args:
        dict_info, set_info = {}, args[0]
    else:
        dict_info, set_info = {}, set([])
    if dict:
        for key in dict:
            if mode == "course":
                key.cat_course()
            if mode == "main":
                key.cat_school()
            if mode == "teacher" and key == "teacher":
                dict[key].cat_teacher()
                # dict_info[key] = dict[key]
                set_info.add(dict[key].name)
            if mode == "grade" and key == "grade":
                dict[key].cat_grade()
                set_info.add(dict[key].name)
            if mode == "teacher_center":
                pass
            if type(key) != str:  # key值不是字符串
                dict_info[key.name] = key
    return dict_info, set_info


def school_center():
    # 学校管理中心
    Flag = True
    while Flag:
        dict_main = file_oper(__db_main, "rb")  # 主字典
        res_dict = information(dict_main, "main")[0]  # 打印学校信息
        school_name = input("\33[34;0m输入要选择的学校名\33[0m:").strip()
        if school_name in res_dict:
            school = res_dict[school_name]  # 匹配选择的学校
            while Flag:
                print("\33[32;1m欢迎进入【%s】学校\33[0m".center(50, "*") % school.name)
                choice = options(list_school)  # 打印当前选项
                if choice == "1":
                    while True:
                        print("\33[32;0m学校【%s】目前已经有的班级信息\33[0m".center(40, "-") % school.name)
                        teacher_dict = file_oper(__db_teacher, "rb")
                        res_course = information(dict_main[school], "None")[0]
                        set_info = set([])
                        if res_course:  # 打印课程与讲师对应关系
                            for i in res_course:
                                k = res_course[i]
                                res_grade = information(dict_main[school][k], "grade", set_info)[1]
                        if_cont = input("\n\33[34;0m是否要创建班级 【y】创建 【b】退出\33[0m:")
                        if if_cont == "y":
                            grade_name = input("\33[34;0m输入要创建班级的名称\33[0m:").strip()
                            course_name = input("\33[34;0m输入要班级要上的课程\33[0m:").strip()
                            if course_name in res_course:
                                course = res_course[course_name]
                                if dict_main[school][course]:
                                    teacher = dict_main[school][course]["teacher"]
                                    if grade_name not in res_grade:
                                        grade = Grade(grade_name, course_name, teacher.name)
                                        school.create_grade(dict_main, teacher_dict, course, grade, teacher, __db_main,
                                                            __db_teacher)
                                    else:
                                        print("\33[31;0m错误:当前班级已经存在\33[0m")
                                else:
                                    print("\33[31;0m错误:当前课程还没有讲师\33[0m")
                            else:
                                print("\33[31;0m错误:课程【%s】不存在,请先创建课程\33[0m" % course_name)
                        if if_cont == "b":
                            break

                if choice == "2":
                    # 招聘讲师
                    while True:
                        print("\33[32;0m学校【%s】目前已经有的课程与讲师\33[0m".center(40, "-") % school.name)
                        res_course = information(dict_main[school], "None")[0]
                        set_info = set([])
                        if res_course:  # 打印课程与讲师对应关系
                            for i in res_course:
                                k = res_course[i]
                                res_teacher = information(dict_main[school][k], "teacher", set_info)[1]
                                if not res_teacher:
                                    print("课程【%s】\t讲师【None】" % (i))
                        if_cont = input("\n\33[34;0m是否要招聘讲师 【y】招聘 【b】退出\33[0m:")
                        if if_cont == "y":
                            teacher_name = input("\33[34;0m输入要招聘讲师的名字\33[0m:").strip()
                            teacher_age = input("\33[34;0m输入要招聘讲师的年龄\33[0m:").strip()
                            course_name = input("\33[34;0m输入讲师【%s】要授课的课程\33[0m:" % teacher_name).strip()
                            if course_name in res_course:
                                course = res_course[course_name]  # 创建讲师并写入数据库
                                if teacher_name not in res_teacher:
                                    teacher = Teacher(teacher_name, teacher_age, school.name, course_name)
                                    school.hire_teacher(dict_main, course, teacher, __db_main)
                                else:
                                    print("\33[31;0m错误:教师【%s】已经被聘用\33[0m" % teacher_name)
                            else:
                                print("\33[31;0m错误:课程【%s】不存在,请先创建课程\33[0m" % course_name)
                        if if_cont == "b":
                            break

                if choice == "3":
                    # 创建课程
                    while True:
                        print("\33[32;0m学校【%s】目前已经有的课程\33[0m".center(40, "-") % school.name)
                        res_dict = information(dict_main[school], "course")[0]  # 打印课程信息赋值给字典course_dict
                        if_cont = input("\n\33[34;0m是否要创建课程 【y】创建 【b】退出\33[0m:")
                        if if_cont == "y":
                            course_name = input("\33[34;0m输入要创建的课程\33[0m:").strip()
                            if course_name not in res_dict:  # 课程不存在,创建
                                price = input("\33[34;0m输入课程 【%s】 的价格\33[0m:" % (course_name))
                                time = input("\33[34;0m输入课程 【%s】 的周期(月)\33[0m:" % (course_name))
                                course = Course(course_name, price, time)  # 创建课程course
                                school.create_course(dict_main, course, __db_main)  # 关联学校和课程
                            else:  # 课程存在
                                print("\33[31;0m错误:当前课程 【%s】 已经存在\33[0m" % (course_name))
                        if if_cont == "b":
                            break

                if choice == "4":
                    Flag = False
        if Flag:
            print("\33[31;0m错误:输入的学校 【%s】 不存在\33[0m" % (school_name))


def teacher_center():
    # 讲师中心
    print("\33[32;1m欢迎进入讲师中心\33[0m".center(50, "*"))
    teacher_dict = file_oper(__db_teacher, "rb")
    dict_info = information(teacher_dict, "teacher_center")[0]  # 验证登录
    teacher_name = input("\n\33[34;0m输入要登录讲师的名字\33[0m:").strip()
    if teacher_name in dict_info:
        while True:
            print("\33[32;1m欢迎进入讲师【%s】的管理中心\33[0m".center(40, "*") % teacher_name)
            choice = options(list_teacher)
            teacher = dict_info[teacher_name]
            grade = teacher_dict[teacher]["grade"]
            if choice == "1":
                print("\33[32;0m讲师【%s】的班级信息\33[0m".center(40, "-") % teacher.name)
                print("学校【%s】\t课程【%s】\t班级【%s】\t" % (teacher.school, teacher.course, grade.name))
                any = input("\n\33[34;0m输入任意键退出当前\33[0m:")
            if choice == "2":
                print("\33[32;0m讲师【%s】的班级学员列表\33[0m".center(40, "-") % teacher.name)
                print("班级【%s】\n学员【%s】" % (grade.name, grade.student))
                any = input("\n\33[34;0m输入任意键退出当前\33[0m:")
            if choice == "3":
                break
    else:
        print("\33[31;0m错误:讲师【%s】 不存在\33[0m" % (teacher_name))


def student_center():
    # 学员中心
    print("\33[32;1m欢迎进入学员中心中心\33[0m".center(50, "*"))
    while True:
        choice = options(list_student)  # 打印学生中心选项
        if choice == "1":
            student_name = input("\33[34;0m输入学员的名字\33[0m:")
            dict = file_oper(__db_main, "rb")
            teacher_dict = file_oper(__db_teacher, "rb")
            school_dict = information(dict, "main")[0]  # 打印当前可选的学校
            school_name = input("\33[34;0m输入要选择的学校名\33[0m:").strip()
            if school_name in school_dict:
                school = school_dict[school_name]
                if dict[school]:
                    course_dict = information(dict[school], "course")[0]  # 打印当前学校下的课程
                    course_name = input("\33[34;0m输入要选择的课程\33[0m:").strip()
                    if course_name in course_dict:
                        course = course_dict[course_name]
                        if dict[school][course].get("grade"):
                            for i in teacher_dict:
                                if course.name == i.course:
                                    teacher = i
                                    grade = teacher_dict[teacher]["grade"]
                            print("课程【%s】的费用为【%s】" % (course.name, course.price))
                            if_pay = input("\33[34;0m是否支付当前费用 支付【y】\33[0m:")
                            if if_pay == "y":  # 上面全部匹配成功,选课成功
                                grade.add_student(student_name, teacher_dict, teacher, __db_teacher)
                                print("\33[31;0m选课成功\33[0m")
                                any = input("\n\33[34;0m输入任意键退出当前\33[0m:")
                        else:
                            print("\33[31;0m错误:课程没有班级\33[0m")
                    else:
                        print("\33[31;0m错误:课程不存在\33[0m")
                else:
                    print("\33[31;0m错误:当前学校没有课程\33[0m")
        if choice == "2":
            break


def options(list):
    # 打印可选择的操作模式,并返回选择值
    for i, v in enumerate(list):
        print(i + 1, v)
    choice = input("\33[34;0m选择要进入模式\33[0m:")
    return choice


def start():
    '''程序开始'''
    while True:
        print("\33[35;1m欢迎进入选课系统\33[0m".center(50, "#"))
        choice = options(list_main)  # 打印选项
        if choice == "1":
            student_center()  # 学生中心
        if choice == "2":
            teacher_center()  # 教师中心
        if choice == "3":
            school_center()  # 学校中心
        if choice == "4":
            break


def init_database():
    '''数据库初始化,不存在则创建,存在跳过'''
    bj = School("北京", "北京市")
    sh = School("上海", "上海市")
    if not os.path.exists(__db_teacher):
        dict = {bj: {}, sh: {}}
        file_oper(__db_main, "wb", dict)
    if not os.path.exists(__db_teacher):
        dict = {}
        file_oper(__db_teacher, "wb", dict)


if __name__ == '__main__':
    init_database()  # 初始化数据库
    list_main = ["学生中心", "讲师中心", "学校中心", "退出"]
    list_school = ["创建班级", "招聘讲师", "创建课程", "返回"]
    list_teacher = ["查看班级", "查看班级学员列表", "返回"]
    list_student = ["学员注册", "返回"]
    start()