• 面向对象高级


    1.反射

    反射是所有动态语言比较重要的一个特征 , 提一嘴go , 虽然go是静态的 , 但是也支持反射 , 这是go比较优秀的一点 , python一切皆对象的设计保证了实现反射非常简单

    1.1什么是反射机制

    反射机制指的是在程序的运行状态中
    对于任意一个类,都可以知道这个类的所有属性和方法;
    对于任意一个对象,都能够调用他的任意方法和属性。
    这种动态获取程序信息以及动态调用对象的功能称为反射机制。
    
    反射 : 指的是在程序运行过程中可以"动态(不见棺材不掉泪)"获取对象的信息
    

    补充 :

    任何语言都需要指定类型 , 但你会发现 x = 10 , 没有指定类型啊
    在python中当解释器执行到这一句话的时候 , 才会自动根据值 指定类型
    

    1.2为什么要用反射

    你函数接收到一个参数以后 , 你有可能要访问这个参数内部的一些属性 , 但是你不确定是否有这个属性 , 这样你就要先判断 , 如果有了 , 再访问 , 但是这样判断比较low , 所以我们使用反射来做

    1.3反射怎么用

    # 如何实现反射?
    class People:
        def __init__(self, name, age)
            self.name = name
            self.age = age
    
        def say(self):
            print('<%s: %s>' % (self.name, self.age))
    
    
    obj = People('辣良菜同学')
    
    # 实现反射机制的步骤
    # 1、先通过多din:查看出某一个对象下可以.出哪些属性来
    print(dir(obj))
    # 2、可以通过字符串反射到真正的属性上,得到属性值
    print(obj.__dict__[dir(obj)[-2]])
    
    # 这样些确实实现了反射 , 但是不够优雅 , python给我们专门提供了四个内置函数
    

    四个内置函数的使用 : 通过字符串来操作属性值

    # hasattr() , 见名知意 , 我就不解释了
    print(hasattr(obj,'name'))  # 返回bool值
    print(hasattr(obj,'x'))
    
    # getattr()
    print(getattr(obj, 'name' ))  # 获取name属性值
    print(getattr(obj, 'name1' ,None)) # 当获取不到属性值 , 设置返回值为None
    
    # setattr()
    setattr(obj,'name','EGON')   # obj. name="EGON"  
    
    # delattr()
    delattr(obj, 'name' ) # del obj.name
    print(obj.__dict__)
    
    # 一般hasattr()和getattr() 搭配使用
    

    2.内置方法

    即双下方法 , 在类中定义了这类方法 , 满足某种条件下会自动触发 , 不需要调用

    为什么要用内置方法呢? ---> 就是为了定制化我们的类or对象

    关于内置方法的学习 , 我们只需要知道 , 在什么情况下会自动触发这个方法

    __str__方法

    # __str__ : 在打印对象时会自动触发, 然后将返回值(必须是字符串)当做本次打印的结果输出
    class People:
        def __init__(self, name, age):
            self.name = name
            self.age = age
    
        def __str__(self):
            # print(运行了.,)
            return '<%s: %s>' % (self.name, self.age)
    
    
    obj = People('辣白菜同学', 18)
    print(obj)  # print(obj.__str__())   
    

    __del__方法

    # __del__:在清理对象时触发,会先执行该方法
    class People:
        def __init__(self,name,age):
            self.name =  name
            self.age =  age
            self.x= open('a.txt',mode='w')
            #sef.x=占据的是操作系统资源
        def __del__(self):
            # print( 'run....')
            # 发起系统调用,告诉操作系统回收相关的系统资源
            self.x.close() 
    
    obj = People('alex',99)
    
    # del obj   obj.__del__()
    print('===========')
    

    其他的内置方法还有很多 , 就不一 一介绍了

    详情请见 : https://www.cnblogs.com/linhaifeng/articles/6204014.html

    3.元类

    3.1 什么是元类

    一切源自于一句话:python中一切皆为对象。让我们先定义一个类,然后逐步分析

    class StanfordTeacher(object):
        school='Stanford'
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s says welcome to the Stanford to learn Python' %self.name)
    

    所有的对象都是实例化或者说调用类而得到的(调用类的过程称为类的实例化),比如对象t1是调用类StanfordTeacher得到的

    t1=StanfordTeacher('lili',18)
    print(type(t1)) #查看对象t1的类是<class '__main__.StanfordTeacher'>
    

    如果一切皆为对象,那么类StanfordTeacher本质也是一个对象,既然所有的对象都是调用类得到的,那么StanfordTeacher必然也是调用了一个类得到的,你可能会疑问我StanfordTeacher类 , 不是使用class机制得到的吗 , 实际上任何关键字机制后面 , 都对应了一系列的代码 , 这个代码做的实际上就是造一个类 , 那么类是对象 , 肯定也是通过另一个类加括号实例化的 , 这个类就是元类

    元类就是用来实例化产生类的类 , 前提是我们把类当成对象去看待

    实例化-->类( People )--实例化---对象(obj) , 那么如何查看呢?

    # 查看内置的元类   我们用 class关键字定义的类以及内置的类都是由内置type帮我们实例化产生的# type是内置的元类print(type(People))  # <class 'type'>print(type(int))         # <class 'type'>
    

    3.2class机制分析

    一起分析一下 , class关键字是怎么把类给造出来的?

    # class关键字创造类 People的步骤# 类有三大特征# 1.类名class_name = "People"# 2.类的基类class_bases = (object,)# 3.执行类体代码,拿到类的名称空间class_dic = {}class_body = """    def __init__(self, name, age):        self.name = name        self.age = age    def say(self):        print('<%s: %s>' % (self.name, self.age))"""exec(class_body, {} , class_dic)#print(class_dic)# 4.调用元类# print(type (class_name ,cLass_bases , class _dic))People=type (class_name ,cLass_bases , class _dic)
    

    3.3如何自定元类来控制类的产生

    class Mymeta(type):  # 只有继承了type类的类才是元类(目前先不考虑type类是怎么造的)    # 空对象, " People", (object,),{...}    def __init__(self, x, y, z):        # print(self)        # print(x)        # print(y)        # print(z)        if not x.capitalize():            raise NameError('类名的首字母必须大写')    def __new__(cls, *args, **kwargs):        # 造Mymeta的对象        # super().__new__(cls, *args, **kwargs) or type.__new__(cls, *args, **kwargs)        # 必须返回一个空对象        return type.__new__(cls, *args, **kwargs)# People=Mymeta("People",(object,),{...})# 调用Mymeta发生三件事 , 调用Mymeta就是type.__call__# 1、先造一个空对象=> People# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作# 3、返回初始化好的对象class People(metaclass=Mymeta):  # 默认是type    def __init__(self, name, age):        self.name = name        self.age = age    def say(self):        print('<%s: %s>' % (self.name, self.age))        """强调只要是调用类,那么会一次调用1、类内的__new__2、类内的__init__"""
    

    还有一个返回初始化对象没介绍 , 思考一下 , 调用Mymeta能不能说就是调用__new__ , 不能因为__init__也被调用了 , 而且还有调用顺序 , 实际上调用Mymeta , 是调用__call__方法

    ``call`方法

    class Foo:    def __init__(self, x, y):        self.x = x        self.y = y    def __call__(self,*args,**kwargs):        print('===2>1')obj = Foo(111, 222)print(obj)  # print(obj.__str__())res = obj()  # res = obj.__call__()print(res)# 应用 : 如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
    
    class Mymeta(type):  # 只有继承了type类的类才是元类(目前先不考虑type类是怎么造的)    # 空对象, " People", (object,),{...}    def __init__(self, x, y, z):        # print(self)        # print(x)        # print(y)        # print(z)        if not x.capitalize():            raise NameError('类名的首字母必须大写')    def __new__(cls, *args, **kwargs):        # 造Mymeta的对象        # super().__new__(cls, *args, **kwargs) or type.__new__(cls, *args, **kwargs)        # 必须返回一个空对象        return type.__new__(cls, *args, **kwargs)# People=Mymeta("People",(object,),{...})# 调用Mymeta发生三件事 , 调用Mymeta就是type.__call__# 1、先造一个空对象=> People# 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作# 3、返回初始化好的对象class People(metaclass=Mymeta):  # 默认是type    def __init__(self, name, age):        self.name = name        self.age = age    def say(self):        print('<%s: %s>' % (self.name, self.age))"""对象()->类内的caLL类()->自定义元类内的__call__自定义元类()->内置元类__call__"""
    

    模板 :

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类    def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>        #1、调用__new__产生一个空对象obj        obj=self.__new__(self) # 此处的self是类OldoyTeacher,必须传参,代表创建一个StanfordTeacher的对象obj        #2、调用__init__初始化空对象obj        self.__init__(obj,*args,**kwargs)        #3、返回初始化好的对象obj        return objclass StanfordTeacher(object,metaclass=Mymeta):    school='Stanford'    def __init__(self,name,age):        self.name=name        self.age=age    def say(self):        print('%s says welcome to the Stanford to learn Python' %self.name)t1=StanfordTeacher('lili',18)print(t1.__dict__) #{'name': 'lili', 'age': 18}
    

    上例的__call__相当于一个模板,我们可以在该基础上改写__call__的逻辑从而控制调用StanfordTeacher的过程,比如将StanfordTeacher的对象的所有属性都变成私有的

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类    def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>        #1、调用__new__产生一个空对象obj        obj=self.__new__(self) # 此处的self是类StanfordTeacher,必须传参,代表创建一个StanfordTeacher的对象obj        #2、调用__init__初始化空对象obj        self.__init__(obj,*args,**kwargs)        # 在初始化之后,obj.__dict__里就有值了        obj.__dict__={'_%s__%s' %(self.__name__,k):v for k,v in obj.__dict__.items()}        #3、返回初始化好的对象obj        return objclass StanfordTeacher(object,metaclass=Mymeta):    school='Stanford'    def __init__(self,name,age):        self.name=name        self.age=age    def say(self):        print('%s says welcome to the Stanford to learn Python' %self.name)t1=StanfordTeacher('lili',18)print(t1.__dict__) #{'_StanfordTeacher__name': 'lili', '_StanfordTeacher__age': 18}
    

    3.4属性查找

    元类不是父类

    class Mymeta(type):     n=444    def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>        obj=self.__new__(self)        print(self.__new__ is object.__new__) #Trueclass Bar(object):    n=333    # def __new__(cls, *args, **kwargs):    #     print('Bar.__new__')class Foo(Bar):    n=222    # def __new__(cls, *args, **kwargs):    #     print('Foo.__new__')class StanfordTeacher(Foo,metaclass=Mymeta):    n=111    school='Stanford'    def __init__(self,name,age):        self.name=name        self.age=age    def say(self):        print('%s says welcome to the Stanford to learn Python' %self.name)    # def __new__(cls, *args, **kwargs):    #     print('StanfordTeacher.__new__')StanfordTeacher('lili',18) #触发StanfordTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
    

    总结,Mymeta下的__call__里的self.__new__在StanfordTeacher、Foo、Bar里都没有找到__new__的情况下,会去找object里的__new__,而object下默认就有一个__new__,所以即便是之前的类均未实现__new__,也一定会在object中找到一个,根本不会、也根本没必要再去找元类Mymeta->type中查找__new__

    我们在元类的__call__中也可以用object.__new__(self)去造对象

    image-20211023111626129

    但我们还是推荐在__call__中使用self.__new__(self)去创造空对象,因为这种方式会检索三个类StanfordTeacher->Foo->Bar,而object.__new__则是直接跨过了他们三个

    最后说明一点

    class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类    n=444    def __new__(cls, *args, **kwargs):        obj=type.__new__(cls,*args,**kwargs) # 必须按照这种传值方式	print(obj.__dict__)        # return obj # 只有在返回值是type的对象时,才会触发下面的__init__        return 123    def __init__(self,class_name,class_bases,class_dic):        print('run。。。')class StanfordTeacher(object,metaclass=Mymeta): #StanfordTeacher=Mymeta('StanfordTeacher',(object),{...})    n=111    school='Stanford'    def __init__(self,name,age):        self.name=name        self.age=age    def say(self):        print('%s says welcome to the Stanford to learn Python' %self.name)print(type(Mymeta)) #<class 'type'># 产生类StanfordTeacher的过程就是在调用Mymeta,而Mymeta也是type类的一个对象,那么Mymeta之所以可以调用,一定是在元类type中有一个__call__方法# 该方法中同样需要做至少三件事:# class type:#     def __call__(self, *args, **kwargs): #self=<class '__main__.Mymeta'>#         obj=self.__new__(self,*args,**kwargs) # 产生Mymeta的一个对象#         self.__init__(obj,*args,**kwargs) #         return obj
    
  • 相关阅读:
    luogu P1486 [NOI2004]郁闷的出纳员
    Luogu P1894 [USACO4.2]The Perfect Stall
    关于中间6个月停更通知
    Luogu P1381油滴扩展
    没有上司的舞会(题解)
    幂的模运算(题解)
    闭合区域面积统计(题解)
    字符序列(题解)
    最大连续和(题解)
    排列问题
  • 原文地址:https://www.cnblogs.com/xcymn/p/15721357.html
Copyright © 2020-2023  润新知