原文出处:http://www.cnblogs.com/linhaifeng/articles/8029564.html

exec的使用

  1. #可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
  2. g={
  3. 'x':,
  4. 'y':
  5. }
  6. l={}
  7.  
  8. exec('''
  9. global x,z
  10. x=
  11. z=
  12.  
  13. m=
  14. ''',g,l)
  15.  
  16. print(g) #{'x': , 'y': ,'z':,......}
  17. print(l) #{'m': }

引子(类也是对象)

  1. class Foo:
  2. pass
  3.  
  4. f1=Foo() #f1是通过Foo类实例化的对象

python中一切皆对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例), 因而我们可以将类当做一个对象去使用。

对象的使用:

1、都可以被引用,x=obj

2、都可以当做函数的参数传入

3、都可以当做函数的返回值

4、都可以当做容器类的元素, l = [func, obj, 1]

上例可以看出f1是由Foo这个类产生的对象,而Foo本身也是对象,那它又是由哪个类产生的呢?

  1. print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
  2. print(type(Foo)) # 输出:<type 'type'>

什么是元类?

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

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

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

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

创建类的两种方式

方式一:使用class关键字

  1. class Chinese(object):
  2. country='China'
  3. def __init__(self,name,age):
  4. self.name=name
  5. self.age=age
  6. def talk(self):
  7. print('%s is talking' %self.name)

方式二:就是手动模拟class创建类的过程):将创建类的步骤拆分开,手动去创建

  1. #准备工作:
  2.  
  3. #创建类主要分为三部分
  4.  
  5.    类名
  6.  
  7.    类的父类
  8.  
  9.    类的__dict__
  10.  
  11. #类名
  12. class_name='Chinese'
  13. #类的父类
  14. class_bases=(object,)
  15. #类体
  16. class_body="""
  17. country='China'
  18. def __init__(self,name,age):
  19. self.name=name
  20. self.age=age
  21. def talk(self):
  22. print('%s is talking' %self.name)
  23. """

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

  1. class_dic={}
  2. exec(class_body,globals(),class_dic)
  3.  
  4. print(class_dic)
  5. #{'country': 'China', 'talk': <function talk at 0x101a560c8>, '__init__': <function __init__ at 0x101a56668>}

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

  1. Foo=type(class_name,class_bases,class_dic) #实例化type得到对象Foo,即我们用class定义的类Foo
  2.  
  3. print(Foo)
  4. print(type(Foo))
  5. print(isinstance(Foo,type))
  6. '''
  7. <class '__main__.Chinese'>
  8. <class 'type'>
  9. True
  10. '''

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

  • 第一个参数是字符串'Foo',  表示类名
  • 第二个参数是元组(object,),表示所有的父类
  • 第三个参数是字典,这里是一个孔子点,表示没有定义属性和方法

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

类的几个常用方法

1.__new__方法

__new__方法接受的参数和__init__一样,但__init__实在类对象(创建一个空对象)创建之后调用,而__new__方法正式创建这个空对象的方法。__init__方法里面的self实际都是__new__所创建的空对象,__new__返回值就是已经实例化完的对象(__init__),属于类级别方法

2.__init__方法

__init__通常用于初始化一个实例,控制这个初始化的过程,比如添加一属性,做一些额外的操作,发生在类实例(__new__创建出来的空对象)被创建完之后。它是实例级别的方法。无返回值。

3.__call__方法

构造方法的执行是创建对象出发的,即:对象=类名();而对于__call__方法的执行是实例化的时候‘对象=类名()’触发的。

自定义元类控制(实例化)类的行为

type是所有新式类的类,所有类都可以说是type创建的
object是所有新式类的父类

