反射与元类

1.isinstance与issubclass  

  在介绍反射之前,先来介绍两个关于类的内置方法,第一个是用来判断对象是否是某一类的对象(以前常说的判断是否是某一类型,类与类型其实是一个概念),第二个则是用来判断某一类是否是继承了另一个类

l=list([1,2,3])
print(isinstance(l,list)) #True
class People:
    def __init__(self):
        pass
class Chinese(People):
    def __init__(self):
        pass
print(issubclass(Chinese,People))#True
#两者用法一致,均是将判断的对象与对象可能是某一类型作为第一第二参数传入

2.反射

  先来给反射下一个定义:反射就是通过字符串来操作python代码中的变量,函数,甚至类与方法,(如何去理解这句话呢,试想我们如果想让打印机打印,是不是要给它一个打印的指令呢,现在如果说你必须通过输入的形式去直接命令计算机,让计算机能理解你输入的字符串到底是对应什么操作,关键在于我们提供input输入的都是字符串,而计算机是无法直接识别字符串的),那就要介绍反射中的四小龙了,

1.hasattr 判断一个变量是否能够加点调用一个名字,返回值为True 或 False

class Foo:
    x=1
    def __init__(self):
        pass
    def tell(self):
        print(‘from tell‘)
print(hasattr(Foo,‘x‘))

2.getattr 直接获取一个变量中的名字的值

class Foo:
    x=1
    def __init__(self):
        pass
    def tell(self):
        print(‘from tell‘)
obj=Foo()
getattr(obj,‘tell‘)() #from tell

3.setattr

class Foo:
    x=1
    def __init__(self):
        pass
    def tell(self):
        print(‘from tell‘)
obj=Foo()
print(Foo.x) #1
setattr(Foo,‘x‘,11)
print(Foo.x)#11

4.delattr

class Foo:
    x=1
    def __init__(self):
        pass
    def tell(self):
        print(‘from tell‘)
obj=Foo()
print(Foo.x) #1
delattr(Foo,‘x‘)
print(Foo.x) #AttributeError: type object ‘Foo‘ has no attribute ‘x‘

  总结:上面这四个方法需要注意的地方有以下几点:1.括号里面的传入的前后参数之间,肯定是可以加点再加名字的形式调用(也就是说,只要是设计到通过点加名字就可以调用的形式,都可以转化成上面这四种方式去操作,比如:导入模块后应用模块中的名字,类中属性方法的调用等),2.传入的参数中要寻找的参数必须是以字符串的形式的传入,咋一看好像这种方式更加复杂,但是恰恰相反,请看例子

class People:
    country=‘China‘
    def __init__(self,name):
        self.name=name
    def tell(self):
        print(‘from tell‘)
    def check(self):
        print(‘from check‘)
p1=People(‘jby‘)
cmd=input(‘please input your cmd‘).strip()
if hasattr(p1,cmd):
    getattr(p1,cmd)()

  这样我们就可以直接将用户输入的字符串直接反射到对应的名字身上,从而直接调用~

3.类的内置方法补充

  1.__str__ 在对象,在遇到被打印的情况下,自动触发~

class People:
    def __init__(self,name,age,sex):
        self.name=name
        self.age=age
        self.sex=sex
    def __str__(self):#__str__内置方法,遇到print自动触发返回字符串格式类型的数据(可自己定制)
        return ‘<name:%s,age:%s,sex:%s>‘%(self.name,self.age,self.sex)
l=list([1,2,3])#用list类产生的对象为什么在打印的时候会直接打印对象里面包含的所有值而不是内存地址原因就是内部有__str__方法
print(l)
p1=People(‘alex‘,73,‘male‘)
print(p1)

  2.__del__ 在对象被删除的情况下(在对象被删除之前)自动触发(这里的删除可以是主动删除也可能是程序执行完毕,回收资源删除)

class Myopen:
    def __init__(self,filename,mode=‘r‘,encoding=‘utf-8‘):
        self.filename=filename
        self.mode=mode
        self.encoding=encoding
        self.f=open(filename,mode=mode,encoding=encoding)
        #这里的open申请了系统资源,在程序关闭时应用程序会回收自己资源,但是系统资源没有被回收
    def __str__(self):
        return ‘%s‘%self.f.read()
    def __del__(self):
        #这里的__del__会在对象被删除之前(被系统回收),执行将系统资源先回收了
        #所有__del__主要用于设计到回收资源这块的应用
        print(‘正在删除系统资源‘)
        self.f.close()
file=Myopen(‘settings.py‘)
print(file)

  3.__call__ 在对象被调用的情况下,自动触发

class Foo:
    def __init__(self):
        pass
    def __str__(self):
        return ‘aaa‘
    def __del__(self):
        pass
    def __call__(self, *args, **kwargs):#(调用对象时就会自动触发此方法的执行将对象当作第一个参数传给self,将对象括号内的参数传给args和kwargs)
        print(‘执行了__call__‘,args,kwargs)

