zoukankan      html  css  js  c++  java
  • 反射、自定义内置方法来定制类的功能、元类

    一、反射

      1. 定义:通过字符串来操作类或者对象属性

      2. 方法:hasattr、getattr、setattr、delattr

      3. 使用方法:

     1 class People:
     2     def __init__(self,name):
     3         self.name=name
     4     def put(self):
     5         print('%s is putting'%self.name)
     6     def get(self):
     7         print('%s get sth'%self.name)
     8     def run(self):
     9         while True:
    10             choice=input('>>').strip()
    11             method=getattr(obj,choice,None)
    12 
    13             # # 判断对象obj里边有没有choice这个属性
    14             # print(hasattr(obj,choice))
    15 
    16             # # obj.choice=z
    17             # setattr(obj,choice,name)
    18 
    19             # # delete boj.msg
    20             # delattr(obj,msg)
    21             if method is None:
    22                 print('command is not exists')
    23             else:
    24                 # getattr 是取得字符串并将该字符串转成可执行的函数
    25                 method()
    26 
    27 obj=People('大王')
    28 obj.run()
    hasattr、setattr、getattr、delattr

      4.反射的好处:可以事先定义好接口,接口只有在被完成后才会真正执行,这实现了即插即用,这其实是一种‘后期绑定’,什么意思?即你可以事先把主要的逻辑写好(只定义接口),然后后期再去实现接口的功能

    二、自定义内置方法来实现定制类的功能

      1. __str__

      

    class People:
        def __init__(self,name,age):
            self.name=name
            self.age=age
        
        # 在打印对象时会自动触发这个功能
        # 此时应该收集跟这个对象有关的信息,输出到屏幕上
        def __str__(self):
            return '<%s : %s>'%(self.name,self.age)
    
    p=People('小天使',17)
    print(p)

      2.__del__析构方法

    class People:
        def __init__(self,name):
            self.name=name
            self.f=open('a.txt','rt',encoding='utf8')
    
        # 会在对象删除之前自动触发
        def __del__(self):
            # print('>>>>>>>')
    
            # 做回收系统资源相关的事情
            self.f.close()
    p=People('魔鬼')
    print('')

    三、元类

      1.定义

      在python中一切都是对象,所以用class 关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,即产生类的类即称为元类。

      2.为何用元类

      元类是负责产生类的,所以学习元类或者自定义元类的目的是为了控制类的产生过程,还可以控制对象的产生过程。

    知识准备:

      内置函数exec的用法:将函数体代码执行一遍

    # 将函数体代码执行一遍,放至局部{}中
    msg='''
    x=1
    def func():
        pass
    '''
    class_dic={}
    exec(msg,{},class_dic)
    print(class_dic)

        __call__的用法

        如果想要让实例化出来的对象变成可以调用的对象,则需要在该对象的类中定义一个__call__的方法,该对象会在调用时自动触发

    class Foo:
        def __call__(self, *args, **kwargs):
            print(self)
            print(args)
            print(kwargs)
    
    obj=Foo()
    obj()
    obj(1,2,3,x=1,y=2)

      执行结果入下图

      3. 创建类的方法有两种

    如果说类也是对象的话,那么用class关键字去创建类的过程也是一个实例化的过程

    该实例化的目的是为了得到一个类,调用的是元类

    3.1 方式一:用默认的元类type

    class People:
        def __init__(self,name):
            self.name=name
    
        def eat(self):
            print('%s is eating...'%self.name)
    
    print(type(People))

    3.1.1 创建类的三个要素 (类名,基类们,类的名称空间)

    class_name='People'     #类名
    class_bases=(object,)   #基类
    class_dic={}            #类的名称空间
    class_body='''          #类体代码
    class People:
        def __init__(self,name):
            self.name=name
    
        def eat(self):
            print('%s is eating...'%self.name)
    
    print(type(People))
    '''
    exec(class_body,{},class_dic)

    3.2 用的自定义的元类

    class Mymeta(type):  # Mymeta=type(...)
        def __init__(self,class_name,class_bases,class_dic):
            super(Mymeta, self).__init__(class_name,class_bases,class_dic)
    class People(object,metaclass=Mymeta): # People=Mymeta(类名,基类们,类的名称空间)
        def __init__(self,name):
            self.name=name
        def eat(self):
            print('%s is eating...'%self.name)
    p=People('小三炮')
    p.eat()

    原理:

      3.2.1 得到一个字符串的类名 class_name = 'People'

      3.2.2 得到一个类的基类们 class_bases = (object,)

      3.3.3 执行类体代码,得到一个类的名称空间 class_dic = {...}

      3.3.4 调用 People = type(class_name,class_bases,class_dic)

    3.3 应用

      自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程

    class Mymeta(type):
        def __init__(self,class_name,class_bases,class_dic):
            super(Mymeta, self).__init__(class_name,class_bases,class_dic)
            print(class_dic) # {'__module__': '__main__', '__qualname__': 'People', '__doc__': ' ',
                            #  '__init__': <function People.__init__ at 0x000001FCE6048AE8>}
            print(class_name)  # People
            print(class_bases)  # (<class 'object'>,)
            if class_dic.get('__doc__') is None and len(class_dic.get('__doc__'))==0:
                raise TypeError('Document must exists and must not be None')
            if not class_name.istitle():
                raise NameError('First letter must be capital')
    class People(object,metaclass=Mymeta):  # if class_name'initials is not capital,it 
                                                      # will report an error
        ''' hey guys '''  
        def __init__(self,name):
            self.name=name
    p=People('明日之星')
     1 class Mymeta(type):
     2 
     3 
     4     def __call__(self, *args, **kwargs):
     5 
     6         # 先造出一个类的空对象
     7         obj=self.__new__(self)
     8 
     9         # 为该空对象初始化独有属性
    10         self.__init__(obj,*args,**kwargs)
    11 
    12         # 返回一个初始化好的对象
    13         return obj
    14 
    15 
    16 class People(object,metaclass=Mymeta):
    17     def __init__(self,name):
    18         print(self)
    19         self.name=name
    20 
    21     def eat(self):
    22         print('%s is eating'%self.name)
    23     def __new__(cls, *args, **kwargs): #(涉及查找顺序)
    24         print(cls)   # <class '__main__.People'>
    25         # cls.__new__(cls) # 错误
    26         obj=super(People,cls).__new__(cls)
    27         return obj
    28 obj=People('大宝',)
    自定义元类来控制类的调用的过程,即类的实例化过程

     3.4 元类中的属性查找顺序

      1.先查找对象的层次

      2.对象层次中若没有,则从元类的层次中寻找

     1 # 将类的属性更改
     2 class Mymeta(type):
     3     def __init__(self,class_name,class_bases,class_dic):
     4         # 控制类Foo的创建
     5         super(Mymeta, self).__init__(class_name,class_bases,class_dic)
     6 
     7     def __call__(self, *args, **kwargs):
     8         # 控制类Foo的调用过程,即控制类Foo的产生过程
     9         obj=self.__new__(self)
    10         self.__init__(obj,*args,**kwargs)
    11         obj.__dict__={'_%s__%s'%(self.__name__,k):v for k,v in obj.__dict__.items()}
    12         # 必须有返回值,否则报AttributeError: 'NoneType' object has no attribute '__dict__'
    13         return obj
    14 
    15 class Foo(object,metaclass=Mymeta):
    16     def __init__(self,name):
    17         self.name=name
    18 
    19 obj=Foo('读书郎')
    20 print(obj.__dict__)
    控制类的产生的例子
  • 相关阅读:
    第四周
    第二次作业
    jsp
    软件测试第一次
    增删改查
    用户添加
    登录
    购物商城
    jsp第七周作业
    jsp第六周作业
  • 原文地址:https://www.cnblogs.com/Smart1san/p/9310677.html
Copyright © 2011-2022 走看看