# 一个类没有定义自己的元类,默认他的元类就是type, 除了使用元类type, 用户也可以通过继承type来自定义元类

  1. #步骤一:如果说People=type(类名,类的父类们,类的名称空间),那么我们定义元类如下,来控制类的创建
  2.  
  3. class Mymeta(type): # 继承默认元类的一堆属性
  4. def __init__(self, class_name, class_bases, class_dic):
  5. # 实例化类的时候,必须给 元类传入 class_name, class_bases, class_dic三个参数, 我们就可以根据传入的这三个参数来控制类的创建(实例化)
  6. if '__doc__' not in class_dic or not class_dic.get('__doc__').strip():
  7. raise TypeError('必须为类指定文档注释')
  8.  
  9. if not class_name.istitle():
  10. raise TypeError('类名首字母必须大写')
  11.  
  12. super(Mymeta, self).__init__(class_name, class_bases, class_dic)
  13.  
  14. class People(object, metaclass=Mymeta):
  15. country = 'China'
  16.  
  17. def __init__(self, name, age):
  18. self.name = name
  19. self.age = age
  20.  
  21. def talk(self):
  22. print('%s is talking' % self.name)
  23.  
  24. # People这个类在创建的时候注明了他的元类是Mymeta,也就是我们定义的元类, 所以必须满足我们的控制条件,类名首字母必须大写, 必须要有文档注释切不能为空, 否则报出错误信息。

自定义元类控制类的调用        --->    (实例化)类的实例化对象的行为

  1. #如果我们想控制类实例化的行为,那么需要先储备知识__call__方法的使用
  1. class People(object,metaclass=type):
  2. def __init__(self,name,age):
  3. self.name=name
  4. self.age=age
  5.  
  6. def __call__(self, *args, **kwargs):
  7. print(self,args,kwargs)
  8.  
  9. # 调用类People,并不会触发__call__
  10. obj=People('egon',)
  11.  
  12. # 调用对象obj(,,,a=,b=,c=),才会出发对象的绑定方法obj.__call__(,,,a=,b=,c=)
  13. obj(,,,a=,b=,c=) #打印:<__main__.People object at 0x10076dd30> (, , ) {'a': , 'b': , 'c': }
  14.  
  15. #总结:如果说类People是元类type的实例,那么在元类type内肯定也有一个__call__,会在调用People('egon',)时触发执行,然后返回一个初始化好了的对象obj

开始自定义class Mymeta(type): #继承默认元类的一堆属性

  1. class Mymeta(type): #继承默认元类的一堆属性
  2. def __init__(self,class_name,class_bases,class_dic):
  3. if not class_name.istitle():
  4. raise TypeError('类名首字母必须大写')
  5. super(Mymeta,self).__init__(class_name,class_bases,class_dic)
  6. def __call__(self, *args, **kwargs):
  7. #self=People
  8. print(self,args,kwargs) #<class '__main__.People'> ('egon', 18) {}
  9. #1、产生空对象obj
  10. obj=object.__new__(self)
  11. #2、调用People下的函数__init__,初始化obj
  12. self.__init__(obj,*args,**kwargs)
  13. #3、返回初始化好了的obj
  14. return obj
  1. class Chinese(object,metaclass=Mymeta):
    '''
    中文人的类
    '''
    country='China'
  2.  
  3. def __init__(self,namem,age):
    self.name=namem
    self.age=age
  4.  
  5. def talk(self):
    print('%s is talking' %self.name)
  6.  
  7. obj=Chinese('egon',age=18) #Chinese.__call__(Chinese,'egon',18)
  8.  
  9. print(obj.__dict__)
  1.  

自定义元类来实现单例模式

实现方式一

  1. class MySQL:
  2. __instance=None #__instance=obj1
  3.  
  4. def __init__(self):
  5. self.host='127.0.0.1'
  6. self.port=
  7.  
  8. @classmethod
  9. def singleton(cls):
  10. if not cls.__instance:
  11. obj=cls()
  12. cls.__instance=obj
  13. return cls.__instance
  14.  
  15. def conn(self):
  16. pass
  17.  
  18. def execute(self):
  19. pass
  20.  
  21. # obj1=MySQL()
  22. # obj2=MySQL()
  23. # obj3=MySQL()
  24. #
  25. # print(obj1)
  26. # print(obj2)
  27. # print(obj3)
  28.  
  29. obj1=MySQL.singleton()
  30. obj2=MySQL.singleton()
  31. obj3=MySQL.singleton()
  32.  
  33. print(obj1 is obj3)