obj=Foo()
obj(1,2,3,a=2,b=3,c=1)#执行了__call__ (1, 2, 3) {‘a‘: 2, ‘b‘: 3, ‘c‘: 1}

4.元类

  1.python中一切皆对象,相信你已经似乎搞清楚了,那这里要揭示我们平常用class定义出来的类,它其实也是一个对象,它也是通过类实例化得到的,这个类就叫做元类!

在介绍元类之前需要先了解一个方法:exec,先介绍它的一个牛逼之处,它可以执行字符串中的代码,与我们执行程序文件无异,也会产生名称空间并将执行产生的名字全部丢到名称空间中,默认情况下会将产生的名字全部存放到局部名称空间中,了解到这里之后就可以介绍exec具体的用法了

exec——它需要三个参数,第一个就是字符串形式的代码,第二个是全局作用域(字典),第三个就是局部作用域(字典)。后面两个都是在执行了第一个参数内的代码后exec会自动将产生的全局作用名放入中间的参数中,将参数的局部作用名放入最后一个参数中

global x
x=1

y=2
z=3
‘‘‘
g={}
l={}
exec(code,g,l)
print(g,l) #g={‘x‘:1},l={‘y‘:2,‘z‘:3}

  2.元类:

    元类是类的类,是类的模板

    元类是用来控制如何创建类的,正如类是创建对象的模板一样,而元类的主要目的是为了控制类的创建行为

    元类的实例化的结果为我们用class定义的类,正如类的实例为对象(f1对象是Foo类的一个实例Foo类是 type 类的一个实例)

    type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象

  3.创建类的两种方式:

    第一种就是通过class关键字来自动帮我们调用type创造出类

    第二种就是我们自己定义出元类从而创建类

  在创建类之前需要准备的知识点有,创建类必须要有三个关键参数:1.类名,2.父类名,3.类的名称空间。还记得我们在通过类创造对象时的步骤吗?这里先回顾一下,第一步调用类产生一个空的对象,第二步触发类中__init__方法的自动执行完成初始化,第三步就是将初始化的结果返回给空对象。

#准备工作:

