zoukankan      html  css  js  c++  java
  • python全栈开发中级班全程笔记(第三模块、第一章(多态、封装、反射、内置方法、元类、作业))

                  python全栈开发笔记第三模块

    第二部分:面向对象进阶

     

     一、类的多态与多态性(重点)

       1、多态:指的就是同一类事物有多种形态
    2、多态性,又称多态动态绑定,在继承背景下使用时,有时也称为多态性,
    是指不考虑实例类型的情况下,使用实例,分为静态实例(如:很多类型都可以使用“+”这个符号)与动态实例
    class Animal:                      # 定义动物的形态
        def run(self):                 # 定义走
            pass
    
    class People(Animal):                     # 定义人的形态
        def run(self):
            print("sey hello!")
    
    
    class Pig(Animal):                       # 定义猪的形态
    
        def run(self):
            print("sey hengheng")
    
    
    class Dog(Animal):                      # 定义狗的形态
        def run(self):
            print("sey wangwang")
    
    
    class Cat(Animal):
        def run(self):
            print("sey miaomiao!!")
    
    
    peo = People()
    pig = Pig()
    dog = Dog()           # 定义各自的形态
    cat = Cat()
    
    
    peo.run()
    pig.run()
    dog.run()             # 不考虑实例而使用实例
    
    还可更简单一步(拓展性)
    def func(name):     # 定义一个接口,
        name.run()
    
    
    func(peo)
    func(pig)
    func(dog)
    func(cat)
    View Code
    ***它的好处:
    ①、增强了程序的灵活性,使程序更简洁 func(name)去调用,但必须是多态化代码
    ②、增强程序的拓展性,如再增加一个猫类形态,不需要更改调用接口,只需增加猫类的属性和实例化方式。就可以使用
      3、其实 python 崇尚的并不只是上面的拓展性, python 崇尚的《鸭子类型》
    《鸭子类型》:考察一种动物,走路像鸭子,叫声像,造型像,形态也像鸭子,就被定义为鸭子(是一种概念)
    在类中:如果没有继承关系,只是模拟的像父类,那他就是父
    class File:                            # 定义文件类,实现读写方法
        def read(self):
            print("open's read")
    
        def write(self):
            print("open's write!")
    
    
    class Disk:                           # 定义磁盘类,实现读写方法
        def read(self):
            print("read disk")
    
        def write(self):
            print("disk is write!!")
    
    
    class Txt:                         # 定义文本档, 实现读写功能
        def read(self):
            print("txt is read")
    
        def write(self):
            print("write is txt")
    
    
    file = File()
    disk = Disk()
    txt = Txt()               # 把三类实例化
    
    
    def itea(name):         # 虽然三者没有继承关系,也不属于同一类, 但使用方法相同或相似,就可以定义个接口,以供调用
        name.read()
        name.write()
    
    
    itea(file)
    itea(disk)              # 只传进去各个的类名,在更简洁的代码下,就可以实现相同的功能
    itea(txt)
    
    # 这就是多态性的优势所在(可以为不同的类设置相同的调用方法(前提是:必须有相同的调用方式)
    View Code
        *#*#*#*#这就是多态性的优势所在(可以为不同的类设置相同的调用方法(前提是:必须有相同的调用方式)

    二、类的封装与封装意义
        封装:字面意思就是把某种物品或东西,通过隐藏和整合包装,使其更具可观性或可用性
       1、封装之隐藏属性
    class A:        # 隐藏属性的写法(字符前加上__)
        __ppx = 1      # _A__ppx = 1 (后台执行时的名称)
        def __init__(self, name, age):
            self.__name = name       # self._A__name = name
            self.__age = age         # self._A__age = age
    
        def __foo(self):            # _A__foo(self):
            print("run.__foo")
    
        def dir_name(self):        # 调用时,也自动更改了调用方式
            self.__foo()            # 后台执行的代码:self._A__foo()
            print("my is dir_name")
    
    
    a = A("will", 18)
    print(a.__ppx)                  # 报错发现不存在
    print(a.__name)
    print(a.__age, a.__sex)
    print(a.__foo)                   # 成功实现所有属性的隐藏
    
    print(A.__dict__)                # 通过名称空间查看内部发生了什么变化
    print(a.__dict__)
    a.dir_name()
    
    # 发现命名和类的名称空间结构已经发生变化,实现属性隐藏
    View Code
      发现命名和类的名称空间结构已经发生变化,实现属性隐藏
      它的特点:
        • 封装了类,使外部无法正常调用
        • 在类内部可以正常调用(它的作用主要对外封闭隐藏,但是内部可以正常调用)
        • 子类无法覆盖父类__开头的属性详情看下面例子

      例:

    class Foo:
        dog = "dog"
    
        def func(self):                 # 没有加 __的属性
            print("form is func")
    
        def __foo(self,):               # 加了 __的属性
            print("form foo")
    
    
    class Func(Foo):
        def __foo(self):
            print("form foo 111111")
    
    
    f = Func()
    f.func()      # 调用继承的不加的属性
    f.__foo()   # 调用加了__的属性(发现无法覆盖, 在定义类之初,类内部存在__的属性,就会使类的调用方法发生变化,所以,他不会覆盖父类的属性,只是增加了自己的属性)
    View Code
     *#*#*#*#*#*#
      综合以上,总结一:
              这种属性隐藏只是个障眼法(不会真正意义上达到隐藏,只不过在定义类时,自动检测语法后,更改了类名的调用方式)
       python 并不会真的限制,而只是更改了调用方式(_类名__属性),虽然这样也能访问到,但如果隐藏了还调用,
       那就不要去隐藏了,这种方法也没有意义

    class B:
        __xx = 1
    print(_B__xx)
        def __init__(self,  name):
            self.__name = name
    
    
    B.__y = "alex"
    b = B("will")
    b.__f = "f"
    print(B.__dict__)    # 发现在定义类体完成之后,再增加的属性不会被隐藏
    print(b.__dict__)
    print(b.__f)
    print(B.__y)          # 发现都可以正常调用
    View Code
         总结二 :这种变形只有在定义类体的时候发生一次改变。在有赋值操作的时候,是不会再改变的
          验证问题3:实现类体内部自己调用自己,不使用其他的同名变量函数
    # 验证问题3:实现类体内部自己调用自己,不使用其他的同名变量函数
    class K:
        def __foo(self):
            print("form is K foo")
    
        def bar(self):
            print("form is bar")
            self.__foo()
    
    
    class M(K):
        def __foo(self):
            print("form is M foo")
    
    
    m = M()
    m.bar()
    View Code
        总结三 : 在继承中,如果不想让子类覆盖自己的方法,可以将方法定义为私有
    *#*#*#*#*#*#

     2、封装的意义
      封装属性,分为 ① 封装数据属性 ② 封装与方法属性
    (1)
    封装数据属性:明确的区分内外,控制外部对隐藏属性的操作行为
    class People:                           # 定义一个类
        def __init__(self, name, age):      # 设置隐藏属性
            self.__name = name
            self.__age = age
    
        def _format(self):                  # 定义接口并把隐藏属性定义规则(使其他人无法修改)
            print("name:<%s>, age:<%s>" % (self.__name, self.__age))
    
        def alter(self, name, age):         # 为了方便使用, 定义一个修改名称接口,便于多人调用
            if not isinstance(name, str):   # 可以为其设置条件,
                print("input error! is not str")
                return
            else:
                self.__name = name
            if not isinstance(age, int):
                print("input error! is not int!")
                return
            else:
                self.__age = age
    
    
    p = People("will", 25)
    p._format()
    p.alter(4, 3)
    p._format()
    View Code
      (2)封装方法属性:隔离复杂度(把复杂的问题隔离,实现调用环节简单化)
    class ATM:
    
        def __plug(self):
            print("插卡")
    
        def __user(self):
            print("用户认证")
    
        def __money(self):
            print("取款金额")
    
        def __print(self):
            print("print bill")
    
        def __exit(self):
            print("exit card")
    
        def operate(self):
            self.__plug()
            self.__user()
            self.__money()
            self.__print()
            self.__exit()
    
    
    a = ATM()
    
    a.operate()
    View Code
     三、封装的可扩展性
        
    封装的可扩展性非常高,可以给用户定义该看到的东西和没必要看的东西,
    class Room:                                         # 定义一个房子类
        def __init__(self, name, owner, weight, length, height):        # 再次增加一个需求
            self.name = name
            self.owner = owner
            self.__weight = weight                      # 把计算面积的数值隐藏
            self.__length = length
            self.__height = height
    
        def area(self):                                # 后台计算方式封装成调用接口
            sum = int(self.__weight) * int(self.__length) * self.__height
            print("房间空间为 %sm³" % sum)
    
    
    r = Room("alex", "厨房", 10, 4, 5)
    
    r.area()                                           # 实现查看房屋面积功能(也实现了调用体积的功能)
    # 更改了内部属性,但没有更改调用方式, 这就是可扩展性
    View Code
       *#*#*#*#  更改了内部属性,但没有更改调用方式, 这就是可扩展性
    四、类的装饰器
     1、用一个计算成人 BMI 指数的方法简述装饰器的作用
    小于18.5 = 太轻
    18.5 - 23.9 = 正常
    24 - 27 = 过重
    28 - 32 = 肥胖
    大于 32 = 超胖

    体征指数(BMI) = 体重(kg)÷ 身高的2次方(米)
    class People:
        def __init__(self,name,  weight, height):
            self.name = name
            self.weight = weight
            self.height = height
    
        @property                       # 加上这个功能,相当于在调用时执行 p.bmi 等同于执行 p.bmi()
        def bmi(self):
            iminit = self.weight/(self.height ** 2)
            print("your's BMI %s" % iminit)
            return
    
    
    p = People("will", 100, 1.81)
    p.bmi                    # 发现 访问数据属性,还要加括号
    
    p.height = 1.88
    p.bmi
    
    
    # 小结: property 的封装方法主要应用于正常访问不加括号,但是后台又必须加括号才能执行的地方
    View Code
        *#*#*#*#*#*小结: property 的封装方法主要应用于正常访问不加括号,但是后台又必须加括号才能执行的地方
    2、
    property 的拓展功能
    class People1:
        def __init__(self, name):
            self.__name = name
    
        @property                   # 封装调用函数时不加括号
        def name(self):
            print("get name")
            return self.__name
    
        @name.setter                # 把 property 封装的函数当做装饰器调用, 修改装饰器
        def name(self, val):
            print("revise")
            if not isinstance(val, str):
                print("input error!!!")
                return
            else:
                self.__name = val
    
        @name.deleter             # 封装的删除装饰器
        def name(self):
            print("delete")
            print("not is delete!!")
    
    
    p = People1("will")
    
    p.name = "pps"
    print(p.name)                   # 成功实现查询、修改、删除的属性
    del p.name
    View Code

    五、绑定与非绑定方法的介绍与使用
        在类内部定义的函数分为 2 大类:绑定方法 与 非绑定方法
    1、绑定方法:绑定方法绑定给谁,就由谁来调用,谁调用就会把谁当做第一个参数自动传入
      ① 绑定到对象的方法:在类内部定义且没有被装饰器修饰的函数
       ② 绑定到类的方法:在类内部定义且被装饰器 @cllassmethod 修饰的函数

    2、非绑定方法:
        非绑定方法是指:既不与类绑定也不与对象绑定,也没有自动传值,对象和类都可使用,这就涉及到 @staticmethod 方法
    class Atm:
        """验证绑定对象方法"""
        def __init__(self, name):
            self.name = name
    
        def tell(self):
            print("name is %s" % self.name)
    
    
    f = Atm("will")
    print(Atm.tell)                 # 类的函数类型
    print(f.tell)                   # 绑定对象的方法
    
    
    class Atm:
        """验证绑定到类的方法"""
    
        @classmethod                # 新的语法,在类内部调用类并自动传值
        def func(cls):
            print(cls)
    
    
    print(Atm.func)                 # 发现调用并绑定了自己
    print(Atm)
    
    
    
    class Atm:
        """非绑定方法的额验证"""
        def __init__(self, name):
            self.name = name
        @staticmethod                   # 非绑定方法的调用
        def tell(x,y):
            print(x+y)
    
    
    f = Atm
    f.tell(3, 5)           # 绑定给对象
    Atm.tell(5, 8)         # 类自己调用
    View Code
      3、绑定方法与非绑定方法的使用方法与使用场景
    # 定义一个类,查看每个人的用户信息
    class People:
        def __init__(self, name, age, sex):
            self.id = self.class_id()               # 高深之处,先绑定类内的函数再定义函数
            self.name = name
            self.age = age
            self.sex = sex
    
        def tell_info(self):            # 绑定到对象的方法
            """绑定对象方法"""
            print("name:%s age:%s sex:%s" %(self.name, self.age, self.sex))
    
        @classmethod
        def form_conf(cls):     # 需求升级,从文件里导入资料并和类绑定匹配
            """绑定类的方法"""
            obj = cls(sett.name,
                  sett.age,
                  sett.sex)
            return obj
    
        @staticmethod
        def class_id():                # 非绑定参数的 实现(独立的不需要传值,也不需要依靠别的)
            m = hashlib.md5(str(time.time()).encode("utf-8"))
            return m.hexdigest()
    View Code
    六、反射:通过字符串,映射到对象属性
    # 定义一个类
    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def prints(self):
            print("name %s age is %s" % (self.name, self.age))
    
    
    obj = People("will", 22)
    obj.prints()
    View Code
     1、映射需要掌握的知识点:
      ①
    hasattr: hasattr(obj(对象),"name"(字符串形式的属性)) 判断属性(布尔类型返回 True 或 False )
    # 1、 hasattr(obj(对象),"name"(字符串形式的属性)) 判断属性(布尔类型返回 True 或 False )
    print(hasattr(obj, "name"))         # 查看对象内部的名称空间(__dict__)内有没有对应的 key
    print(hasattr(obj, "prints"))       # 或者说查看绑定类的名称空间内有没有定义或对应的变量名
    View Code
        ② getattr:getattr(obj(对象), "name"(属性), None(如果查找不到,需返回的值,不填会报错))  获取对象名称空间内的属性值
    print(getattr(obj, "name", "ppt"))          # 找得到,返回对应值,
    print(getattr(obj, "namexxx", "ppt"))       # 找不到,返回最后的值(ppt)
    print(getattr(obj, "prints", "ppt"))        # 查找类内属性会返回对应的绑定方法的函数
    View Code
       ③ steattr:setattr(obj(对象), "sex"(要增加的属性名(key)),"man"(属性名对应的值(value)) )修改属性方法
    print(setattr(obj, "sex", "man"))          # 等同于 obj.sex = "man"
    print(obj.sex)
    View Code
       ④ delattr : delattr(obj(对象), "age"(要删除的属性名))
    delattr(obj, "age")     # 等同于 del obj.age
    print(obj.__dict__)     # 发下已经被删除
    View Code
      *#*#*#*#*#*# 小结:以上4种方法,也同样适用于类,可以达到类和对象的增删改查效果,
    2、以上知识点的应用场景:
    # 这几个知识的应用广场景
    class Service:
        """反射的应用场景"""
        def run(self):
            while True:
                inp = input(">>>:").strip()
    
                cmds = inp.split()
                if hasattr(self, cmds[0]):       # 怎么通过判断用户输入的正确度去执行代码
                    func = getattr(self, cmds[0])
                    func(cmds)
        def get(self, cmds):
            print("get 功能", cmds)
    
        def put(self, cmds):
            print("put.....", cmds)
    
    
    # 通过 hasattr 和 getattr 两个功能实现属性准确度判断
    s = Service()
    s.run()
    
    
    # 需求升级,想通过输入的字符执行相对功能并下载
    # 通过相同方法,把输入的字符串格式化成列表,实现最终目的
    View Code
    七、面向对象常用的内置方法
    1、item 系列:
    (__setitem__, __getitem__, __delitem__)把对象模拟成调用字典的形式操作
    # 一、item系列:(__setitem__, __getitem__, __delitem__)把对象模拟成调用字典的形式操作
    class People:
        def __init__(self, name):
            self.name = name
    
        def __getitem__(self, item):
            """get:获取属性(查询时会用到)"""
            return self.__dict__.get(item)              # 设置方法返回调用所获值
    
        def __setitem__(self, key, value):
            """set:修改与增加属性(修改和增加时会用到)"""
            # print("setitem")
            # print(key, value)
            self.__dict__[key] = value                     # 实现真正操作的一步
    
        def __delitem__(self, key):
            """del:删除属性(删除时用得到)"""
            print("delitem")
            print(key)
            self.__dict__.pop(key)                          # 删除操作
            del self.__dict__[key]                        # 效果同上
    
    obj = People("will")
    
    # 验证get获取和查看属性
    print(obj["name"])
    obj["name"]
    
    
    # 验证set修改与增加设置方法
    # 需求,增加一个属性
    obj.age = 18         # 之前的方法可以这样写
    obj["sex"] = "man"  # 用字典的方法这样写也可以实现
    print(obj.sex)
    print(obj.__dict__)
    
    
    # 验证 del 删除属性
    del obj["name"]
    print(obj.__dict__)
    View Code
    
    
    
      2、__str__方法:  设置对象返回类型,使其转换成str类型,实现定制化
    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __str__(self):
            """调用对象时,设置返回类型为 str
               需要注意的是:必须以字符串类型返回,否则会报错"""
            return "hi !! my name's %s. age %s" % (self.name, self.age)
    
    
    p = People("will", 22)              # 实例化后
    print(p)                    # 打印对象时, 会触发 __str__  系统自动识别并调用类内的 __str__ 属性
    View Code
     3、 __del__方法:在调用结束后,自动释放内存
    class Open:
        def __init__(self, filename):
            print("读取文件.......")
            self.filename = filename
    
        def __del__(self):
            """这个方法会在程序结束后,自动触发并释放内存
               这里面一般会写入跟对象相关的其他程序或资源"""
            print("__del__资源回收")
    
    
    f = Open("sett.py")         # 实例化
    del f       # 如果提前删除 变量,也会触发类内定义的 __del__操作
    #            (一般定义了__del__也不会 刻意去删除,只是验证下)相比手动操作当然还是自动删除比较好
    print("__main__")   # 打印或者再干点其他事,发现类内部定义的 __del__在程序最后才运行,证明是执行的最后一波操作
    
    # 小结:如果在类内部定义了 __del__ 方法,所有类内部定义的属性,都是自动绑定给对象的,
    #       这个方法在对象被删除的时候,会自动先触发这个方法的执行
    View Code
     4、其他内置方法:
      (1) isinstance(obj, class):检测 obj 是否为 class 的对象
      (2) issubclass(sub, super):检测 sub 是否为 super 的派生类/子类
    (3)描述符(__grt__, __set__, __delete__):描述符是新式类中(__grt__, __set__, __delete__)至少实现一个, 也被称为描述协议
       ① __get__() :调用一个属性时触发
        ② __set__() : 为一个属性赋值时触发
        ③ __delete__() : 用 del 删除属性时触发

    八、元类的介绍:
    1、exec 方法的使用
      (1)exec 方法的三个特性
        ① 执行字符串的命令
        ② 全局作用域(字典形式),如果不指定,默认使用 globals()
        ③ 局部作用域(字典形式),如果不指定,默认使用 locals()
    g = {
        "x": 1,
        "y": 2}
    
    
    f = {}
    
    
    # exec :把 exec 内的字符串当做函数执行命令,可修改全局作用域的变量,也可创建局部作用域变量
    exec("""global x, s
    x = 15
    s = 33
    
    z = 3
    """, g, f)      # exec(要执行的命令(字符串形式), 执行命令的作用域,g = 全局, f = 局部)
    
    # print(g)
    print(f)
    View Code
      2、一切皆对象,对象的用法
    ① 都可以被引用,x = obj(函数或类名都可以被当做一个变量值)
    ② 都可以当做函数的参数传入,
    ③ 都可以当做函数的返回值,
    ④ 都可以当做容器的元素(如:f = [func, class, item, obj]列表内的值,字典内的值,元祖内的值, 集合内的值等等...)
    *#*#*#*# 一切皆对象的定义,就是以上四个点,反过来看,符合以上4个点的要求,就是对象,类也是对象,对象本身也是对象
    class People:
        pass
    
    
    p = People()
    print(type(p))
    print(type(People))
    
    print(type(print))
    View Code
    
    
      3、元类的定义方法
       实践证明,所有用 class 定义的类,都叫"type"类型
       能查询类型的 type()这一种类型,也是一个类,默认所有用 class 定义的类,它们的元类都是 type
      定义类的方式有 2 种:
    (1)、class 定义的类
    # 1、class 定义的类
    
    class Chinese:
        """之前定义类的方法"""
        country = "china"
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print("%s is talking" % self.name)
    
    
    obj = Chinese("will", 18)       # 实例化
    print(obj, obj.name, obj.age)
    
    print(Chinese)
    View Code
       (2)、用 type 产生的类, 且不用 class 定义(需要符合定义三要素)
      ① 有类名
      ② 基类,如果继承就会有一个或多个,如果不继承则默认继承 object
      ③ 类的名称空间
    # 定义类的三要素
    class_name = "Chinese"    # 三要素之一:类名
    class_bases = (object, )     # 三要素之二:基类,如果继承,会有一个或多个,如果不继承,会有一个默认值( object )
    class_body = """         
    country = "china"
    
    def __init__(self, name, age):
        self.name = name
        self.age = age
    
    def talk(self):
        print("%s is talking" % self.name)
    """
    
    class_dict = {}
    class_room = exec(class_body, globals(), class_dict)    # 三要素之三(重点):类的名称空间
    # print(class_dict)                           # 打印验证效果发现名称空间已经生成
    Chinese1 = type(class_name, class_bases, class_dict)
    
    # print(Chinese1)
    obj1 = Chinese1("will", 18)       # 实例化
    print(obj1, obj1.name, obj1.age)
    View Code
      *#*#*#*#*#*# 小结:
                一切皆对象:以前定义的所有变量对应的值,都是对象,包括用 class 定义的类,还有对象本身,也是对象。
                不用 class 定义类
    用type 定义类需要三要素 ① 类的名称,② 类的基类参数,③ 类的名称空间
      4、自定义元类控制类的创建行为与抛异常的解析
      (1)通过定义元类,控制类的行为
     如果定义一个父类,
    class My_mete(type):
        """自定义元类,也需要继承 type """
        def __init__(self, class_name, class_bases, class_dict):            # 定义 __init__ 就要继承之前基类所拥有的功能
            if not class_name.istitle():            # 在元类内部,可以定义其他框架(判断如果类名的首字母不是大写,触发报错机制)
                raise TypeError("类名的首字母必须大写,")
            elif "__doc__" not in class_dict or not class_dict["__doc__"].strip():      # 定义元类的其他内置方法
                raise TypeError("定义类,必须注释且注释不能为空")
            super(My_mete, self).__init__(class_name, class_bases, class_dict)      # 所以,需要继承基类所拥有的功能且增加自己创建的功能
    
    
    class Chinese(object, metaclass=My_mete):      # 不写继承的基类,也有默认继承,后面的 metaclass = type 也是默认值
        """这样写,相当于 Chinese = My_mite(class_name, class_bases, class_dict),也就相当于基类内必须有 __init__ 的方法"""
        country = "china"
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print("%s is talking" % self.name)
    View Code
        (2) raise 抛异常语法
        平时都是报错了才发现有异常,而用此方法之后,可以主动抛异常(就是不报错也可以出发异常)
    class Chinese(object, metaclass=My_mete):      
        country = "china"
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print("%s is talking" % self.name)
    
    
    print(Chinese.__dict__)
    View Code
     
    5、义元类控制类的实例化行为与 __call__ 详解
      (1) __call__方法
    # 本节知识储备点:__call__ 方法
    class Foo:
        def __call__(self, *args, **kwargs):        # 定义函数传参的功能(所有类型)
            """定义此方法后,对象名加括号(obj()),也可以被调用"""
            print(self)
            print(args)
            print(kwargs)
    
    
    obj = Foo()
    obj(1, 2, 10, a=1, b=2, c=5)  # obj() 加括号,也可以调用,需要在类内部增加 __call__ 函数,
    # 等同于调用 obj.__call__(obj, 1, 2, 10, a=1, b=2, c=5) 的调用方法
    # 用这样的方法,就可以把对象变成一个可调用对象,且可修改调用对象方法
    # 元类内部也有同样的内置方法(__call__)可被调用和控制,
    # 会在创建类时,自动生成 __call__ 的方法,以备调用并触发,
    # 如果调用 Foo(1, 2, a=2, b=4),相当于执行了 Foo.__call__(self, 1, 2, a=2, b=4) 的方法
    View Code
        (2)通过定制元类,控制类的实例化行为
    # 同理以下行为就用到了__call__的方法
    class My_mete(type):
        """自定义元类,也需要继承 type """
        def __init__(self, class_name, class_bases, class_dict):
            if not class_name.istitle():
                raise TypeError("类名的首字母必须大写,")
            elif "__doc__" not in class_dict or not class_dict["__doc__"].strip():
                raise TypeError("定义类,必须注释且注释不能为空")
    
            super(My_mete, self).__init__(class_bases, class_bases, class_dict)
    
    
        def __call__(self, *args, **kwargs):
            """本方法默认就有,但是,自定义了会按照自定义方法执行,
              这样就可以在自定义内部增加自定义的任务与格式实现自定义功能"""
            """后台操作"""
            print(self)         # self = Chinese
            print(args)         # args = ("will", )
            print(kwargs)       # kwargs = {"age": 22}
            # 在被调用时,会触发此方法执行三件事
            #   1、先造出一个新对象 obj(在之前学的 object 方法有一个功能)
            obj=object.__new__(self)         # 新建对象的语法,会提示传进去 一个类名
            #   2、初始化obj(触发 __init__() 方法)
            self.__init__(obj, *args, **kwargs)
            #   3、返回obj
            return obj
    
    
    class Chinese(object, metaclass=My_mete):
        """这样写,相当于 Chinese = My_mite(class_name, class_bases, class_dict),
           也就相当于基类内必须有 __init__ 的方法"""
        country = "china"
    
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def talk(self):
            print("%s is talking" % self.name)
    
    
    obj = Chinese("will", age=22)     # 同理:此操作相当于调用 Chinese.__call__(Chinese, "will", 22)
    print(obj.__dict__)     # 验证自定义元类的实例化功能,轻松实现控制实例化过程
    View Code
    
    
    
      6、元类控制类实例化行为的应用场景与单利模式的详解
       (1)单例模式(设计模式的一种):如果执行内容和结果一样,就会只有一个内存地址()属于内存优化
        新的需求,不要重复调用相同内容的值创建新的内存地址造成空间浪费
    # 实现方式一:正常实现
    class My_SQL:
        """SQL 是一个访问和处理数据库的计算机语言"""
        __instance = None
    
        def __init__(self):
    
            self.host = "127.0.0.1"
            self.port = 3306
    
        @classmethod
        def singleton(cls):
            """为了节省空间,可以判断实例化如果是同一个内容,可以覆盖
               此处需要用到 装饰器@classmethod """
            if cls.__instance is None:
                obj = cls()
                cls.__instance = obj
            return cls.__instance
    
        def conn(self):
            pass
    
        def execute(self):
            pass
    
    
    obj1 = My_SQL.singleton()
    obj2 = My_SQL.singleton()
    obj3 = My_SQL.singleton()
    print(obj1, obj2, obj3)
    
    # 通过此方法,实现了单例行为,这样的方式是处理 普通类加装饰器实现此功能,下面用元类控制此功能
    View Code
        (2)元类控制实例化的应用
    # 方式二:元类实现此功能
    
    class My_mete(type):
        """自定义元类,也需要继承 type """
        def __init__(self, class_name, class_bases, class_dict):
            if not class_name.istitle():
                raise TypeError("类名的首字母必须大写,")
            elif "__doc__" not in class_dict or not class_dict["__doc__"].strip():
                raise TypeError("定义类,必须注释且注释不能为空")
            super(My_mete, self).__init__(class_bases, class_bases, class_dict)
            self.__instance = None
    
        def __call__(self, *args, **kwargs):
            if not self.__instance:
                obj = object.__new__(self)
                self.__init__(obj)
                self.__instance = obj
            return self.__instance
    
    
    class Mysql(object, metaclass=My_mete):
        """用元类实现此功能就简单多了"""
    
        def __init__(self):
    
            self.host = "127.0.0.1"
            self.port = 3306
    
        def conn(self):
            pass
    
        def execute(self):
            pass
    
    
    obj1 = Mysql()
    obj2 = Mysql()
    obj3 = Mysql()
    
    print(obj1 is obj2 is obj3)    # 通过自定义元类,轻松实现了重复开辟地址的行为,节省了空间
    View Code
    
    
    
      7、有关元类的小作业
      作业要求:
          (1)、通过自定义元类属性,把自定义类的属性全部大写
    # 第一题:
    # 1、通过自定义元类属性,把自定义类的属性全部大写
    
    
    class Mymete(type):
        """判断派生类所有属性必须都要大写"""
        def __init__(self, class_name, class_bases, class_dict):
            if not class_name.isupper():
                raise TypeError("定义类名,必须要大写")
            for attr in class_dict:
                if not (attr.startswith("__") and attr.endswith("__")) and not attr.isupper():
                    """判断不是以 __开头和 __结尾的属性,都必须大写,否则抛出错误"""
                    raise TypeError("所有属性都必须要大写!!!")
            super(Mymete, self).__init__(class_name, class_bases, class_dict)
    
    
    class CHINESE(object, metaclass=Mymete):
        """已完成元类定制 子类所有属性全部大写"""
        DDD = "QQQ"
        def __init__(self, name, age, sex):
            self.name = name
            self.age = age
            self.sex = sex
    
        def DDD(self):
            print(self.name)
            print(self.age)
            print(self.sex)
    
    
    
    # 老师的答案:
    class Mymetaclass(type):
        def __new__(cls,name,bases,attrs):
            update_attrs={}
            for k,v in attrs.items():
                if not callable(v) and not k.startswith('__'):
                    update_attrs[k.upper()]=v
                else:
                    update_attrs[k]=v
            return type.__new__(cls,name,bases,update_attrs)
    
    class Chinese(metaclass=Mymetaclass):
        country='China'
        tag='Legend of the Dragon' #龙的传人
        def walk(self):
            print('%s is walking' %self.name)
    
    
    print(Chinese.__dict__)
    '''
    {'__module__': '__main__',
     'COUNTRY': 'China',
     'TAG': 'Legend of the Dragon',
     'walk': <function Chinese.walk at 0x0000000001E7B950>,
     '__dict__': <attribute '__dict__' of 'Chinese' objects>,
     '__weakref__': <attribute '__weakref__' of 'Chinese' objects>,
     '__doc__': None}
    '''
    View Code
      (2)、在元类中控制自定义的类不需要使用 __init__ 方法
    ①:元类帮其完成初始对象,以及初始化操作
    ②:要求实例化使传入参数必须是关键字参数,否则,抛出异常:TypeError:must use keyword argument
    ③:key 作为自定义类产生对象的属性,且所有属性变成大写
    # 第二题:
    
    class Mymetaclass(type):
        def __new__(cls,name,bases,attrs):
            update_attrs={}
            for k,v in attrs.items():
                if not callable(v) and not k.startswith('__'):
                    update_attrs[k.upper()]=v
                else:
                    update_attrs[k]=v
            return type.__new__(cls,name,bases,update_attrs)
    
        def __call__(self, *args, **kwargs):
            if args:
                raise TypeError('must use keyword argument for key function')
            obj = object.__new__(self) #创建对象,self为类Foo
    
            for k,v in kwargs.items():
                obj.__dict__[k.upper()]=v
            return obj
    
    class Chinese(metaclass=Mymetaclass):
        country='China'
        tag='Legend of the Dragon' #龙的传人
        def walk(self):
            print('%s is walking' %self.name)
    
    
    p=Chinese(name='egon',age=18,sex='male')
    print(p.__dict__)
    View Code
    
    
    
     九、异常处理

    1、
    什么是异常:异常是程序发出的错误信号,一旦程序出错且没有处理这个错误,系统就会抛出异常且程序 运行终止
    异常分为三部分:
    1、异常追踪信息:Traceback(会显示那里出错并定位)
    2、异常的类型:ValueError
    3、异常的内容及错误的说明 
    # print("1")
    # print("2")
    # int("ppr")
    View Code
    2、错误分类:
      分为 2 部分:

    (1)、语法错误:这个没的说,就是写程序的时候 没按照 python 约束的语法执行,就会报错,这样只能排查语法
    例:
    if 1>2
    # 1不可能大于二,所以是语法书写层次的错误
    View Code
      (2)、逻辑错误:逻辑错误又分为以下几种
    # ValueError
    # int("aaa")
    
    # NameError
    # name
    
    # IndexError
    # f = [1, 2, 3]
    # f[100]
    
    # KeyError
    # d = {}
    # d["name"]
    
    # AttributeError
    # class Foo:
    #     pass
    # Foo.xxx
    
    # ZeroDivisionError
    # 1/0
    
    # TypeError
    # for i in 55:
    #     print(i)
    
    
    # AttributeError 试图访问一个对象没有的属性,比如foo.x,但是foo没有属性x
    # IOError 输入/输出异常;基本上是无法打开文件
    # ImportError 无法引入模块或包;基本上是路径问题或名称错误
    # IndentationError 语法错误(的子类) ;代码没有正确对齐
    # IndexError 下标索引超出序列边界,比如当x只有三个元素,却试图访问x[5]
    # KeyError 试图访问字典里不存在的键
    # KeyboardInterrupt Ctrl+C被按下
    # NameError 使用一个还未被赋予对象的变量
    # SyntaxError Python代码非法,代码不能编译(个人认为这是语法错误,写错了)
    # TypeError 传入对象类型与要求的不符合
    # UnboundLocalError 试图访问一个还未被设置的局部变量,基本上是由于另有一个同名的全局变量,
    # 导致你以为正在访问它
    # ValueError 传入一个调用者不期望的值,即使值的类型是正确的
    View Code
    
    
    
    3、异常处理
      强调一、对于错误发生的条件如果是可以预知的,此时应该用 if else 判断,去预防异常
    age = 10
    a = input(">>>:").strip()
    if a.isdigit():
        a = int(a)
        if age > a:
            print("")
    View Code

    强调二、对于错误发生的条件如果不能预知的,此时应该用到异常处理机制, try...except
       需求:如果读文件,不知 到文件 有多少行,且没有更先进的方法 和机制,就要用到抛异常了
    try:
        f = open("exec.txt", "r", encoding="utf-8")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")         # 正常情况下是会报错的
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        f.close()
    
    except StopIteration:
        print("报错了")
    
    print("++++>>>1")
    print("++++>>>2")
    print("++++>>>3")
    
    
    # 这样情况下,就不会报错了
    View Code
     4、异常处理之多分支:用多分支处理异常的场景:代码被检测的代码块报出的异常有多种可能性,且需要针对每一种异常类型都定制处理逻辑
    # 一、异常处理之多分支
    
    try:
        print("++++>>>1")
        # name
        print("++++>>2")
        l[1000]
        print("+++++>>3")
        d = {}
        d[name]
        print("++++++>>>4")
    
    except NameError as b:
        print("+++++>", b)
    except ImportError as c:            # 有点像elif 的分支结构
        print("+++++++>", c)
    except KeyError as p:
        print("+++++>", p)
    
    
    # 用多分支处理异常的场景:代码被检测的代码块报出的异常有多种可能性,且需要针对每一种异常类型都定制处理逻辑
    View Code
    
    
    
    5、万能异常:Exception
          Exception(包含所有异常类型的异常机制)不包括语法错误,因为语法错误本不应该在程序中存在  
     万能异常的使用场景: 被检测的代码块报出的异常有多种可能性,且针对所有的异常,只用一种异常处理逻辑,就用 Exception
    try:
        print("++++>>>1")
        name
        print("++++>>2")
        l[1000]
        print("+++++>>3")
        d = {}
        d[name]
        print("++++++>>>4")
    
    except Exception as b:
        print("异常", b)
    
    
    print("+++++>>>")
    View Code
    6、二者的合并使用
    try:
        print("++++>>>1")
        # name
        print("++++>>2")
        l[1000]
        print("+++++>>3")
        d = {}
        d[name]
        print("++++++>>>4")
    
    except NameError as b:
        print("+++++>", b)
    except ImportError as c:            # 有点像elif 的分支结构
        print("+++++++>", c)
    except KeyError as p:
        print("+++++>", p)
    
    except Exception as r:              # 在最后再用 万能异常处理不可预知的错误
        print("万能异常的执行", r)
    
    print("++++++>>>>0")
    View Code
    7、其他结构 
      (1) elxe 在 try 内的作用及使用场景
    try:
        print("++++>>>1")
        # name
        print("++++>>2")
        # l[1000]
        print("+++++>>3")
        d = {}
        # d[name]
        print("++++++>>>4")
    
    except NameError as b:
        print("+++++>", b)
    except ImportError as c:            # 有点像elif 的分支结构
        print("+++++++>", c)
    except KeyError as p:
        print("+++++>", p)
    
    except Exception as r:              # 在最后再用 万能异常处理不可预知的错误
        print("万能异常的执行", r)
    
    else:
        print("它的 else 应用场景是:发生异常,就不会执行此处")  # 在被检测的代码没有发生异常时,就要执行这里的代码
    print("++++++>>>>0")
    View Code
        (2) finally 的使用方法
       finally:不管检测代码有没有发生异常都会执行
    try:
        print("++++>>>1")
        name
        print("++++>>2")
        l[1000]
        print("+++++>>3")
        d = {}
        d[name]
        print("++++++>>>4")
    
    except NameError as b:
        print("+++++>", b)
    except ImportError as c:            # 有点像elif 的分支结构
        print("+++++++>", c)
    except KeyError as p:
        print("+++++>", p)
    
    except Exception as r:              # 在最后再用 万能异常处理不可预知的错误
        print("万能异常的执行", r)
    
    else:
        print("它的 else 应用场景是:发生异常,就不会执行此处")  # 在被检测的代码没有发生异常时,就要执行这里的代码
    finally:
        print("不管被检测的代码有没有异常,本方法都会正常执行!")
    print("++++++>>>>0")
    View Code
         finally 经常会应用在回收场景
    try:
        f = open("exec.txt", "r", encoding="utf-8")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")
        print(f.__next__(), end="")         # 正常情况下是会报错的
        print(f.__next__(), end="")
        # f.close()      # f.close()  写在这里,可能报错之后,不能回收资源。
    finally:            # 利用 finally 这样的写法,就可以有效的避免浪费内存资源
        f.close()
    
    print("++++>>>1")
    print("++++>>>2")
    print("++++>>>3")
    View Code
    8、主动触发异常(raise)的应用场景
    class People:
        def __init__(self, name, age, sex):             # 这样定义一个普通类,
            if not isinstance(name, str):
                raise TypeError("name not is str!!!")
            if not isinstance(age, int):
                raise TypeError("age not is int!!!")
            self.name = name
            self.age = age
            self.sex = sex
    
    
    # 在实例化时:
    p = People("will", 33, "man")    # 正常传值,是没问题
    
    # 主动触发异常
    p2 = People(55, "11", 12)   # 但是这样传值(潜规则的错误),也不会报错,但是,人的名字都是字符串形式,所以就用到了主动触发异常
    View Code
    9、自定义异常
    class MyException(BaseException):
        """自定义异常时,通常要继承系统异常"""
        def __init__(self, msg):
            super(MyException, self).__init__()
            self.msg = msg
    
        def __str__(self):
            """自定义异常,必须要有此格式,否则不能打印异常要求"""
            print("<%s>" % self.msg)
    
    raise TypeError("自定义异常的处理结果!!!")
    View Code
    10、断言 assert 
    # 断言(assert):代码分为 2部分的场景,且互相有依赖
    # 第一部分:(定义部分)
    info = {}
    info["name"] = "will"
    info["age"] = 22
    
    
    # 第二部分:(分析与判断部分)
    # 常用的写法:
    if "name" not in info:          # 分析
        raise KeyError("必须有 name 这个key!")
    if "age" not in info: 
        raise KeyError("必须有 age 这个 key!!!")
    
    if info["name"] == "will" and info["age"] > 18:     # 判断
        print("welcome! ! ")
    
    
    # 断言的写法:
    assert "name" in info and "age" in info
    """断言的应用就是符合硬性条件,如果不符合,直接报错"""
    
    if info["name"] == "will" and info["age"] > 22:
        print("welcome!!!!")
    View Code
    
    
    
         *#*#*#*#*#小结:异常处理常用在知道会有异常,又无法预知发生错误的具体条件,且又不能让代码系统崩溃。如果 try 和 except 用多了密密麻麻的会显得效果非常差
    十、面向对象软件开发基础流程与结课作业讲解
     1、简
    单介绍面向对象软件开发的基本流程与注意事项:
         软件开发其实是一整套的规范,学习的只是一小部分,一个完整的开发工程,需要明确每个阶段的任务,
         在保证一个阶段正确的前提下,在进行下一个阶段的工作,这种行为称之为软件工(1)、面向对象分析(OOA): 学完面向对象不要上去就是写代码,要有更深层次的分析与大局框架意识      软件工程中的系统分析阶段,需要分析员与用户在一起,对用户的需求做出精确的分析和明确的表述,从大的方面解析软件系统应该做些什么,而不是怎么去去做。
              面向对象的分析要按照 面向对象的概念与方法,在对任务的分析中,从客观存在的事物和事物之间的关系,归纳出有关的对象(对象的特征和技能),
         以及对象之间的联系,并将具有相同属性和行为的对象用一个类 class 来标识。



    (2)、面向对象设计(OOD):根据面向对象分析阶段形成的需求模型,对每个部分 分别进行具体设计
        1、首先是类的设计,类的设计可能包含多个层次(利用继承与派生的机制)。然后以这些类为基础,提出程序设计的思路和方法,包括对算法的设计。
        2、在设计阶段并不牵涉任何一门具体的计算机语言,而是用一种更通用的描述工具(如:伪代码或流程图)来描述



    (3)、面向对象编程(OOP):根据面向对象设计的结果,选择一种计算机语言把他写成程序,可以是 python。

    (4)、面向对象测试(OOT):在写好程序交给用户使用之前,必须对程序进行严格测试。
        1、测试的目的是发现程序中的错误并修正它。
        2、面向对象的测试:是用面向对象的方法进行测试,以类作为测试的基本单元。

    (5)、面向对象的维护(OOSM)

    2、作业
    # -*- coding:utf-8 -*-
    # author:Will Loong
    
    # 本章节作业:
    # 角色:学校,学生,课程,讲师
    # 要求:
    # 1、创建北京、上海2个学校
    # 2、创建 linux、python、go 三种课程,linux在北京开、go 在上海开
    # 3、课程包括:周期、价格、通过学校创建课程
    # 4、通过学校创建班级,班级关联课程、讲师
    # 5、创建学时、选择学校,关联班级
    # 6、创建讲师角色时要关联学校
    #    提供 2 种接口
    # 6.1、学员视图:可以注册、交学费、选班级
    # 6.2、讲师视图,讲师可以管理自己的班级,查看班级学员列表,修改所管理的学员成绩
    # 6.3、管理视图,创建讲师,创建班级,创建课程
    # 7、上面的操作产生的数据通过 pickle 序列化保存到文件里
    
    
    
    # 作业分析:
    # 1、场景、角色、(类) ———> 属性、方法
    # ①、课程类:课程没有具体的动作和方法,所以只需要设计属性
    # 属性:课程名、周期、老师
    
    # ②、学生类:
    # 属性:姓名、对应课程
    # 方法及流程:查看可选课程、选择对应课程、查看所选课程、退出程序
    
    # ③、管理员:
    # 属性:姓名
    # 方法:创建课程、创建学生账号、查看所有课程信息、查看所有学生信息、查看所有学生的选课情况、退出程序
    
    
    # 2、站在每个角色的角度上去思考程序执行流程
    import os
    import sys
    import pickle
    from color import font_color
    
    # class Pea():
    #     while True:
    #         try:
    #             with open("student_info", "rb") as f8:
    #                 cde = pickle.load(f8)
    #             print(cde.name)
    #         except EOFError:
    #             print("aaa")
    # p = Pea()
    # print(p)
    class Course:
        """定义课程类"""
        def __init__(self, name, price, period, teacher):
            """定义属性并初始化赋值"""
            self.name = name
            self.price = price
            self.period = period
            self.teacher = teacher
    
    
    class Person:
        """把打印课程提取出来,创建一个父类"""
        def show_course(self):
            """查询课程"""
            with open("course_info", "rb") as f:
                count = 0
                while True:
                    try:
                        count += 1
                        course_obj = pickle.load(f)
                        font_color("%s.%s-%s-%s-%s" % (count, course_obj.name, course_obj.price, course_obj.period,
                                                       course_obj.teacher), "noting")
                    except EOFError:
                        break
    
    
    class Student(Person):
        """创建学生类"""
        operate_lst = [("查看可选课程", "show_course"),
                       ("选择课程", "select_course"),
                       ("查看已选课程!", "check_selected_course"),
                       ("退出", "exit")]
    
        def __init__(self, name):
            """一个学生不一定只有一门课程,所以在定义学生课程时,格式化一个列表"""
    
            self.name = name
            self.course_name = []
    
        def select_course(self):
            self.show_course()
            num = int(input("Number >>>:").strip())
            count = 1
            with open("course_info", "rb") as f:
                while True:
                    try:
                        course_obj = pickle.load(f)
                        if count == num:
                            self.course_name.append(course_obj)
                            print(len(self.course_name))
                            font_color("You chose the %s course !!!" % course_obj.name, "true")
                            break
                        count += 1
                    except EOFError:
                        font_color("there is not course!!!", "error")
                        break
    
        def check_selected_course(self):
            """查看已选课程"""
            print(self.course_name)
            for cou in self.course_name:
                print(cou.name, cou.teacher)
    
    
        def exit(self):
            """把写入的文件创建在最后,如果修改完了退出,再写入文件"""
            with open("student_info", "rb") as f1,  open("student_info_back", "wb") as f2:
                """在内存打开2个文件,f1, f2"""
                while True:
                    try:
                        student_f = pickle.load(f1)
                        if student_f.name == self.name:
                            """如果相等,就把修改后的文件存入 f2 文件"""
                            pickle.dump(self, f2)
                        else:
                            """否则,原内容保持不变"""
                            pickle.dump(student_f, f2)
                    except EOFError:
                        break
            os.remove("student_info")
            os.rename("student_info_back", "student_info")
            exit()
    
        @staticmethod
        def init(name):
            """返回一个学生对象,在student内"""
            with open("student_info", "rb") as f:
                while True:
                    try:
                        stu_obj = pickle.load(f, encoding="utf-8")
                        if stu_obj.name == name:
                            return stu_obj
                    except EOFError:
                        font_color("学生账号不存在!!!", "error")
                        break
    
    
    class Manager(Person):
        """创建管理员"""
        operate_lst = [("创建课程", "create_course"),
                       ("创建学生账号", "create_student"),
                       ("查看所有课程", "show_course"),
                       ("查看所有学生", "show_student"),
                       ("查看所有学生选课情况", "show_student_course"),
                       ("退出", "exit")]
    
        def __init__(self, name):
            self.name = name
    
        def create_course(self):
            """创建课程"""
            name = input("course name:").strip()
            price = input("course price:").strip()
            period = input("course period:").strip()
            teacher = input("course teacher:").strip()
            course_obj = Course(name, price, period, teacher)
            with open("course_info", "ab") as f:
                pickle.dump(course_obj, f)
            font_color("%s 课程创建完毕!" % course_obj.name, "yes")
    
        def create_student(self):
            """创建学生账号:
            1、用户名和密码以及身份记录到 user_info 文件内
            2、将学生对象存进student 文件"""
            stu_name = input("student name:")
            if isinstance(stu_name.strip(), str) is not None:
                stu_pwd = input("student password:").strip()
                if isinstance(stu_pwd.strip(), (str or int)) is not None:
                    stu_pwd2 = input("student password:").strip()
                    if stu_pwd2 == stu_pwd:
                        stu_auth = "%s|%s|Student" % (stu_name, stu_pwd2) + "
    "
                        stu_obj = Student(stu_name)
                        with open("user_info.txt", "a", encoding="utf-8") as f:
                            f.write(stu_auth)
                        with open("student_info", "ab") as f:
                            pickle.dump(stu_obj, f)
                        font_color("%s学生账号创建成功!!!" % stu_obj.name, "yes")
                    else:
                        font_color("2次密码输入不一致!!!", "error")
            else:
                font_color("学生账号必须是 str 格式!!!", "error")
    
        def show_student(self):
            """查询学生"""
            with open("student_info", "rb") as f:
                count = 0
                while True:
                    try:
                        count += 1
                        student_obj = pickle.load(f, encoding="utf-8")
                        font_color("%s.%s" % (count, student_obj.name), "yes")
                    except EOFError:
                        break
                font_color("-----------end------------", "false")
    
        def show_student_course(self):
            with open("student_info", "rb") as f:
                while True:
                    try:
                        student_obj = pickle.load(f)
                        course_list = [cor for cor in student_obj.courses]
                        font_color("%s.opt course %s " % (student_obj.name, "|".join(course_list)), "false")
                    except EOFError:
                        break
    
        @classmethod
        def init(cls, name):
            return cls(name)
    
        def exit(self):
            exit()
    
    
    # 学生角度:登录就可以选课了
    #             已经有账号,已经有课程
    
    
    # 管理员:登录就可以完成以下操作:
    #         学生账号由管理员创建
    #         学生课程由管理员创建
    
    
    # 应该先创建管理员角色更合适着手开发
    # 登录需求:
    #           必须自动识别身份(存在文件内的信息:账号、密码、身份)
    
    
    
    def login():
        user_name = input("username:").strip()
        passwod = input("password:").strip()
        with open("user_info", "r", encoding="utf-8") as f:
            for line in f:
                user, pwd, identify = line.strip().split("|")
                if user == user_name and passwod == pwd:
                    return {"state": True, "name": user_name, "id": identify}
            else:
                return {"state": False,  "name": user_name}
    
    log_res = login()
    if log_res["state"]:
        font_color("登录成功!!!", "yes")
        if hasattr(sys.modules[__name__], log_res["id"]):
            cls = getattr(sys.modules[__name__], log_res["id"])
            obj = cls(log_res["name"])
            while True:
                for idx, nature in enumerate(cls.operate_lst, 1):
                    font_color("%s.%s" % (idx, nature[0]), "noting")
                inp = input("please>>>:").strip()
                if inp.isdigit() and len(cls.operate_lst) >= int(inp):
                    func_str = cls.operate_lst[int(inp) - 1][1]
                    if hasattr(obj, func_str):
                        getattr(obj, func_str)()
                else:
                    font_color("No such option!!!", "error")
    else:
            font_color("登录失败!!!", "error")
    View Code
    *****老师讲解 
    # -*- coding: utf-8 -*-
    # @Time    : 2018/8/31 10:59
    # @Author  : 骑士计划
    # @Email   : customer@luffycity.com
    # @File    : 5.作业讲解.py
    # @Software: PyCharm
    
    import os
    import sys
    import pickle
    
    student_info = 'student_info'
    course_info = 'course_info'
    userinfo = 'userinfo'
    class Base:
        def __str__(self):
            return self.name
    class Course(Base):
        def __init__(self,name,price,period,teacher):
            self.name = name
            self.price = price
            self.period = period
            self.teacher = teacher
        def __repr__(self):
            return ' '.join([self.name, self.price, self.period, self.teacher])
    
    class Person:
        @staticmethod
        def get_from_pickle(path):
            with open(path,'rb') as f:
                while True:
                    try :
                        stu_obj = pickle.load(f)
                        yield stu_obj
                    except EOFError:
                        break
    
        def show_courses(self):
            for count,course in enumerate(self.get_from_pickle(course_info),1):
                print(count,repr(course))
    
        def dump_obj(self,path,obj):
            with open(path,'ab') as f:
                pickle.dump(obj,f)
    
    class Student(Person,Base):
        operate_lst = [
                       ('查看所有课程', 'show_courses'),
                       ('选择课程', 'select_course'),
                       ('查看已选课程', 'check_selected_course'),
                       ('退出', 'exit')]
        def __init__(self,name):
            self.name = name
            self.courses = []
    
        def __repr__(self):
            # course_name = [course.name for course in self.courses]
            course_name = [str(course) for course in self.courses]
            return '%s %s'%(self.name,'所选课程%s' % '|'.join(course_name))
    
        def select_course(self):
            self.show_courses()
            num = int(input('num >>>'))
            for count,course in enumerate(self.get_from_pickle(course_info),1):
                if count == num:
                    self.courses.append(course)
                    print('您选择了%s课程' % (course))
                    break
            else:print('没有您要找的课程')
    
        def check_selected_course(self):
            for course in self.courses:
                print(course.name,course.teacher)
    
        def exit(self):
            with open(student_info+'_bak', 'wb') as f2:
                for stu in self.get_from_pickle(student_info):
                    if stu.name == self.name:  # 如果从原文件找到了学生对象和我当前的对象是一个名字,就认为是一个人
                        pickle.dump(self, f2)  # 应该把现在新的学生对象写到文件中
                    else:
                        pickle.dump(stu, f2)  # 反之,应该原封不动的把学生对象写回f2
            os.remove(student_info)
            os.rename(student_info+'_bak',student_info)
            exit()
    
        @classmethod
        def init(cls,name):
            for stu in cls.get_from_pickle(student_info):
                if stu.name == name:
                    return stu
            else:print('没有这个学生')
    
    class Manager(Person):
        operate_lst = [('创建课程','create_course'),
                       ('创建学生','create_student'),
                       ('查看所有课程','show_courses'),
                       ('查看所有学生','show_students'),
                       ('查看所有学生的选课情况','show_student_course'),
                       ('退出','exit')]
        def __init__(self,name):
            self.name = name
    
        def create_course(self):
            name = input('course name : ')
            price = input('course price : ')
            period = input('course period : ')
            teacher = input('course teacher : ')
            course_obj = Course(name,price,period,teacher)
            self.dump_obj(course_info, course_obj)
            print('%s课程创建成功'%course_obj.name)
    
        def create_student(self):
            # 用户名和密码记录到userinfo文件,将学生对象存储在student_info文件
            stu_name =input('student name : ')
            stu_pwd =input('student password : ')
            stu_auth = '%s|%s|Student
    '%(stu_name,stu_pwd)
            stu_obj = Student(stu_name)
            with open(userinfo,'a',encoding='utf-8') as f:
                f.write(stu_auth)
            self.dump_obj(student_info, stu_obj)
            print('%s学生创建成功'%stu_obj.name)
    
        def show_students(self):
            for count,stu in enumerate(self.get_from_pickle(student_info),1):
                print(count,stu)
    
        def show_student_course(self):
            for stu in self.get_from_pickle(student_info):
                print(repr(stu))
    
        def exit(self):
            exit()
    
        @classmethod
        def init(cls,name):
            return cls(name)   # 管理员的对象
    
    def login():
        name = input('username : ')
        pawd = input('password : ')
        with open(userinfo,encoding='utf-8') as f:
            for line in f:
                usr,pwd,identify = line.strip().split('|')
                if usr == name and pawd == pwd:
                    return {'result':True,'name':name,'id':identify}
            else:
                return {'result':False,'name':name}
    
    ret = login()
    if ret['result']:
        print('33[1;32;40m登录成功33[0m')
        if hasattr(sys.modules[__name__],ret['id']):
            cls = getattr(sys.modules[__name__],ret['id'])
            obj = cls.init(ret['name'])   # 实例化
            while True:
                for id,item in enumerate(cls.operate_lst,1):
                    print(id,item[0])
                func_str = cls.operate_lst[int(input('>>>')) - 1][1]
                print(func_str)
                if hasattr(obj,func_str):
                    getattr(obj,func_str)()
    else:
        print('登录失败')
    View Code
  • 相关阅读:
    COPY SAP 标准gui状态
    销售类型转换
    SAP数据表相关
    T_CODE I18N
    SAP-Function
    MLGBZ
    爷爷的烟斗
    使用 Composer 查看 FastAdmin 项目 组件的版本
    FastAdmin 后台前端后端组件说明(待续)
    FastAdmin 在 Nginx 中的配置
  • 原文地址:https://www.cnblogs.com/guoyilong/p/11327050.html
Copyright © 2011-2022 走看看