• Python入门day33——元类、属性查找


    元类

    一、什么是元类
    # 元类就是用来实例化产生类的类
    # 关系:元类---实例化---->类(People)---实例化---->对象(obj)
    python中一切皆为对象。让我们先定义一个类,然后逐步分析。
    
    class Pelple(object):
    
        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是调用类Pelple得到的
    
    t1=Pelple('lili',18)
    
    print(type(t1)) # 查看对象t1的类是<class '__main__.Pelple'>
    
    如果一切皆为对象,那么类Pelple本质也是一个对象,既然所有的对象都是调用类得到的,那么Pelple必然也是调用了一个类得到的,这个类称为元类
    
    于是我们可以推导出===>产生Pelple的过程一定发生了:Pelple=元类(...)
    
    print(type(Pelple)) # 结果为<class 'type'>,证明是调用了type这个元类而产生的Pelple,即默认的元类为type。
    
    # 查看内置的元类:
    # 1、type是内置的元类
    # 2、我们用class关键字定义的所有的类以及内置的类都是由元类type实例化产生
    print(Pelple,type(People)) # <<class '__main__.People'> <class 'type'>
    print(type(int)) # <class 'type'>
    
    二、 class关键字创建类的流程分析
    # 类有三大特征:
    # 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.name))
    """
    exec(class_body,{},class_dic)
    # print(class_dic) # {'__init__': <function __init__ at 0x000001DADBA49160>, 'say': <function say at 0x000001DADBA491F0>}
    
    # 4、调用元类
    People=type(class_name,class_bases,class_dic)
    print(People) # <class '__main__.People'> 跟之前的结果一样
    
    三、如何自定义元类来控制类的创建
    class Mymeta(type): # 只有继承了type类的类才是元类
        #            空对象,"People",(),{...}
        def __init__(self,x,y,z): # People=Mymeta("People",(object,),{...})
            print('run22222222222....')
            print(self)
            print(x)
            print(y)
            print(z)
            print(self.__bases__)
            if not x.istitle():
                raise NameError('类名的首字母必须大写')
    
        #          当前所在的类,调用类时所传入的参数
        def __new__(cls, *args, **kwargs):
            # 造Mymeta的对象
            print('run1111111111.....')
            # return super().__new__(cls,*args, **kwargs)
            return type.__new__(cls,*args, **kwargs)
    
    # 调用Mymeta发生三件事,调用Mymeta就是type.__call__
    # 1、先造一个空对象=>People,调用Mymeta类内的__new__方法
    # 2、调用Mymeta这个类内的__init__方法,完成初始化对象的操作
    # 3、返回初始化好的对象
    
    class People(metaclass=Mymeta):
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s:%s' %(self.name,self.name))
    # 结果        
    run1111111111.....
    run22222222222....
    <class '__main__.People'>
    People
    ()
    {'__module__': '__main__', '__qualname__': 'People', '__init__': <function People.__init__ at 0x0000023551A891F0>, 'say': <function People.say at 0x0000023551A893A0>}
    (<class 'object'>,)
    
    # 强调:
    # 只要是调用类,那么会一次调用
    # 1、类内的__new__
    # 2、类内的__init__
    
    四、自定义元类控制类的调用
    # 五:__call__
    class Foo:
        def __init__(self,x,y):
            self.x=x
            self.y=y
    
        #            obj,1,2,3,a=4,b=5,c=6
        def __call__(self,*args,**kwargs):
            print('===>',args,kwargs)
            return 123
    
    obj=Foo(111,222)
    print(obj) # obj.__str__ <__main__.Foo object at 0x000002008FB85700>
    res=obj(1,2,3,a=4,b=5,c=6) # res=obj.__call__()
    print(res) 
    # 结果
    ===> (1, 2, 3) {'a': 4, 'b': 5, 'c': 6}
    123
    
    # 应用:如果想让一个对象可以加括号调用,需要在该对象的类中添加一个方法__call__
    # 总结:
    # 对象()->类内的__call__
    # 类()->自定义元类内的__call__
    # 自定义元类()->内置元类__call__
    
    五、自定义元类控制类的调用=》类的对象的产生
    class Mymeta(type): # 只有继承了type类的类才是元类
        def __call__(self, *args, **kwargs):
            # 1、Mymeta.__call__函数内会先调用People内的__new__
            people_obj=self.__new__(self)
            # 2、Mymeta.__call__函数内会调用People内的__init__
            self.__init__(people_obj,*args, **kwargs)
    
            # print('people对象的属性:',people_obj.__dict__)
            people_obj.__dict__['xxxxx']=11111
            # 3、Mymeta.__call__函数内会返回一个初始化好的对象
            return people_obj
    
    # 类的产生
    # People=Mymeta()=》type.__call__=>干了3件事
    # 1、type.__call__函数内会先调用Mymeta内的__new__
    # 2、type.__call__函数内会调用Mymeta内的__init__
    # 3、type.__call__函数内会返回一个初始化好的对象
    
    class People(metaclass=Mymeta):
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
        def say(self):
            print('%s:%s' %(self.name,self.name))
    
        def __new__(cls, *args, **kwargs):
            # 产生真正的对象
            return object.__new__(cls)
    
    # 类的调用
    # obj=People('egon',18) =》Mymeta.__call__=》干了3件事
    # 1、Mymeta.__call__函数内会先调用People内的__new__
    # 2、Mymeta.__call__函数内会调用People内的__init__
    # 3、Mymeta.__call__函数内会返回一个初始化好的对象
    
    obj1=People('egon',18)
    obj2=People('yu',20)
    print(obj1) #  <__main__.People object at 0x000002A8520C5CD0>
    print(obj2) #  <__main__.People object at 0x000002B3E9C601C0>
    print(obj1.__dict__) # {'name': 'egon', 'age': 18, 'xxxxx': 11111}
    print(obj2.__dict__) # {'name': 'yu', 'age': 20, 'xxxxx': 11111}
    

    属性查找

    # 属性查找的原则:对象-》类-》父类
    # 切记:父类 不是 元类
    
    class Mymeta(type):
        n=444
    
        def __call__(self, *args, **kwargs): #self=<class '__main__.StanfordTeacher'>
            obj=self.__new__(self) # StanfordTeacher.__new__
            # obj=object.__new__(self)
            print(self.__new__ is object.__new__) # True
            self.__init__(obj,*args,**kwargs)
            return obj
    
    class Bar(object):
        # n=333
    
        # def __new__(cls, *args, **kwargs):
        #     print('Bar.__new__')
        pass
    
    class Foo(Bar):
        # n=222
    
        # def __new__(cls, *args, **kwargs):
        #     print('Foo.__new__')
        pass
    
    class StanfordTeacher(Foo,metaclass=Mymeta):
        # n=111
    
        def __init__(self,name,age):
            self.name=name
            self.age=age
    
    
    obj=StanfordTeacher('lili',18)
    print(obj.__dict__) # {'name': 'lili', 'age': 18}
    # print(obj.n) # 报错
    print(StanfordTeacher.n) # 444
    
  • 相关阅读:
    HDU 2888 Check Corners (模板题)【二维RMQ】
    POJ 3264 Balanced Lineup(模板题)【RMQ】
    poj 3368 Frequent values(经典)【RMQ】
    SPOJ RPLN (模板题)(ST算法)【RMQ】
    UVA 796 Critical Links(模板题)(无向图求桥)
    UVA 315 Network (模板题)(无向图求割点)
    POJ 2029 Get Many Persimmon Trees (模板题)【二维树状数组】
    poj 3067 Japan 【树状数组】
    POJ 2481 Cows 【树状数组】
    POJ 1195 Mobile phones【二维树状数组】
  • 原文地址:https://www.cnblogs.com/yding/p/12708681.html
Copyright © 2020-2023  润新知