#创建类主要分为三部分
类名
类的父类
类体
#类名
class_name=‘Chinese‘
#类的父类
class_bases=(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_dic={}
exec(class_body,globals(),class_dic)
print(class_dic)
#{‘country‘: ‘China‘, ‘talk‘: <function talk at 0x101a560c8>, ‘__init__‘: <function __init__ at 0x101a56668>}
Foo=type(class_name,class_bases,class_dic) #实例化type得到对象Foo,即我们用class定义的类Foo
print(Foo)
print(type(Foo))
print(isinstance(Foo,type))
‘‘‘
<class ‘__main__.Chinese‘>
<class ‘type‘>
True
‘‘‘

  步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典

  步骤二:调用元类type(也可以自定义)来产生类Chinese

  我们看到,type 接收三个参数:

  • 第 1 个参数是字符串 ‘Foo’,表示类名
  • 第 2 个参数是元组 (object, ),表示所有的父类
  • 第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法

  补充:若Foo类有继承,即class Foo(Bar):.... 则等同于type(‘Foo‘,(Bar,),class_dic)

  自定义元类控制类的行为

#一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类(顺便我们也可以瞅一瞅元类如何控制类的行为,工作流程是什么)

#知识储备:
    #产生的新对象 = object.__new__(继承object类的子类)

#步骤一:如果说People=type(类名,类的父类们,类的名称空间),那么我们定义元类如下,来控制类的创建
class Mymeta(type):  # 继承默认元类的一堆属性
    def __init__(self, class_name, class_bases, class_dic):
        if ‘__doc__‘ not in class_dic or not class_dic.get(‘__doc__‘).strip():
            raise TypeError(‘必须为类指定文档注释‘)

        if not class_name.istitle():
            raise TypeError(‘类名首字母必须大写‘)

        super(Mymeta, self).__init__(class_name, class_bases, class_dic)

class People(object, metaclass=Mymeta):
    country = ‘China‘

    def __init__(self, name, age):
        self.name = name
        self.age = age

    def talk(self):
        print(‘%s is talking‘ % self.name)

#步骤二:如果我们想控制类实例化的行为,那么需要先储备知识__call__方法的使用
class People(object,metaclass=type):
    def __init__(self,name,age):
        self.name=name
        self.age=age

    def __call__(self, *args, **kwargs):
        print(self,args,kwargs)

# 调用类People,并不会出发__call__
obj=People(‘egon‘,18)

# 调用对象obj(1,2,3,a=1,b=2,c=3),才会出发对象的绑定方法obj.__call__(1,2,3,a=1,b=2,c=3)
obj(1,2,3,a=1,b=2,c=3) #打印:<__main__.People object at 0x10076dd30> (1, 2, 3) {‘a‘: 1, ‘b‘: 2, ‘c‘: 3}

#总结:如果说类People是元类type的实例,那么在元类type内肯定也有一个__call__,会在调用People(‘egon‘,18)时触发执行,然后返回一个初始化好了的对象obj

#步骤三:自定义元类,控制类的调用(即实例化)的过程
class Mymeta(type): #继承默认元类的一堆属性
    def __init__(self,class_name,class_bases,class_dic):
        if not class_name.istitle():
            raise TypeError(‘类名首字母必须大写‘)

        super(Mymeta,self).__init__(class_name,class_bases,class_dic)

    def __call__(self, *args, **kwargs):
        #self=People
        print(self,args,kwargs) #<class ‘__main__.People‘> (‘egon‘, 18) {}

        #1、实例化People,产生空对象obj
        obj=object.__new__(self)

        #2、调用People下的函数__init__,初始化obj
        self.__init__(obj,*args,**kwargs)

        #3、返回初始化好了的obj
        return obj

class People(object,metaclass=Mymeta):
    country=‘China‘

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def talk(self):
        print(‘%s is talking‘ %self.name)

obj=People(‘egon‘,18)
print(obj.__dict__) #{‘name‘: ‘egon‘, ‘age‘: 18}

#步骤四:
class Mymeta(type): #继承默认元类的一堆属性
    def __init__(self,class_name,class_bases,class_dic):
        if not class_name.istitle():
            raise TypeError(‘类名首字母必须大写‘)

        super(Mymeta,self).__init__(class_name,class_bases,class_dic)

    def __call__(self, *args, **kwargs):
        #self=People
        print(self,args,kwargs) #<class ‘__main__.People‘> (‘egon‘, 18) {}

        #1、调用self,即People下的函数__new__,在该函数内完成:1、产生空对象obj 2、初始化 3、返回obj
        obj=self.__new__(self,*args,**kwargs)

        #2、一定记得返回obj,因为实例化People(...)取得就是__call__的返回值
        return obj

class People(object,metaclass=Mymeta):
    country=‘China‘

    def __init__(self,name,age):
        self.name=name
        self.age=age

    def talk(self):
        print(‘%s is talking‘ %self.name)

    def __new__(cls, *args, **kwargs):
        obj=object.__new__(cls)
        cls.__init__(obj,*args,**kwargs)
        return obj

obj=People(‘egon‘,18)
print(obj.__dict__) #{‘name‘: ‘egon‘, ‘age‘: 18}

#步骤五:基于元类实现单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存
class Mysql:
    __instance=None
    def __init__(self,host=‘127.0.0.1‘,port=‘3306‘):
        self.host=host
        self.port=port

    @classmethod
    def singleton(cls,*args,**kwargs):
        if not cls.__instance:
            cls.__instance=cls(*args,**kwargs)
        return cls.__instance

obj1=Mysql()
obj2=Mysql()
print(obj1 is obj2) #False

obj3=Mysql.singleton()
obj4=Mysql.singleton()
print(obj3 is obj4) #True

#应用:定制元类实现单例模式
class Mymeta(type):
    def __init__(self,name,bases,dic): #定义类Mysql时就触发
        self.__instance=None
        super().__init__(name,bases,dic)

    def __call__(self, *args, **kwargs): #Mysql(...)时触发

        if not self.__instance:
            self.__instance=object.__new__(self) #产生对象
            self.__init__(self.__instance,*args,**kwargs) #初始化对象
            #上述两步可以合成下面一步
            # self.__instance=super().__call__(*args,**kwargs)

        return self.__instance
class Mysql(metaclass=Mymeta):
    def __init__(self,host=‘127.0.0.1‘,port=‘3306‘):
        self.host=host
        self.port=port

obj1=Mysql()
obj2=Mysql()

print(obj1 is obj2)

5.单例模式

  指的是不管调用多少次产生的对象都指向同一个地址,这样的好处就在于充分的节省空间,但是仅用于每次实例化对象都是一样的结果(比如需要从配置文件中导入配置信息,而且一个程序中需要重复用到,这个时候用单例模式比较好)

import settings
class MySQL:
    __instance=None  #标识位
    def __init__(self,ip,port):
        self.ip=ip
        self.port=port
    @classmethod
    def singleton(cls):
        if not cls.__instance:
            obj=cls(settings.IP, settings.PORT)
            cls.__instance=obj       return obj
        return cls.__instance

obj4=MySQL.singleton()
obj5=MySQL.singleton()
obj6=MySQL.singleton()
print(obj4 is obj5 is obj6) #True

  上述这种单例模式其实我们在之前学绑定方法与分绑定方法时就已经接触过了,就是将方法从绑定给对象改为绑定给类,只需要再加个标识位加以控制即可做到单例模式,可以看出这里的单例模式其实是我们在内部进行了一步监控,如果产生的结果与我的标识符一致,那就直接将我的标识位上的对象给出去,而不继续执行实例化~

6.练习题

练习一:在元类中控制把自定义类的数据属性都变成大写

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}
‘‘‘