实现方式二:

  1. class Mymeta(type):
  2. def __init__(self,class_name,class_bases,class_dic):
  3. if not class_name.istitle():
  4. raise TypeError('类名的首字母必须大写')
  5.  
  6. if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
  7. raise TypeError('必须有注释,且注释不能为空')
  8.  
  9. super(Mymeta,self).__init__(class_name,class_bases,class_dic)
  10. self.__instance=None
  11.  
  12. def __call__(self, *args, **kwargs): #obj=Chinese('egon',age=)
  13. if not self.__instance:
  14. obj=object.__new__(self)
  15. self.__init__(obj)
  16. self.__instance=obj
  17.  
  18. return self.__instance
  19.  
  20. class Mysql(object,metaclass=Mymeta):
  21. '''
  22. mysql xxx
  23. '''
  24. def __init__(self):
  25. self.host='127.0.0.1'
  26. self.port=
  27.  
  28. def conn(self):
  29. pass
  30.  
  31. def execute(self):
  32. pass
  33.  
  34. obj1=Mysql()
  35. obj2=Mysql()
  36. obj3=Mysql()
  37.  
  38. print(obj1 is obj2 is obj3)

再看属性查找

在学习完元类后,其实我们用class自定义的类也全都是对象(包括object类本身也是元类type的 一个实例,可以用type(object)查看),我们学习过继承的实现原理,如果把类当成对象去看,将下述继承应该说成是:对象OldboyTeacher继承对象Foo,对象Foo继承对象Bar,对象Bar继承对象object

  1. class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义类
  2. n=
  3.  
  4. def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
  5. obj=self.__new__(self)
  6. self.__init__(obj,*args,**kwargs)
  7. return obj
  8.  
  9. class Bar(object):
  10. n=
  11.  
  12. class Foo(Bar):
  13. n=
  14.  
  15. class OldboyTeacher(Foo,metaclass=Mymeta):
  16. n=
  17.  
  18. school='oldboy'
  19.  
  20. def __init__(self,name,age):
  21. self.name=name
  22. self.age=age
  23.  
  24. def say(self):
  25. print('%s says welcome to the oldboy to learn Python' %self.name)
  26.  
  27. print(OldboyTeacher.n) #自下而上依次注释各个类中的n=xxx,然后重新运行程序,发现n的查找顺序为OldboyTeacher->Foo->Bar->object->Mymeta->type

于是属性查找应该分成两层,一层是对象层(基于c3算法的MRO)的查找,另外一个层则是类层(即元类层)的查找

  1. #查找顺序:
  2. #、先对象层:OldoyTeacher->Foo->Bar->object
  3. #、然后元类层:Mymeta->type

依据上述总结,我们来分析下元类Mymeta中__call__里的self.__new__的查找

  1. class Mymeta(type):
  2. n=
  3.  
  4. def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
  5. obj=self.__new__(self)
  6. print(self.__new__ is object.__new__) #True
  7.  
  8. class Bar(object):
  9. n=
  10.  
  11. # def __new__(cls, *args, **kwargs):
  12. # print('Bar.__new__')
  13.  
  14. class Foo(Bar):
  15. n=
  16.  
  17. # def __new__(cls, *args, **kwargs):
  18. # print('Foo.__new__')
  19.  
  20. class OldboyTeacher(Foo,metaclass=Mymeta):
  21. n=
  22.  
  23. school='oldboy'
  24.  
  25. def __init__(self,name,age):
  26. self.name=name
  27. self.age=age
  28.  
  29. def say(self):
  30. print('%s says welcome to the oldboy to learn Python' %self.name)
  31.  
  32. # def __new__(cls, *args, **kwargs):
  33. # print('OldboyTeacher.__new__')
  34.  
  35. OldboyTeacher('egon',) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找

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

  1.  

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

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

