zoukankan      html  css  js  c++  java
  • 封装、继承、多态、反射

    面向对象(OOP)

    #什么是对象
    	面向对象是一种编程思想,是指导程序员如何编写出更好的程序的思想
        核心是对象,程序就是一系列对象的集合,我们程序员只需要调度、控制这些对象来交互完成工作
        
    #案例1,把对象装进冰箱
    	面向过程:
        	1.打开冰箱
            2.装入冰箱
    		3.关闭冰箱
        面向对象:
        	找个具备装大象的技能的对象
        
    在面向对象中,程序员的角度发生改变,从具体的操作者变成了指挥者
    强调:对象不是凭空产生的,需要我们自己设计
        
    #案例2,西天取经
    	面向过程:
        	如来找5个人,经历九九八十一难来取经,在把经书送回去
        面向对象:
        	如来只需要找5个对象,自己负责调度即可,如果某个对象发生变化,也不会影响其他对象的扩展性
        
    #案例3,
    	面向过程:
            曹操吟诗
            喝酒吃肉,人生真爽	后改为
            喝酒吃肉,人生几何	后改为
            对酒当歌,人生几何
    	面向对象:
        	把字拆成一个个对象,用的时候组装
        	优点:
            	1.扩展性强
        		2.灵活性强
        		3.重用性强
        	缺点:
            	1.程序的复杂度提高了
        		2.无法准确预知结果
        
    #使用场景
        1.对扩展性要求较高的程序,通常是直接面向用户的(QQ、微信)
        
    #不是所有的程序都要面向对象,要分析具体的需求
    

    面向过程

    #面向过程
    	面向过程关注的是过程,就是先干什么、后干什么
        
    #优点
    	逻辑清晰,可以将复杂的问题简单化、流程化
        
    #缺点
    	扩展性差,可维护性差,重用性低
        
    #使用场景
        1.对扩展性要求较低的程序,比如系统内核、git、计算器
    

    类和对象

    #类和对象
    	类和对象是面向对象编程的最核心的两个概念
    

    #类	
    	类就是类型、类别,类就是一个抽象的概念
        类是一系列具备相同特征和相同行为的对象的集合
    

    对象

    #对象
    	对象就是具体存在的某个事物,有自己的特征和行为
        
    #类和对象的关系
    	类包含一系列对象
        一个对象属于某个类
        
    #先有类,还是先有对象
    	生活中,是先有对象,然后有类
        程序中,先有类,才能有对象,我们必须先告诉计算机这类对象有什么特征、有什么行为(也就是说要先创建类)
        所以说,在使用面向对象编程时,第一步就是考虑需要使用什么对象,再考虑使用什么类,然后,先创建类,再创建对象
    

    创建类和对象

    #创建类
    #语法
    	class 类的名字:
            #注释
            #类中的内容,描述属性和技能
            #描述属性用变量
            #描述行为用函数
            
    #类的名字
    	1.见名知意
        2.大驼峰(单词的首字母大写)
            
    #创建类
    	#一个类,使用类名+()可以创建多个不同的对象
    class Student:
        pass
    
    #创建对象(类名加括号)
    print(Student)		#<class类 '__main__.Student'>
    res = Student()
    print(res)  		#<__main__.Student object对象 at 0x000001CBB06AFA90>
    print(res.__class__.__name__)   #Student
    print(res.__dict__)				#{}
            
    #类中的属性可以被任何一个对象访问,所以类中存放的应该是对象的公共属性  
    class Person:
        name = '张三丰'
        age = 2000
        sex = 'male'
    res = Person()
    print(res)
    print(res.name)
    print(res.age)
    print(res.sex)
    
    <__main__.Person object at 0x00000138EAA9A940>
    张三丰
    2000
    male
    
    #可以为对象单独设置属性	
    	#属性的查找顺序为,先查找对象,再查找类
    class Person:
        eat = '会吃饭'
        run = '会跑路'
    zhangsan = Person()
    zhangsan.eat = '不会吃饭'
    lisi = Person()
    lisi.run = '不会跑路'
    print(zhangsan.eat)
    print(zhangsan.run)
    print(lisi.eat)
    print(lisi.run)
    
    不会吃饭
    会跑路
    会吃饭
    不会跑路
    
    #对象的精髓就在于将数据和处理数据的函数整合到一起,这样以来,当拿到一个对象,就同时拿到处理的数据和处理数据的函数
    #在类和对象中,属性(数据)用变量表示,方法用函数表示
    	#属性、方法也可以统一称为属性,分为数据属性、函数属性
        
    class Student:
        school = '清华'
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def study(self):
            print('hello i am a student, my name is:%s'%self.school)
    
    t1 = Student('syy',18)
    print(type(t1.study))
    print(type(Student.study))
    
    <class 'method'>		#方法
    <class 'function'>		#函数
    

    属性的增删改查

    #类
    #增加类的属性
    	新建一个类(覆盖)
        
    #对象
    #增加对象的属性
    对象变量名.属性名称 = 属性值
    
    #删除对象的属性
    del 对象变量名.属性名称
    
    #修改对象的属性
    对象变量名.属性名称 = 新的属性值
    
    #查看对象的属性(专属属性)
    	#无论是查看类的属性还是查找对象的属性,都是使用__dict__
        #查看对象的类,使用__class__
    class Person:
        eat = '会吃饭'
        run = '会跑路'
    print(Person.__dict__)		#类的属性
    res = Person()		
    print(res)					#没有专属属性的对象
    print(res.__dict__)
    print(res.__class__)		#对象的类信息
    zhangsan = Person()
    zhangsan.eat = '不会吃饭'
    print(zhangsan.__dict__)	#有专属属性的对象
    print(zhangsan.__class__)		#对象的类信息
    
    {'__module__模块名称': '__main__', 'eat': '会吃饭', 'run': '会跑路', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__注释': None}
    <__main__.Person object at 0x000001C11D14A9E8>
    {}
    <class '__main__.Person'>
    {'eat': '不会吃饭'}
    <class '__main__.Person'>
    

    初始化对象的属性

    #使用类产生对象1
    class Teacher:
        school = '清华'
    
    t1 = Teacher()
    t1.name = 'tom'
    t1.age = 19
    
    t2 = Teacher()
    t2.name = 'jerry'
    t2.age = 20
    
    t3 = Teacher()
    t3.name = 'syy'
    t3.age = 21
    
    print(t1.name)
    print(t2.name)
    print(t3.name)
    
    #使用类产生对象2,使用函数封装重复代码
    class Teacher:
        school = '清华'
    
    def init(obj,name,age):
        obj.name = name
        obj.age = age
    
    t1 = Teacher()
    t2 = Teacher()
    t3 = Teacher()
    
    init(t1,'tom',19)
    init(t2,'jerry',19)
    init(t3,'syy',19)
    
    print(t1.name)
    print(t2.name)
    print(t3.name)
    
    #使用类产生对象3,使用__init__方法
    	#self就是生成的对应的对象
        #使用__init__定义的变量,就是对象的专属属性(__init__的作用就是给对象附初始值)
        #函数名必须是__init__,__init__函数不能使用return返回值
        #调用函数的时候,注意变量的个数要对应类体内的变量的个数(少一个),原因在于,调用函数的时候会将对象本身作为第一个参数
        #当需求是在创建对象时还需要做掉别的事,那就应该想到对象的初始化
    
    class Teacher:
        school = '清华'
        def __init__(self):
            print(self)
    t1 = Teacher()
    print(t1)
    
    <__main__.Teacher object at 0x000002C04478F908>
    <__main__.Teacher object at 0x000002C04478F908>
    
    class Teacher:
        school = '清华'
        def __init__(self,name,age):
            self.name = name
            self.age = age
    t1 = Teacher('tom',19)
    t2 = Teacher('jerry',20)
    t3 = Teacher('syy',21)
    print(t1,t2,t3)
    print(t1.name,t2.name,t3.name)
    
    <__main__.Teacher object at 0x0000022248F15A90> <__main__.Teacher object at 0x0000022248F15BA8> <__main__.Teacher object at 0x0000022248F159B0>
    tom jerry syy
    
    #使用locals()初始化对象
    	#locals(),是一个字典,表示当前名称空间中所有的变量名
    
    class Hero:
        def __init__(self,name,level,blood,attach,q_hurt,w_hurt,e_hurt):
            # self.name = name
            # self.level = level
            # self.blood = blood
            # self.attach = attach
            # self.q_hurt = q_hurt
            # self.w_hurt = w_hurt
            # self.e_hurt = e_hurt
            lcs = locals()
            lcs.pop('self')
            self.__dict__.update(lcs)
    s1 = Hero(1,2,3,4,5,6,7)
    print(s1.__dict__)
    
    {'e_hurt': 7, 'w_hurt': 6, 'q_hurt': 5, 'attach': 4, 'blood': 3, 'level': 2, 'name': 1}
    

    对象的绑定方法

    #对象绑定方法
    	#不使用装饰器的前提下,类中的方法(函数),都是对象绑定方法
    	#使用类生成对象,通过对象可以调用别的方法(函数体代码)
        
        
    #一个类里面可以有多个函数,但是一个类同时只能生成一个对象
    	#只要一生成对象,类中的代码就会运行,但是不会运行除了__init__别的函数体代码
        #别的函数体代码只会在调用的时候运行
        
    class Student:
        school = '清华'
        def __init__(self,name,age):
            self.name = name
            self.age = age
            print(self)
        def study(self):
            print('hello i am a student, my name is:%s'%self.name)
    
    t1 = Student('syy',18)
    t2 = Student('tom',19)
    print(t1.study())		#不需要传参,自动将该对象当做第一个参数传入
    print(t2.study())
    
    <__main__.Student object at 0x000002842EECAA20>
    <__main__.Student object at 0x000002842EEDE978>
    hello i am a student, my name is:syy
    None
    hello i am a student, my name is:tom
    None
    
    #使用对象调用方法和使用类调用方法的行为不同
    	#使用对象调用方法,有几个参数就传几个参数
        #使用类名调用方法,该方法就是一个普通函数
    class Student:
        school = '清华'
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def study(self):
            print('hello i am a student, my name is:%s'%self.name)
    
    t1 = Student('syy',18)
    t1.study()						#使用对象调用方法
    Student.study(t1)				#使用类名调用方法
    
    hello i am a student, my name is:syy
    hello i am a student, my name is:syy
        
    #练习
    	#写一个学生类,具备一个打招呼的技能,要能输出自己的名字信息
    class Student:
        def __init__(self,name):
            self.name = name
        def say_hi(self):
            print('my name is %s'%self.name)
    s1 = Student('syy')
    s1.say_hi()
    
    my name is syy
    

    类的绑定方法

    #
    class Student:
        school = '清华'
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def study(self):
            print(self.school)
    
    t1 = Student('syy',18)
    print(Student.school)		#访问类
    print(t1.school)			#访问对象	
    Student.study(t1)			#访问类中的函数(需要传入一个对象)
    t1.study()
    
    #类的绑定方法
    	#使用装饰器@classmethod装饰
        #不管是用类还是对象调用,都会自动传入类本身,作为第一个参数
    class Student:
        school = '清华'
        def __init__(self,name,age):
            self.name = name
            self.age = age
        @classmethod
        def study(cls):
            print(cls.school)
    
    t1 = Student('syy',18)
    print(Student.school)
    print(t1.school)
    Student.study()			#访问类中的函数(不需要传参数)
    t1.study()
    
    #什么时候绑定给对象
    	#当函数逻辑需要访问对象中的数据时
    #什么时候绑定给类
    	#当函数逻辑需要访问类中的数据时
    

    非绑定方法

    #非绑定方法或叫做静态方法
    	#使用装饰器@staticmethod
    	#即类既不绑定对象self,也不绑定类cls
        #该方法定义函数与类体外定义函数结果相同
        
    class Student:
        school = '清华'
        def __init__(self,name,age):
            self.name = name
            self.age = age
        @staticmethod
        def study():
            print('hello')
    
    t1 = Student('syy',18)
    Student.study()			#使用类访问方法
    t1.study()				#使用对象访问方法
    
    #实例
    	#为学生类添加一个save方法,一个get方法
        #save是将对象存储到文件中(对象绑定方法)
        #get是从你文件中获取对象(非绑定方法)
        
    import pickle
    class Student:
        def __init__(self,name):
            self.name = name
        def save(self):
            with open(self.name,'wb') as f:
                pickle.dump(self,f)
        @staticmethod
        def get(name):
            with open(name,'rb') as f:
                obj = pickle.load(f)
                return obj
    s1 = Student('syy')
    s1.save()
    obj = Student.get('syy')
    print(obj.name) 
    
    #当类中的函数需要使用self或者__init__中定义的变量,那么这个函数就是对象绑定
    #当类中的函数需要使用类中定义的变量,那么这个函数就是类绑定方法,可以使用装饰器classmethod修饰
    #当类中的函数既不需要使用类中定义的变量,又不需要使用__init__中定义的变量,那么这个对象就是非绑定方法
    

    实例 (对象交互练习)

    参考网站

    import random
    import time
    
    class Hero:
        def __init__(self,name,level,blood,att,Q_hurt,W_hurt,E_hurt):
            lcs = locals()
            lcs.pop('self')
            self.__dict__.update(lcs)
        def attack(self,enemy):
            enemy.blood -= self.att
            print('%s对%s释放了普通攻击,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.att,enemy.blood))
            if enemy.blood <= 0:
                print('%s被%s使用普通攻击击杀!'%(enemy.name,self.name))
        def Q(self,enemy):
            enemy.blood -= self.Q_hurt
            print('%s对%s释放了Q技能,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.Q_hurt,enemy.blood))
            if enemy.blood <= 0:
                print('%s被%s使用Q技能击杀!'%(enemy.name,self.name))
        def W(self,enemy):
            enemy.blood -= self.W_hurt
            print('%s对%s释放了W技能,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.W_hurt,enemy.blood))
            if enemy.blood <= 0:
                print("%s被%s使用W技能击杀!" % (enemy.name, self.name))
        def E(self,enemy):
            enemy.blood -= self.E_hurt
            print('%s对%s释放了E技能,造成了%s的伤害,敌人剩余血量%s'%(self.name,enemy.name,self.E_hurt,enemy.blood))
            if enemy.blood <= 0:
                print('%s被%s使用E技能击杀!'%(enemy.name,self.name))
    
    #定义英雄
    h1 = Hero('亚索',level=1,blood=50000,att=500,Q_hurt=600,W_hurt=1000,E_hurt=2000)
    h2 = Hero('妲己',level=2,blood=3000,att=600,Q_hurt=700,W_hurt=1000,E_hurt=2000)
    h3 = Hero('鲁班',level=3,blood=3000,att=700,Q_hurt=800,W_hurt=1500,E_hurt=2000)
    h4 = Hero('蔡文姬',level=4,blood=5000,att=100,Q_hurt=200,W_hurt=300,E_hurt=400)
    # h1.attack(h2)
    # h2.Q(h1)
    # h2.W(h1)
    # h2.E(h1)
    
    while True:
        #把所有的攻击方法装到字典,为了随机取出一个
        funcs = {1:Hero.Q,2:Hero.W,3:Hero.E,4:Hero.attack}
        func_index = random.randint(1,len(funcs))
        func = funcs[func_index]
        #把所有英雄装到字典,为了随机取出一个
        heros = {1:h1,2:h2,3:h3,4:h4}
        hero_index = random.randint(1,len(heros))
        hero = heros[hero_index]
        #剩余的英雄们
        other_heros = {}
        new_index = 1
        for k,v in heros.items():
            if k != hero_index:
                other_heros[new_index] = v
                new_index +=1
        #从剩余的英雄中随机跳出一个英雄来挨打
        enemy_index = random.randint(1,len(other_heros))
        enemy = other_heros[enemy_index]
        #打他
        func(hero,enemy)
        if enemy.blood <=0:
            break
        time.sleep(1)
    

    面向对象(OOP)的三大特征

    继承

    #什么是继承
    	继承是一种关系,描述两个类之间的关系
        在程序中,继承描述的是类和类之间的关系(父类、基类、子类)
        在python中,一个子类可以同时继承多个父类,使用逗号分开
    
    #为什么要使用继承
    	继承的一方可以直接使用被继承一方已经有的东西
        继承的作用就是重用已经有的代码,提高重用性
        
    #如何使用继承
    #语法
    class 类名称(父类的名称):
        pass
    
    #例
    class Base:
        desc = '这是一个基类'
        def show_info(self):
            print(self.desc)
        def make_money(self):
            print('一天赚它一个亿')
    class Subclass(Base):
        pass
        # def make_money(self):
        #     print('一天赚它一百块')
    obj = Subclass()
    obj.make_money()
    print(obj.desc)
    
    一天赚它一个亿
    这是一个基类
    

    抽象与继承

    #原始代码
    class Teacher:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
        def say_hi(self):
            print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
    class Student:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
        def say_hi(self):
            print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
    
    t1 = Teacher('syy','male',20)
    t1.say_hi()
    stu1 = Student('dog','female',2)
    stu1.say_hi()
    
    #抽象
    	#生活中,抽象是指不具体、不清晰、很模糊、看不懂,形容词
        #程序中,抽象是动词
        #将多个不同的子类中相同的部分进行抽取,形成一个新的类,这个过程叫抽象
        #先抽象再继承
        #继承一个已经存在的类,可以扩展或是修改原始的功能
        
    class Person:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
        def say_hi(self):
            print('name:%s,age:%s,gender:%s'%(self.name,self.age,self.gender))
    class Teacher(Person):
        def teacher(self):
            print('老师专属...')
    class Student(Person):
        pass
    
    t1 = Teacher('syy','male',20)
    t1.say_hi()
    stu1 = Student('dog','female',2)
    stu1.say_hi()
    

    属性查找顺序

    #属性查找顺序
    	对象 > 子类 > 父类
        
    class A:
        a = 1
    class B(A):
        b = 2
        def __init__(self,c):
            self.c = c
    class C(B):
        pass
    class D(C):
        pass
    t = B(3)
    print(t.b)		#2
    print(t.a)		#1
    print(t.c)		#3
    print(D.mro())	#所有父类名,按继承顺序排列,[<class '__main__.D'>, <class '__main__.C'>, <class '__main__.B'>, <class '__main__.A'>, <class 'object'>]
    

    派生与覆盖

    #派生
    	当一个子类中出现了与父类中不同的内容时,这个子类就称之为派生类
        通常子类都会写一些新的代码,不可能和父类完全一样,所以,子类通常都是派生类
        
    #覆盖
        覆盖也称之为overwrite
        当子类出现了与父类完全一致(名字一致)的属性和方法时,父类中的属性和方法被子类覆盖
        
    #派生与覆盖
    class Person:
        def say_hi(self):
            print('hello person')
    class Student(Person):
        def say_hi(self):
            print('hello student')
    stu = Student()
    print(stu.say_hi())
    
    #例
    	实现一个可以限制元素类型的列表
    	#子类中访问父类的内容使用关键字super
    class Mylist(list):
        def __init__(self,element_type):
            #先继承父类,后自定义子类的初始化
            super().__init__()
            #element_type: 指定的数据类型
            self.element_type = element_type
        def append(self,object):
            #object: 要存储的元素
            if type(object) == self.element_type:
                #在这里访问父类的append函数
                super(Mylist,self).append(object)
            else:
                print('sorry,you element type not is %s'%self.element_type)
    m = Mylist(str)
    m.append('壹')
    print(m[0])
    

    super

    参考网站

    #python2中的supper
    class Parent:
        test = 'abc'
        def say_something(self):
            print('anything')
    class Sub(Parent):
        def show_info(self):
            print(super(Sub,self).test)
            super(Sub, self).say_something()
    sub = Sub()
    sub.show_info()
    
    #python3中的supper
    class Parent:
        test = 'abc'
        def say_something(self):
            print('anything')
    class Sub(Parent):
        def show_info(self):
            print(super().test)
            super().say_something()
    sub = Sub()
    sub.show_info()
    
    #使用类名称,与继承无关
    class Parent:
        test = 'abc'
        def say_something(self):
            print('anything')
    class Sub(Parent):
        def show_info(self):
            print(Parent.test)
            Parent.say_something(self)
    sub = Sub()
    sub.show_info()
    
    #参考
    class Animal:  				# 定义一个父类
        def __init__(self):  	# 父类的初始化
            self.name = 'animal'
            self.role = 'parent'
            print('I am father')
     
    class Dog(Animal):				# 定一个继承Animal的子类
        def __init__(self):  		# 子类的初始化函数,此时会覆盖父类Animal类的初始化函数
            super(Dog, self).__init__()  # 在子类进行初始化时,也想继承父类的__init__()就通过super()实现,此时会定义self.name= 'animal'等
            print('I am son')
            self.name = 'dog'	  # 定义子类的name属性,并且会把刚才的self.name= 'animal'更新为'dog'
    animal = Animal()			#I am father
    xbai = Dog()				#I am son(没有继承父类的print?)
    print(xbai.name)			#'dog'
    
    #使用super继承
    	#子类在初始化的时候,调用父类的初始化
    class Teacher:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
        def say_hi(self):
            print('name: %s,age: %s,gender: %s'%(self.name,self.age,self.gender))
    class Student(Teacher):
        def __init__(self,name,age,gender,number):
            super(Student, self).__init__(name,age,gender)
            self.number = number
        def say_hi(self):
            super().say_hi()
            print('number: %s'%self.number)
    stu = Student('syy',18,'male',2333333)
    stu.say_hi()
    
    name: syy,age: 18,gender: male
    number: 2333333
        
    #继承方法
    class Teacher:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
            self.aa()
        def say_hi(self):
            print('name: %s,age: %s,gender: %s'%(self.name,self.age,self.gender))
        def aa(self):
            print('my name si aa')
    class Student(Teacher):
        def __init__(self,name,age,gender,number):
            super().__init__(name,age,gender)
            self.number = number
        def say_hi(self):
            super().say_hi()
            print('number: %s'%self.number)
    stu = Student('syy',18,'male',2333333)
    stu.say_hi()
    
    #当继承一个现有的类,并且覆盖了父类的__init__方法,那么,必须在初始化方法的第一行调用父类的初始化方法,并传入父类所需的参数,再根据情况自定义属性
    

    组合

    #组合
    	组合描述的是对象与对象之间的关系
        将一个对象作为另一个对象的属性
        组合也可以理解成继承的一种
        组合对比继承,类的耦合度更低
        
    #组合的作用
    	重用代码,减少代码冗余
        
    #什么时候使用继承
    	两个类之间存在父子关系,同类
        
    #什么时候使用组合
    	两个类之间毫无关系,异类
        
    #组合的代码实现
    	#人不是收集,但是人可以使用收集的功能
    class Phone:
        def __init__(self,price,kind,color):
            self.price = price
            self.kind = kind
            self.color = color
        def call(self):
            print('呼叫...')
        def send_message(self):
            print('发短信...')
    class Student:
        def __init__(self,name,age,phone):
            self.name = name
            self.age = age
            self.phone = phone
        def show_info(self):
            print('name: %s,age: %s'%(self.name,self.age))
    phone1 = Phone(10000,'apple','red')
    stu1 = Student('syy',18,phone1)
    stu1.phone.call()
    

    菱形继承

    #python支持多继承
    	存在类的继承顺序问题
    
    #一般的继承顺序
    	#按Sub的书写顺序继承
    class A:
        pass
    class B:
        pass
    class C:
        pass
    class Sub(A,B,C):
        pass
    print(Sub.mro())
    
    [<class '__main__.Sub'>, <class '__main__.A'>, <class '__main__.B'>, <class '__main__.C'>, <class 'object'>]
    
    #python3中,任何类都是直接或间接的继承Object
    class A:
        pass
    print(A.mro())		#[<class '__main__.A'>, <class 'object'>]
    
    #新式类与经典类
    	#新式类
        	任何显示或隐式的继承自Object的类,就叫做新式类
            python3中,全部都是新式类
        #隐式类
        	没有继承Object的类,就叫做经典类,python2中才会存在经典类
            所以类的创建一般标识继承自object,这样代码既可以兼容python2有可以兼容python3
            
    #菱形继承
    	当一个类有多个父类,且多个父类有共同的基类,那么这个类的继承就叫做菱形继承
        
    class A:
        j = 1
        pass
    class B(A):
        # j = 2
        pass
    class C(A):
        j = 3
        pass
    class D(B,C):
        # j =4
        pass
    test = D.j
    print(test)		#3
        
    #菱形继承中新式类与经典类的区别
    	#新式类
        	有共同父类就广度(广度没有再深度),没有共同父类就深度找
        #经典类
        	有共同父类是深度(深度没有再广度),没有共同父类是且仅是深度找
    class A:
        num = 1
        pass
    class B():
        num = 2
        pass
    class C(A):
        num = 3
        pass
    class D(A):
        num = 4
        pass
    class E(B):
        # num = 5
        pass
    class F(C):
        num = 6
        pass
    class G(D):
        num = 7
        pass
    class H(E,F,G):
        # num = 8
        pass
    print(H.num)	#2
    print(H.mro())	#[<class '__main__.H'>, <class '__main__.E'>, <class '__main__.B'>, <class '__main__.F'>, <class '__main__.C'>, <class '__main__.G'>, <class '__main__.D'>, <class '__main__.A'>, <class 'object'>]
    

    继承的使用

    #原始数据
    	程序员,姓名、性别、年龄、工资、编程技能
        项目经理,姓名、性别、年龄、工资、编程技能、奖金、管理技能
        
    #分析关系
    	初始化变量较多的时候,可以使用locals
    	项目经理来自程序员,可以使用super继承,没有必要使用抽象
        无论是程序员还是项目经理,都要使用存和取,单独抽取代码更加简洁,有利于代码扩展
    
    import pickle
    import os
    class Baseclass:
        '''
        将存储数据的操作单独抽取,这样可以降低耦合度,减少代码冗余
        '''
        def save(self):
            #判断类名对应的文件夹是否存在
            cls_name = self.__class__.__name__
            if not os.path.exists(cls_name):
                os.makedirs(cls_name)
            path = os.path.join(cls_name,self.name)
            with open(path,'wb') as f:
                pickle.dump(self,f)
        @classmethod
        def get_obj(cls,name):
            #拼接路径
            path = os.path.join(cls.__name__,name)
            if os.path.exists(path):
                with open(path,'rb') as f:
                    return pickle.load(f)
    
    class Coder(Baseclass):
        def __init__(self,name,age,gender,salary):
            self.name = name
            self.age = age
            self.gender = gender
            self.salary = salary
        def programming(self):
            print('程序员正在开发项目...')
    class Manager(Coder):
        def __init__(self,name,age,gender,salary,bonus):
            super().__init__(name,age,gender,salary)
            self.bonus = bonus
        def manage(self):
            print('经理正在殴打程序员...')
    if __name__ == '__main__':
        # cod1 = Coder('syy',18,'男神',10000)
        # cod1.programming()
        # cod1.save()
        # man1 = Manager('ji',88,'糟老头子',100,1)
        # man1.programming()
        # man1.save()
    
        cod1 = Coder.get_obj('syy')  #type: Coder
        cod1.programming()
        man1 = Manager.get_obj('ji')    #type: Manager
        man1.manage()
        
    程序员正在开发项目...
    经理正在殴打程序员...
    

    封装

    #什么是封装
    	封装就是将复杂的细节隐藏到内部,对外提供简单的使用接口
        
    #为什么要使用封装
    	为了保证关键数据的安全性
        对外隐藏细节,隔离复杂度
        
    #什么时候使用封装
    	当有一些数据不希望外界直接修改
        当有一些数据不希望外界使用时
        
    #怎么使用封装
    class Person:
        def __init__(self,id_number,name,age):
            self.id_number = id_number
            self.name =name
            self.age = age
        def show_id(self):
            print(self.id_number)
    p = Person('111','syy',18)
    p.id_number = '222'
    p.show_id()				#222
    print(p.id_number)		#222
    
    class Person:
        def __init__(self,id_number,name,age):
            self.__id_number = id_number
            self.name =name
            self.age = age
        def show_id(self):
            print(self.__id_number)
    p = Person('111','syy',18)
    p.__id_number = '222'
    p.show_id()				#111
    print(p.__id_number)	#222
    
    class Person:
        def __init__(self,id_number,name,age):
            self.__id_number = id_number
            self.name =name
            self.age = age
        def show_id(self):
            print(self.__id_number)
    p = Person('111','syy',18)
    p.id_number = '222'
    p.show_id()				#111,此时外界无法修改类体(可以使用_Person__id_number修改)
    print(p.id_number)		#222
    
    
    #例
    class PC:
        def __init__(self,price,kind,color):
            self.price = price
            self.kind = kind
            self.color = color
        def open(self):
            print('接通电源')
            self.__check_device()
            print('载入内核')
            print('初始化内核')
            self.__start_service()
            print('启动GUI')
            self.__login()
        def __check_device(self):
            print('硬件检测1.')
            print('硬件检测2..')
            print('硬件检测3...')
        def __start_service(self):
            print('启动服务1.')
            print('启动服务2..')
            print('启动服务3...')
        def __login(self):
            print('登录1.')
            print('登录2..')
            print('登录3...')
    p = PC(10000,'苹果','白色')
    p.open()
    # p.login(),外界无法修改类体,无法使用类体内部的数据
    
    #被封装的函数的特点
    	1.外界不能直接访问
        2.内部依然可以使用函数名调用
        
    #学习了封装之后就可以控制属性的权限
    	1.公开权限,默认所有属性和方法都是公开的
        2.私有权限,当前类才能使用的属性和方法
        
    #如何访问被封装的属性或方法
        #属性虽然被封装了,但是还是需要访问或修改的
        #通过定义函数,完成对私有属性的访问或修改
        	#这样以来,可以在外界访问或修改私有属性或方法时,添加额外的逻辑
        
    class Downloader:
        def __init__(self,filename,url,buffer_size):
            self.filename = filename
            self.url = url
            self.__buffer_size = buffer_size
        def start_download(self):
            if self.__buffer_size <= 1024*1024:
                print('当前缓冲大小为: %s'%self.__buffer_size)
                print('开始下载...')
            else:
                print('内存炸了!!!')
        #定义接口函数
        def set_buffer_size(self,size):
            #可以在接口函数中添加额外的逻辑
            if type(size) == int:
                print('缓冲大小修改成功,大小为: %s'%size)
                self.__buffer_size = size
            else:
                print('缓冲大小必须是整型!!!')
        # 通过函数访问内部封装的属性
        def get__buffer_size(self):
            return self.__buffer_size
    #生成对象
    d = Downloader('奥特曼全集','www.aoteman.com',1024*1024)
    #下载
    d.start_download()
    #修改函数内部封装的属性
    d.set_buffer_size(1024*512)
    #继续下载
    d.start_download() 
    

    property、key.setter、key.deleter装饰器

    #装饰器是用在函数上的
    #property装饰器
    	该装饰器为了解决封装的属性和方法的'访问'方法的不同
        使用了该装饰器,类中隐藏方法的'访问',直接使用点,不再使用括号
        
    #key.setter装饰器
    	该装饰器为了解决封装前后的属性'修改’的不同
        使用了该装饰器,类中隐藏属性的修改,直接使用点,不再使用括号
        
    #key.deleter装饰器
    	该装饰器为了解决不能删除封装的属性的问题,类似于触发器
        使用了该装饰器,类中隐藏属性的删除,可以直接删除,也可以设置触发条件
        
    #原始代码
    class A:
        def __init__(self,name,key):
            self.name = name
            self.__key = key
        def get_key(self):
            return self.__key
        def set_key(self,new_key):
            self.__key = new_key
    a = A('syy',123)
    print(a.name)			#访问属性不加括号
    print(a.get_key())		#访问方法加括号
    
    #使用了property后的代码
    class A:
        def __init__(self,name,key):
            self.name = name
            self.__key = key
        @property
        def get_key(self):
            return self.__key
        def set_key(self,new_key):
            self.__key = new_key
    a = A('syy',123)
    print(a.name)			#访问属性不加括号
    print(a.get_key)		#访问方法不再需要加括号,隐藏属性与普通属性访问方式相同
    a.set_key(456)			#属性的修改需要加括号
    print(a.get_key)
    
    #使用property、key.setter、key.deleter后的代码
    class A:
        def __init__(self,name,key):
            self.name = name
            self.__key = key
        @property
        def key(self):
            return self.__key
        @key.setter
        def key(self,new_key):
            self.__key = new_key
        @key.deleter
        def key(self):
            print('不允许删除该属性...')
            #del self.__key		#可以在这里删除隐藏属性
    a = A('syy',123)
    print(a.key)
    a.key = 456			#属性的修改不需要再使用括号,直接点,隐藏属性与普通属性修改方式相同
    print(a.key)
    del a.key			#隐藏属性不能直接删除,可以使用装饰器删除,隐藏属性与普通属性删除方式相同
    print(a.key)
    
    123
    456
    不允许删除该属性...
    456
    
    #注意
    	key是property装饰器装饰的方法的名字,在使用setter和deleter时,装饰器的函数名要保持一致
    

    封装的原理

    #封装的原理
    	#python中,类中的属性的封装非常简单,仅仅是把类体中的属性的'__变量名'改为'_类名__变量名',这样外界将不能直接使用变量名或者__变量名访问隐藏属性(但是外界可以使用_类名__变量名访问类的隐藏属性)
        #字符串的修改,对于python解释器的是很简单的
        #这种字符串的修改只会在加载类的时候,在类体中运行(属性隐藏和访问的时候,'添加都会运行')
    
    class A:
        def __init__(self,key):
            self.__key = key
        @property
        def key(self):
            return self.__key
        @key.deleter
        def key(self):
            del self.__key
    a = A(123)
    print(a.key)
    del a.key
    print(a.key)	#AttributeError: 'A' object has no attribute '_A__key'
    
    #
    class A:
        def __init__(self,key):
            self.__key = key
        @property
        def key(self):
            return self.__key
        @key.deleter
        def key(self):
            del self.__key
    a = A(123)
    print(a.key)			#123
    print(a.__dict__)		#{'_A__key': 123}
    print(a._A__key)		#123
    

    计算属性

    #使用property装饰器,可以用来实现计算属性
    	#计算属性指的是:属性的值不能直接获取,必须通过计算才能获取
        #这里的property装饰器,仅仅只是为了让方法的调用不加括号,与属性的调用相同而已
        
    #例
    	求正方形的面积
    class Square:
        def __init__(self,width):
            self.width = width
        @property
        def area(self):
            return self.width * self.width
    s = Square(10)
    print(s.area)		#100
    s.width = 20
    print(s.area) 		#400
    
    #例2
    	求BMI,BMI = 体重/身高的平方
    class Bmi:
        def __init__(self,height,weight):
            self.height = height
            self.weight = weight
        @property
        def bmi(self):
            return self.weight/self.height/self.height
    b = Bmi(170,165)
    print(b.bmi)		#0.005709342560553633
    

    接口

    #什么是接口
    	接口是一组功能的集合,但是接口中只包含功能的名字,不包含具体的实现代码
        接口本质上是一套协议标准,遵循这个标准的对象就能被调用
        
    #接口的作用
    	为了提高代码的扩展性
        
    #例
    	例如电脑上的USB接口协议,只要你遵循该协议,相关的设备就能被电脑使用,不需要关心设备的种类
        #PC的代码一旦完成,后期无论什么样的设备,只要遵循了USB接口协议,都能被电脑调用
        #接口主要是方便了对象的使用者,降低了使用者的学习难度,因为使用者只要学习了一套使用方法,就可以操作各类USB设备
        
    class USB:
        def open(self):
            pass
        def close(self):
            pass
        def read(self):
            pass
        def write(self):
            pass
    class Mouse(USB):
        def open(self):
            print('鼠标开机')
        def close(self):
            print('鼠标关机')
        def read(self):
            print('获取光标位置')
        def write(self):
            print('鼠标不支持写入')
    class Keyboard(USB):
        def open(self):
            print('键盘开机')
        def close(self):
            print('键盘关机')
        def read(self):
            print('获取键盘字符')
        def write(self):
            print('键盘写入灯光颜色')
    def pc(usb_device):
        usb_device.open()
        usb_device.read()
        usb_device.write()
        usb_device.close()
    m = Mouse()
    k = Keyboard()
    pc(m)
    pc(k)
        
    鼠标开机
    获取光标位置
    鼠标不支持写入
    鼠标关机
    键盘开机
    获取键盘字符
    键盘写入灯光颜色
    键盘关机
        
    #问题
    	如果子类没有按照你的协议来进行代码的编写,将导致代码无法运行
    

    抽象类

    #abc模块,是单词absract class的缩写,意为'抽象类' 
    #抽象类
    	一个类中没有包含函数体(或者经过装饰器装饰的函数体),那么这个类就是抽象类
        #作用
        	可以限制子类必须遵循父类中定义的抽象方法
    
    import abc
    class USB(metaclass=abc.ABCMeta):
        @abc.abstractmethod
        def open(self):
            pass
        @abc.abstractmethod
        def close(self):
            pass
        @abc.abstractmethod
        def read(self):
            pass
        @abc.abstractmethod
        def write(self):
            pass
    
    class Mouse(USB):
        def open(self):
            print('鼠标开机')
    
        def close(self):
            print('鼠标关机')
    
        def read(self):
            print('获取光标位置')
    
        def write(self):
            print('鼠标不支持写入')
    
    class Keyboard(USB):
        def open(self):
            print('键盘开机')
    
        def close(self):
            print('键盘关机')
    
        def read(self):
            print('获取键盘字符')
    
        def write(self):
            print('键盘写入灯光颜色')
    
    def pc(usb_device):
        usb_device.open()
        usb_device.read()
        usb_device.write()
        usb_device.close()
    
    m = Mouse()
    k = Keyboard()
    pc(m)
    pc(k)
    

    鸭子类型

    #python一般不会限制你必须怎么写,作为一个优秀的程序员,就应该遵守相关协议,所以有了鸭子类型的说法
    #鸭子类型
    	如果一个对象长得像鸭子,走路像鸭子,那他就是鸭子
        #由此类推,只要保证你的类,按照相关的协议编写,就可以达到提高扩展性的目的
        
    class Mouse:
        def open(self):
            print('鼠标开机')
        def close(self):
            print('鼠标关机')
        def read(self):
            print('获取光标位置')
        def write(self):
            print('鼠标不支持写入')
    class Keyboard:
        def open(self):
            print('键盘开机')
        def close(self):
            print('键盘关机')
        def read(self):
            print('获取键盘字符')
        def write(self):
            print('键盘写入灯光颜色')
    def pc(usb_device):
        usb_device.open()
        usb_device.read()
        usb_device.write()
        usb_device.close()
    
    m = Mouse()
    k = Keyboard()
    pc(m)
    pc(k) 
    

    多态性

    #多态
    	生活中,一种事物有多种不同的形态
    	官网:多个对象可以响应同一个方法,产生不同的结果
        多态不是一种特殊的语法,而是一种状态、特性
        接口、抽象类、鸭子类型,都可以写出具备多态的代码,其中最简单的就是鸭子类型
        #优点
        	对于使用者而言,大大的降低了使用难度,例如USB接口程序就有多态性
    
    #例1
    class Ji:
        def bark(self):
            print('咯咯咯...')
        def spawn(self):
            print('下鸡蛋')
    class Ya:
        def bark(self):
            print('嘎嘎嘎...')
        def spawn(self):
            print('下鸭蛋')
    class E:
        def bark(self):
            print('eee...')
        def spawn(self):
            print('下鹅蛋')
    j = Ji()
    y = Ya()
    e = E()
    	#多个对象使用相同的方法,可以得到不同的结果
    def manage(obj):
        obj.spawn()
    manage(j)
    manage(y)
    manage(e)
        
    下鸡蛋
    下鸭蛋
    下鹅蛋
        
    #例2
    a = 10
    b = '10'
    c = [10]
    print(type(a))		#多个变量名使用相同的方法,得到不同的结果
    print(type(b))
    print(type(c))      
    

    OOP相关的内置函数

    #该内置函数无论在不在类中,都能执行
    #代码1
    def add_num(a,b):
        if type(a) == int and type(b) == int:
            return a+b
        return None
    print(add_num(1,2))
    print(add_num(1,'2'))
    
    #isinstance()
    	使用isinstance()函数,判断数据类型
        isinstance(对象,数据类型)
    def add_num(a,b):
        if isinstance(a,int) and isinstance(b,int):
            return a+b
        return None
    print(add_num(1,2))
    print(add_num(1,'2'))
    
    #issubclass()
    	使用issubclass()函数,可以判断一个类是否为某个类的子类/子孙类
        issubclass(子类,父类)
    class Animal:
        def eat(self):
            print('动物要吃东西...')
    class Pig(Animal):
        def eat(self):
            print('猪要吃猪食')
    class Tree:
        def light(self):
            print('植物进行光和作用')
    def manage(obj):
        if issubclass(type(obj),Animal):
            obj.eat()
        else:
            print('不是动物!')
    p = Pig()
    t = Tree()
    manage(p)
    manage(t)
    

    类中的魔法函数

    #__str__
    	只要是双下,就会在某个时候自动执行
        __str__会在对象被转换为字符串时执行,转换的结果就是这个函数的返回值
    
    #打印对象,得到内存地址
    class Person:
        pass
    p = Person()
    print(p)
    
    <__main__.Person object at 0x0000028E1471FA20>
    
    #打印对象,得到返回值
    class Person:
        def __str__(self):
            return 'abc'
    p = Person()
    print(p) 		#abc
        
    #python解释器中的内置变量、内置函数、内置类,在程序运行结束会自动清理,但是python解释器不会清理不属于解释器的资源,例如打开的文件等,这个时候就需要手动close关闭代码
    	#__del__,吸构函数,删除对象/类,得到返回值 
    	#__del__函数只会执行一次
        #执行时机:
        	手动删除对象时执行
            程序运行结束,就会执行类中的__del__函数
        #使用场景
        	当你的对象在使用过程中,打开了不属于python解释器的资源,例如文件、网络端口,在程序运行结束之后,就需要使用__del__函数来完成资源的释放工作
            
    import time
    class Person:
        def __del__(self):
            print('del run')
    p = Person()
    print(p)
    # del p
    time.sleep(2)
    print('end')
        
    <__main__.Person object at 0x0000025385DEF400>
    end
    del run
        
    class Filetool:
        '''该类用于文件的读取操作'''
        def __init__(self,path):
            self.file = open(path,'rt',encoding='utf-8')
        def read(self):
            return self.file.read()
        #在这里可以确定生成的对象不再使用了,所以可以放心的关闭文件了
        def __del__(self):
            tool.file.close()
    tool = Filetool(r'E:python_testa.txt')
    print(tool.read())		#a.txt 
        
    #__init__,初始化函数、构造函数
    class Person:
        def __init__(self,name,age):
            self.name = name
            self.age = age
    p = Person('syy',18)
    print(p.name)
        
    #__call__函数,在调用对象的时候执行(对象加括号)
    class A:
        def __call__(self, *args, **kwargs):
            print('call run')
            print(args)
            print(kwargs)
    a = A()
    a(1,'壹',z=233)
        
    #python是一门动态的语言,可以在代码运行期间动态的修改对象的属性所占用的空间
    	#如果代码运行内存不足,那么python解释器就会开启更大的内存区域,将原始的属性迁移过去
        #这里存在一个问题,如果python解释器开启内存太大,那么将会造成内存的浪费
        #解决方法是,在创建对象时,告诉python解释器要开启多大的内存,而不是让python解释器被动的开启内存
    	#__slots__函数,该属性是一个类属性,用于优化对象内存,将原本不固定的属性数量变得固定,这样python解释器就不会为这个对象创建名称空间,所以也没了__dict__方法
        	#使用了该函数,对象将不能再添加、删除属性
    
    #代码1
    import sys
    class Person:
        def __init__(self,name):
            self.name = name
    p = Person('syy')
    print(sys.getsizeof(p))		#56
    
    #代码2
    import sys
    class Person:
        __slots__ = ['name']
        def __init__(self,name):
            self.name = name
    p = Person('syy')
    print(sys.getsizeof(p))		#48
    # p.age = 18				#AttributeError: 'Person' object has no attribute 'age'
    # print(p.__dict__)			#AttributeError: 'Person' object has no attribute 'age'
    

    属性的getattr、setattr、delattr函数

    #这几个函数体现了python解释器如何使用点来设置、访问、删除属性的
    #使用点设置属性的时候,执行__setattr__函数
    #使用点访问属性(属性不存在)的时候,执行___getattr__函数
    
    class A:
        def __setattr__(self, key, value):
            print('__setattr__')
        def __getattr__(self, item):
            print('__getattr__')
        def __delattr__(self, item):
            print('__delattr__')
    a = A()
    a.name = 'syy'
    print(a.name)
    
    __setattr__
    __getattr__
    None
    
    class A:
        def __setattr__(self, key, value):
            super().__setattr__(key,value)
            print('__setattr__')
        def __getattr__(self, item):
            print('__getattr__')
        def __delattr__(self, item):
            print('__delattr__')
    a = A()
    a.name = 'syy'			#通过点语法操作对象的属性(原理是使用__dict__)
    print(a.name)			#syy,变量设置成功,所以可以打印出syy
    
    __setattr__
    syy
    
    a = A()
    a.__dict__['name'] = 'syy'		#通过__dict__设置属性
    print(a.name)					#syy
    
    class A:
        def __setattr__(self, key, value):
            self.__dict__[key] = value
            print('__setattr__')
        def __getattr__(self, item):
            print('__getattr__')
        def __delattr__(self, item):
            print('__delattr__')
    a = A()
    print(a.xxx)					#__getattr__
    
    #删除属性的时候,执行__delattr__函数
    class A:
        def __setattr__(self, key, value):
            self.__dict__[key] = value
            print('__setattr__')
        def __getattr__(self, item):
            print('__getattr__')
        def __delattr__(self, item):
            print('__delattr__')
    a = A()
    a.name = 'syy'
    del a.name
    print(a.name)		#删除不掉,原因是因为__delattr__对应的函数没有删除的操作
    
    __setattr__
    __delattr__
    syy
    
    class A:
        def __setattr__(self, key, value):
            self.__dict__[key] = value
            print('__setattr__')
        def __getattr__(self, item):
            print('__getattr__')
        def __delattr__(self, item):
            self.__dict__.pop(item)
            print('__delattr__')
    a = A()
    a.name = 'syy'
    del a.name
    print(a.name)		#删除成功
    
    __setattr__
    __delattr__
    __getattr__
    None
    
    #访问属性的时候,无论有没有该属性都会执行__getattribute__函数,
    class A:
        def __getattr__(self, item):
            print('__getattr__')
        def __getattribute__(self, item):
            print('__getattribute__')
    a = A()
    a.name = 'syy'
    print(a.name)
    print(a.xxx)
    
    __getattribute__
    None
    __getattribute__
    None
    
    #__dict__方法的原理就是使用了__getattribute__函数
    class A:
        def __getattr__(self, item):
            print('__getattr__')
        def __getattribute__(self, item):
            print('__getattribute__')
            return self.__dict__[item]
    a = A()
    a.name = 'syy'
    print(a.name)
    print(a.xxx)		#RecursionError: maximum recursion depth
    
    #实际上python内部是先使用__getattribute__取值,如果取不到,再使用__getattr__
    class A:
        def __getattr__(self, item):
            print('__getattr__')
        def __getattribute__(self, item):
            print('__getattribute__')
            return super(A, self).__getattribute__(item)
    a = A()
    a.name = 'syy'
    print(a.name)
    print(a.xxx)
    
    __getattribute__
    syy
    __getattribute__
    __getattr__
    None
    

    属性的setitem、getitem、delitem

    #这几个函数体现了python解释器如何使用[]来设置、访问、删除属性的
    #设置属性的时候执行__setitem__
    class A:
        def __setitem__(self, key, value):
            print('__setitem__')
        def __getitem__(self, item):
            print('__getitem__')
        def __delitem__(self, key):
            print('__delitem__')
    a = A()
    a['name'] = 'syy'		#__setitem__
    print(a.name)			#AttributeError: 属性不存在
    
    #访问属性的时候执行__getitem__
    class A:
        def __setitem__(self, key, value):
            print('__setitem__')
            self.__dict__[key] = value
        def __getitem__(self, item):
            print('__getitem__')
            return self.__dict__[item]
        def __delitem__(self, key):
            print('__delitem__')
            del self.__dict__[key]
    a = A()
    a['name'] = 'syy'
    print(a['name'])
    
    __setitem__
    __getitem__
    syy
    
    #删除属性的时候执行__delitem__
    class A:
        def __setitem__(self, key, value):
            print('__setitem__')
            self.__dict__[key] = value
        def __getitem__(self, item):
            print('__getitem__')
            return self.__dict__[item]
        def __delitem__(self, key):
            print('__delitem__')
            del self.__dict__[key]
    a = A()
    a['name'] = 'syy'
    print(a['name'])
    del a['name']
    print(a['name'])		#KeyError: 'name'
    
    __setitem__
    __getitem__
    syy
    __delitem__
    __getitem__
    
    #例
    	如何让一个对象既支持点语法取值,也支持括号取值
        #对象本身就支持括号[]设置、访问、删除值,所以只需要添加点语法即可
    
    class Mydict(dict):
        def __setattr__(self, key, value):
            self[key] = value
        def __getattr__(self, key):
            return self.get(key)
        def __delattr__(self, item):
            del self[item]
    m = Mydict()
    m['name'] = 'syy'
    print(m['name'])		#syy
    del m['name']
    # print(m['name'])
    m.name = 'zyy'
    print(m.name)			#zyy
    del m.name
    # print(m.name)
    

    运算符重载

    #当我们在使用某个符号时,python会为这个符号定义一个含义,同时调用对应的处理函数,当我们需要自定义对象的比较规则时,就可以在子类中覆盖 >、<、=、!=等方法
    	#self和other指的是两个对象
        #类似于大于、小于,我们只需要实现一个即可,如果符号不同,python解释器会自动交换两个对象的位置
    
    class Student:
        def __init__(self,name,age,height):
            self.name = name
            self.age = age
            self.height = height
        def __gt__(self, other):
            if self.height > other.height:
                return True
            else:
                return False
        def __lt__(self, other):
            if self.height < other.height:
                return True
            else:
                return False
        def __eq__(self, other):
            if self.name == other.name and self.age == other.age and self.height == other.height:
                return True
            else:
                return False
        def __ne__(self, other):
            if self.name == other.name or self.age == other.age or self.height == other.height:
                return True
            else:
                return False
    s1 = Student('syy',18,180)
    s2 = Student('zyy',80,140)
    print(s1 > s2)
    print(s1 < s2)
    print(s1 == s2)
    print(s1 != s2)
    
    True
    False
    False
    True
    
    #代码简写
    class Student:
        def __init__(self,name,age,height):
            self.name = name
            self.age = age
            self.height = height
        def __gt__(self, other):
            return self.height > other.height
        def __lt__(self, other):
            return self.height < other.height
        def __eq__(self, other):
            return self.name == other.name and self.age == other.age and self.height == other.height
        def __ne__(self, other):
            return self.name != other.name or self.age != other.age or self.height != other.height
    s1 = Student('syy',18,180)
    s2 = Student('zyy',80,140)
    print(s1 > s2)
    print(s1 < s2)
    print(s1 == s2)
    print(s1 != s2)
    
    True
    False
    False
    True
    

    迭代器协议

    #迭代器的判定方法
    	1.内置有__iter__方法
    	2.内置有__next__方法
    
    #迭代器的作用
    	迭代器就是一种取值的工具
        节省空间
    
    #迭代器原理
    class Myiter:
        def __init__(self,num):
            self.num = num
            self.count = 0
        def __iter__(self):
            return self
        def __next__(self):
            self.count += 1
            if self.count <= self.num:
                return '哈哈'
            else:
                raise StopIteration
    for i in Myiter(10):
        print(i)
    
    #生成器
    class Myrange:
        def __init__(self,start,end,step):
            self.start = start
            self.end = end
            self.step = step
        def __iter__(self):
            return self
        def __next__(self):
            a = self.start
            self.start += self.step
            if a < self.end:
                return a
            else:
                raise StopIteration
    for i in Myrange(1,10,2):
        print(i)
    

    上下文管理

    #上下文comtext
    	指的是一段话的意义,要参考当前的场景,即参考上下文
    	在python中,上下文可以理解为是一个代码区间、一个范围,例如with open,打开的文件尽在这个上下文中有效
    	涉及到的两个方法:
    		enter:
                表示进入上下文,进入某个场景
    		exit:
                表示退出上下文,退出某个场景
            #使用open类的with,先执行__enter__函数,open内代码运行结束,最后执行__exit__函数
            
    class Myopen:
        def __enter__(self):
            print('__enter__')
        def __exit__(self, exc_type, exc_val, exc_tb):
            print('__exit__')
            print(exc_type, exc_val, exc_tb)
    with Myopen() as m:
        print('start')
        # '1' + 1			#open内代码执行的过程中,如果中途代码异常,则之后的代码不再执行,但是立即__exit__函数仍会执行,包含错误的类型、错误的信息、错误的追踪信息
        print('over')
    
    __enter__
    start
    over
    __exit__
    None None None
    
    class Myopen:
        def __init__(self,path):
            self.path = path
        def __enter__(self):
            self.file = open(self.path)
            return self
        def __exit__(self,exc_type, exc_val, exc_tb):
            self.file.close()
            return False        #通过返回的状态码判断代码运行的过程中,有诶有错误或者错误有没有被处理
    with Myopen('a.txt') as m:
        print(m.file.read())
    

    反射

    #反射reflect
    #什么是反射
    	其实reflect的意思是反省、自省的意思
        反射指的是一个对象应该具备可以检测、修改、增加自身属性的能力
        反射就是通过字符串操作属性
        反射涉及到的四个内置函数(python解释器自带的),hasattr、getattr、setattr、delattr
        这几个函数都是针对于对象的,对应于对象属性是否存在、获取、设置、删除
        这几个函数实际上就是封装了__dict__等方法而已
        
    #hasattr()函数
    	#判断一个属性属不属于某个对象 
    class Person:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    p = Person('syy',18,'male')	
    print(hasattr(p,'name'))		#True
    print(hasattr(p,'names'))   	#False
        
    #getattr()函数
    	#获取对象的属性,可以设置默认返回值(属性不存在的时候返回)
    class Person:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    p = Person('syy',18,'male')
    if hasattr(p,'name'):
        print(getattr(p,'name',None))
        
    #setattr()函数
    	#为对象添加新的属性
    class Person:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    p = Person('syy',18,'male')
    setattr(p,'id','123')
    print(p.id)			#123
        
    #delattr()函数
    	#删除对象的属性
    class Person:
        def __init__(self,name,age,gender):
            self.name = name
            self.age = age
            self.gender = gender
    p = Person('syy',18,'male')
    delattr(p,'name')
    print(p.name)  			#AttributeError:   
    

    反射的使用场景

    	反射实际上就是对属性的增删改查,但是如果直接使用内置的__dict__来操作的话,会显得语法繁琐,不好理解,所以python封装了4个函数(getattr、setattr、delattr、getattribute)
        封装的另一个原因就是,如果对象不是我自己写的而是另一方提供的,那我就必须判断这个对象是否具备我需要的属性和方法
        反射是框架的基石,因为框架的设计者,不可能提前知道你的对象到底是怎么设计的,所以你提供给框架的对象,必须通过判断验证之后,才能正常的使用,而判断验证就是反射要做的事情,所以说反射就是对__dict__的操作进行封装
        面向对象不是只有python才有的概念,任何高级语言都有的
        
    #需求
    	要实现一个用于处理用户的终端指令的小框架
        所谓的框架,就是已经实现了最基础的构架,就是所有的项目都一样的部分
        
    #framwork/myframwork.py 
    import framework.plugins
    def run(plugin):
        while True:
            cmd = input('请输入命令>>>: ').strip()
            if cmd == 'exit':
                break
            #判断对象是否具备处理这个指令的方法
            if hasattr(plugin,cmd):
                #去除对应的方法并执行
                func = getattr(plugin,cmd)
                func()
            else:
                print('不支持该指令,请重新输入...')
        print('bay bay ...')
    wincmd = framework.plugins.Wincmd()
    linuxcmd = framework.plugins.Linuxcmd()
    run(wincmd)
    run(linuxcmd) 
        
    #framwork/plugins.py
    class Wincmd:
        def cd(self):
            print('wincmd: 切换目录')
        def delete(self):
            print('wincmd: 删除')
        def dir(self):
            print('wincmd: 查看目录')
    
    class Linuxcmd:
        def cd(self):
            print('linuxcmd: 切换目录')
        def rm(self):
            print('linuxcmd: 删除')
        def ls(self):
            print('linuxcmd: 查看目录')
            
    #思考
    	上述代码写死了必须使用某个类,这是不合理的,因为框架式的设计者无法提前知道对方的类在什么地方,以及类名叫什么
        所以我们应该为框架的使用者提供一个配置文件,要求框架的使用者将类的信息写入配置文件,然后框架自己去加载需要使用的模块
    

    动态导入

    #在框架设计中,我们不可能提前知道框架用户要提供的类的相关信息,所以需要动态导入
    	#动态导入需要使用importlib模块
    
    #framework/myframwork.py
    import importlib
    import framework.settings
    def run(plugin):
        while True:
            cmd = input('请输入命令>>>: ').strip()
            if cmd == 'exit':
                break
            #判断对象是否具备处理这个指令的方法
            if hasattr(plugin,cmd):
                #取出对应的方法并执行
                func = getattr(plugin,cmd)
                func()
            else:
                print('不支持该指令,请重新输入...')
        print('bay bay ...')
    #框架根据配置文件拿到类的路径
    path = framework.settings.class_path
    #框架从配置文件,单独拿出模块路径和类名称
    module_path,class_name = path.rsplit('.',1)
    mk = importlib.import_module(module_path)
    cls = getattr(mk,class_name)
    obj = cls()
    run(obj)
    
    #settings.py
        #settings为框架的配置文件
        #框架之外的部分就由自定义对象来完成,框架值提供固定的格式
        #作为框架的使用者,在配置文件中,手动指定要使用的类的路径和类名
    class_path = 'libs.plugins.Wincmd'
    
    #plugins.py
    class Wincmd:
        def cd(self):
            print('wincmd: 切换目录')
        def delete(self):
            print('wincmd: 删除')
        def dir(self):
            print('wincmd: 查看目录')
    
    class Linuxcmd:
        def cd(self):
            print('linuxcmd: 切换目录')
        def rm(self):
            print('linuxcmd: 删除')
        def ls(self):
            print('linuxcmd: 查看目录')
    

    元类 metaclass

    元类type与object的关系

    #在python中,万物皆对象,当然类也是对象
    	#对象是由类实例化产生的,所以类必然也是由另一个类实例化产生的
        
    #什么是元类
    	元类就是产生类的类
        所有类的元类默认都是type,type都是小写字母
        type是object的类型,同时,object又是type的超类
        	元类type -> object -> 类
        	元类type -> 类  
            class type(object):
                pass
    
    class Person:
        pass
    p = Person()
    print(type(p))
    print(type(Person))
    print(type(type))
    print(type(object))
    
    <class '__main__.Person'>		对象p由Person类产生
    <class 'type'>					对象Person由type类产生
    <class 'type'>
    <class 'type'>
    
    class type1(object):
        pass
    t = type1()
    print(type(t))
    print(type(type1))
    
    <class '__main__.type1'>
    <class 'type'>		#所有类的元类默认都是type
    
    #一个类的三个基本组成
    	1.类的名字,字符串类型
        2.类的父类们,是一个元组或列表
    	3.类的的名称空间,字典类型
        #type类创建类与使用关键字class创建类等效(使用元类创建类的两种方式)
    class type(object):
        def __init__(cls, what, bases=None, dict=None):
            pass
        
    cls_obj = type('test', (), {})
    print(cls_obj)
    class test:
        pass
    print(test)
    class test(metaclass=type):
        pass
    print(test)
        
    <class '__main__.test'>		#cls_obj既是类又是对象
    <class '__main__.test'>
    <class '__main__.test'>
        
    #为什么要使用元类
        高度'自定义'一个类,例如控制类的名字必须以大驼峰的方式书写
        只要是直接继承了type类,那么这个类就变成了一个元类,再利用覆盖可以做出'指定的元类',从而'限制类'
        因为不能修改python的源码,所以在使用元类的时候要自定义一个元类
        
    #需求
    	#如何用代码实现类名必须以大驼峰书写
    #思考
    	类也是对象,可以由元类创建
        类创建对象的时候执行类中的__init__方法,元类创建类的时候,执行元类中的__init__方法
        创建类的时候还有别的操作,想到__init__方法
        所以,只需要找到创建类的类(元类),再覆盖其中的__init__方法,就可以达到需求
    #定义一个元类
    class Mytype(type):
        def __init__(self,class_name,bases,dict):
            super().__init__(class_name,bases,dict)
            if not class_name.istitle():
                raise Exception('请书写大驼峰!')
    #为pig类指定元类为Mytype
    #方式1
    # class Pig(metaclass=Mytype):
    #     pass
    #方式2
    Mytype('Pig',(),{})
    

    call方法的应用

    #类实例化产生对象(类加括号)的时候,执行类中的__init__方法
    #对象加括号执行的时候,调用类中的__call__方法
    class Dog(type):
        def __call__(self,*args,**kwargs):
            print('call run')
    d = Dog()	#类也是对象,1.执行类中的__init__2.执行元类的__call__方法
    d()			#call run,1.执行类中的__call__
    
    #类与元类中__init__,__call__的执行关系
    class Mymete(type):
        def __init__(self,name,bases,dict):
            super().__init__(name,bases,dict)   #先覆盖,后导入
            print('元类init run')
        def __call__(self,*args,**kwargs):     #self为元类生成的类,*args为传入的位置参数,**kwargs为传入的关键字参数
            print('元类call run')
            # print(*args)
            # print(**kwargs)
            return super().__call__(*args,**kwargs)   #必须使用return,作用是创建对象,再执行子类的初始化
    class Dog(metaclass=Mymete):        #此时执行元类的__init__
        def __init__(self,name):        #元类__call__有返回值super().__call__(*args,**kwargs),就会执行
            self.name = name
            print('init run')
        def __call__(self,*args,**kwargs):      #生成的对象家括号执行的时候执行
            print('call run')
    d = Dog('syy')                      #此时执行元类的__call__,实例化产生对象就是执行元类中的__call__
    print(d)
    d()
    
    元类init run
    元类call run
    init run
    <__main__.Dog object at 0x00000212E9A4A908>
    call run
    
    #元类中的__call__方法
    	当你调用类对象时,会自动执行元类中的__call__方法,并将这个类本身作为第一个参数传入,以及后面的一堆参数
    	覆盖元类中的__call__之后,这个类就无法产生对象,必须调用super().__call__来完成对象的创建,并将其作为返回值
        当想要控制对象的创建过程,就覆盖父类(type)的__call__方法
        当想要控制类的创建过程,就覆盖父类(type)的__init__方法
    
    #想要把对象的所有属性变成大写
    class Mymete(type):
        def __call__(self, *args, **kwargs):
            new_args = []
            for i in args:
                new_args.append(i.upper())
            return super().__call__(*new_args,**kwargs)
    class Person(metaclass=Mymete):
        def __init__(self,name):
            self.name = name
    p = Person('syy')
    print(p.name)		#SYY
    
    #要求创建对象时,必须以关键字参数形式来传参
    	#注意一旦覆盖了父类(type)的__call__,就必须调用返回父类(type)的__call__
    class Mymete(type):
        def __call__(self, *args, **kwargs):
            if args:
                raise Exception('不好意思,不允许使用位置参数')
        #     return super().__call__(*args,**kwargs)		#1.创建对象,2.子类初始化
            obj = object.__new__(self)
            self.__init__(obj,*args,**kwargs)
            return obj
    class Person(metaclass=Mymete):
        def __init__(self,name):
            self.name = name
    p = Person(name='syy')
    print(p.name)		#syy
    
    #补充
    	当你创建类对象时,会首先执行元类中的__new__方法,拿到一个空对象,然后自动调用元类__init__,然后对这个类进行初始化操作
        如果覆盖了该方法,则必须保证__new__方法必须有返回值且必须是类对象
        __new__方法和__init__方法,都可以实现控制类的创建过程,init更简单
        
    class Mymete(type):
        def __new__(cls, *args, **kwargs):
            print(cls)      #元类自己
            print(args)     #创建类需要的几个参数,类名、基类、名称空间
            print(kwargs)   #空
            # return super().__call__(cls, *args, **kwargs)
            obj = type.__new__(cls,*args,**kwargs)
            return obj
        def __init__(self,a,b,c):
            super().__init__(a,b,c)
    class A(metaclass=Mymete):
        pass
    m = A()
    print(m)
    
    <class '__main__.Mymete'>
    ('A', (), {'__module__': '__main__', '__qualname__': 'A'})
    {}
    <__main__.A object at 0x000001F53C5AA940>
    
    #习题
    	定义一个元类,功能是获取类中所有的属性和方法,将名字存放到一个列表中,再将列表作为类的属性,使得类可以调用attrs,来获取自己的所有内容名称
        
    class Mymeta(type):
        def __init__(self,name,bases,dict):
            super().__init__(name,bases,dict)
            attrs = [i for i in list(dict.keys()) if not i.startswith('__')]
            self.attrs = attrs
    class A(metaclass=Mymeta):
        name = 'syy'
        def info(self):
            print('info')
        def say(self):
            print('say')
    print(A.attrs)    		#['name', 'info', 'say']
    

    单例设计模式

    #模式
    	固定的格式
        
    #设计模式
    	用于解决某种固定问题的套路
        
    #单例
    	一个类只产生一个对象
        为了节省空间,但一个类的所有对象的属性全部相同时,则没有必要创建多个类
        
    #伪单例  
    	#使用get_instance()方法,只会创建一个对象
        #但是使用'类()'仍然可以创建多个对象
    class Person():
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say_hi(self):
            print('hello %s'%self.name)
        #用于获取对象的方法
        @staticmethod
        def get_instance(name,age):
            #判断该类是否已经创建过对象
            if hasattr(Person,'obj'):
                return getattr(Person,'obj')
            obj = Person(name,age)
            Person.obj = obj
            print('new了一次')
            return obj
    # p = Person()
    p = Person.get_instance('syy',18)
    p2 = Person.get_instance('syy',18)
    p3 = Person.get_instance('syy',18)
        
        
    #单例
    class Mymeta(type):
        def __call__(self, *args, **kwargs):
            if hasattr(self,'obj'):
                return getattr(self,'obj')
            obj = super().__call__(*args,**kwargs)
            self.obj = obj
            print('new了一次')
            return obj
    class Person(metaclass=Mymeta):
        def __init__(self,name,age):
            self.name = name
            self.age = age
        def say_hi(self):
            print('hello %s'%self.name)
    p = Person('syy',18)
    p2 = Person('syy',18)
    p3 = Person('syy',18)		#new了一次
    

    冒泡排序

    #冒泡排序
    	从一个列表中,每次取出两个元素,比较大小并排序
        冒泡排序中,一圈中排序的次数 = 列表中元素的个数 - 1 - 再减去圈数的索引
        冒泡排序中,圈数 = 列表中元素的个数 - 1
        
    #例
      #第一圈
    	把列表[1,2,3,5],从大到小进行冒泡排序
      	#第一次排序,得到2的位置
        	[2,1,3,5]
        #第二次排序,得到3的位置
        	[2,3,1,5]
        #第二次排序,得到5的位置
        	[2,3,5,1]
      #第二圈
    	#第一次排序,得到3的位置
        	[3,2,5,1]
        #第二次排序,得到5的位置
        	[3,5,2,1]
      #第三圈
    	#第一次排序,得到5的位置
        	[5,3,2,1]
    
    #代码实现
    l = [1,2,3,5]
    for i in range(len(l)-1):           #i 为圈数的索引(range出的)
        for j in range(len(l)-1-i):     #j 为循环的次数,即排序的次数(range出索引)
            if l[j] < l[j+1]:           #j 为排序的次数,range后可以作为冒泡排序的索引
                l[j],l[j+1] = l[j+1],l[j]
    print(l)		#[5,3,2,1]
    
  • 相关阅读:
    微信redirect_uri域名与后台配置不一致,错误代码10003
    windows安装centos7子系统
    c++中的var_dump
    egret3.x升级5.2
    PHP更改自动加载的顺序
    重定向如何携带cookie
    elasticsearch和mysql排序问题
    npm错误:Error: listen EADDRNOTAVAIL
    Spring Boot WebFlux 集成 Mongodb 数据源操作
    泥瓦匠:程序猿为啥要坚持写原创技术博客?
  • 原文地址:https://www.cnblogs.com/syy1757528181/p/14059511.html
Copyright © 2011-2022 走看看