练习二:在元类中控制自定义的类无需__init__方法

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__)

  

原文地址:https://www.cnblogs.com/Dominic-Ji/p/8868196.html

时间: 2024-10-05 10:17:06

反射与元类的相关文章

python3全栈开发-内置函数补充,反射,元类,__str__,__del__,exec,type,__call__方法

一.内置函数补充 1.isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(object): pass obj = Foo() print(isinstance(obj, Foo)) #结果为True 2.issubclass(sub, super)检查sub类是否是 super 类的派生类 class Foo(object): pass class Bar(Foo): pass print(issubclass(Bar, Foo)) #结果为True

反射,元类

内置函数 # class Foo: # pass # obj=Foo # print(isinstance(obj,Foo)) #在python3中统一类与类型的概念 #d={"x":1}#d=dict({"x":1}) #print(type(d) is dict) # print(isinstance(d,dict)) isinstance(对象名,类名) 判断是否是实列 # issubclass(子类,父类) 判断一个类是不是另一个类的子类 # class P

反射、元类 练习

'''不会3.在元类中控制自定义的类产生的对象相关的属性全部为隐藏属性4.基于元类实现单例模式''''''1.在元类中控制把自定义类的数据属性都变成大写class Father(type): def __new__(cls,name,bases,dic): update_dic={} for k,v in dic.items(): update_dic.update({k.upper():v}) return type.__new__(cls,name,bases,update_dic) cla

QObject提供了QMetaObject元类信息(相当于RTTI和反射),信号与连接,父子关系,调试信息,属性,事件,继承关系,窗口类型,线程属性,时间器,对象名称,国际化

元类信息(相当于RTTI和反射),信号与连接,父子关系,调试信息,属性,事件,继承关系,窗口类型,线程属性,时间器,对象名称,国际化其中元类又提供了:classInfo,className,构造函数,多重祖先元类,method, property, Enumerator, Signal, Slot等等 http://doc.qt.io/qt-5/qobject.html http://doc.qt.io/qt-5/qmetaobject.html 我感觉Qt的出现,除了提供GUI以外,主要就是提

Python基础- 类和对象(使用、继承、派生、组合、接口、多态、封装、property、staticmethod、classmethod、反射、slots、上下文管理协议、元类)

标签: python对象 2017-07-01 16:28 79人阅读 评论(0) 收藏 举报  分类: python(11)  版权声明:本文为广大朋友交流学习,如有纰漏望不吝赐教,若存在版权侵犯请及时与我联系 目录(?)[+] 一.初识类和对象 在python3中类型就是类 先定义类在产生相对应的对象,也就是现有了概念再有了实体 class Garen: camp = 'Demacia' def attack(self): print('attack') 1.如何使用类 在python3:

python面向对象高级:反射、魔法方法、元类

自省/反射什么是反射?自省也称作反射,这个性质展示了某对象是如何在运行期取得自身信息的.并且在python里,反射可以使得程序运行时对象拥有增删改查它本身属性或行为的一种能力如果Python不支持某种形式的自省功能,dir和type内建函数,将很难正常工作.还有那些特殊属性,像__dict__,__name__及__doc__反射的使用场景? 即插即用,即可以事先定义好接口,接口只有在被完成后才会真正执行 比如:如果和别人共同合作开发项目,但是需要用到对方的类的方法,对方还没完成 f1=FtpC

反射、自定义内置方法来定制类的功能、元类

一.反射 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)

7.28 多态 反射 元类

多态 1.什么是多态 多态指的是一类事物有多种形态 例如: 动物有多种形态: 人,狗,猪 在程序中多态指的是,不同对象可以响应相同方法,并可以有自己不同的实现方式 2.为什么需要多态 案例分析: import abc class Animal(metaclass=abc.ABCMeta): #同一类事物:动物 @abc.abstractmethod def talk(self): pass class People(Animal): #动物的形态之一:人 def talk(self): prin

Python第二十一课(反射/元类)

Python第二十一课(反射/元类)    >>>思维导图>>>中二青年 反射reflect 什么是反射, 其实是反省,自省的意思 反射指的是一个对象应该具备,可以检测,修改,增加自身属性的能力 反射就是通过字符串操作属性 涉及的四个函数,这四个函数就是普通的内置函数 没有双下划綫,与print等等没有区别 hasattr getattr setattr delattr p = Person("jack",18,"man") #