python3-元类的更多相关文章

  1. python3 元类编程的一个例子

    [引子] 虽然我们可以通过“class”语句来定义“类”,但是要想更加细粒度的控制“类”的创建,要使用元类编程才能实现. 比如说我们要实现这样的一个约束.所有项目中用到的类都应该要为它定义的方法提供文 ...

  2. Python3 面向对象之-----元类

    元类 1. 类也是对象 在大多数编程语言中,类就是一组用来描述如何生成一个对象的代码段.在Python中这一点仍然成立: >>> class ObjectCreator(object ...

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

    一.内置函数补充 1.isinstance(obj,cls)检查是否obj是否是类 cls 的对象 class Foo(object): pass obj = Foo() print(isinstan ...

  4. python3全栈开发- 元类metaclass(面试必考题)

    一.知识储备 #exec:三个参数 #参数一:字符串形式的命令 #参数二:全局作用域(字典形式),如果不指定,默认为globals() #参数三:局部作用域(字典形式),如果不指定,默认为locals ...

  5. 元类理解与元类编程 《Python3网络爬虫开发》中第九章代理的使用代码Crawler中代码的理解

    __new__与__init__的理解 __new__()方法是在创建实例之前被调用的,它的作用是创建一个实例,然后返回该实例对象,它是一个静态方法. __init__() 当实例被创建完成之后被调用 ...

  6. Python中的元类和__metaclass__

    1.什么是元类 元类让你来定义某些类是如何被创建的,从根本上说,赋予你如何创建类的控制权.可以把元类想成是一个类中类,或是一个类,它的实例是其它的类.当某个类调用type()函数时,你就会看到它到底是 ...

  7. 5、flask之信号和mateclass元类

    本篇导航: flask实例化参数 信号 metaclass元类解析 一.flask实例化参数 instance_path和instance_relative_config是配合来用的:这两个参数是用来 ...

  8. 【python进阶】详解元类及其应用2

    前言 在上一篇文章[python进阶]详解元类及其应用1中,我们提到了关于元类的一些前置知识,介绍了类对象,动态创建类,使用type创建类,这一节我们将继续接着上文来讲~~~ 5.使⽤type创建带有 ...

  9. Python进阶开发之元类编程

    系列文章 √第一章 元类编程,已完成 ; 本文目录 类是如何产生的如何使用type创建类理解什么是元类使用元类的意义元类实战:ORM . 类是如何产生的 类是如何产生?这个问题肯定很傻.实则不然,很多 ...

  10. python 面向对象进阶之元类metaclass

    一:知识储备 exec exec:三个参数 参数一:字符串形式的命令 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为local ...

随机推荐

  1. spring boot 返回json字符串 null值转空字符串

    @Configuration public class JacksonConfig { @Bean @Primary @ConditionalOnMissingBean(ObjectMapper.cl ...

  2. 2017-12-14python全栈9期第一天第八节之循环语句while

    12,while. while 条件: 循环体 无限循环. 终止循环:1,改变条件,使其不成立. 2,break continue

  3. MyBatis-注解方式整合SSM

    Spring.Spring MVC.MyBatis 整合 一.依赖 <?xml version="1.0" encoding="UTF-8"?> & ...

  4. HDU - 3974 Assign the task (线段树区间修改+构建模型)

    https://cn.vjudge.net/problem/HDU-3974 题意 有一棵树,给一个结点分配任务时,其子树的所有结点都能接受到此任务.有两个操作,C x表示查询x结点此时任务编号,T ...

  5. HDU 1016(素数环 深搜)

    题意是说对一个长度为 n 的数环进行排列,使得相邻两数的和为素数,按从小到大的顺序依次输出. 因为是环,所以总能调整成以 1 为序列首输出.用深度优先搜索的方法即可.在判断素数时由于 n 小于 20, ...

  6. multiprocessing.Manager共享内存的问题记录

    问题:https://stackoverflow.com/questions/8640367/python-manager-dict-in-multiprocessing 使用 multiproces ...

  7. forEach 如何提前终止 跳出运行

    forEach 如何提前终止 跳出运行 try{ arr.forEach(function(item,index){ if (...) { foreach.break=new Error(" ...

  8. Linux文件权限设置

    基本概念 https://linux.cn/article-7418-1.html#3_8880 用户管理 文件权限设置 -添加用户账户08% -理解 /etc/passwd 中的内容12% -理解 ...

  9. Javaweb学习笔记——(二十)——————Javaweb监听器、国际化

    Javaweb监听器     三大组件         *Servlet         *Listener         *Filter Listener:监听器         1.初次相见:A ...

  10. 七、文件IO——I/O处理方式和文件锁

    7.1 I/O 处理方式 7.1.1 I/O处理的五种模型 阻塞I/O模型 若所调用的 I/O 函数没有完成相关的功能就会使进程挂起,直到相关数据到达才会返回.如 终端.网络设备的访问. 非阻塞模型 ...