一、储备知识exec

  储备知识exec:有下面三个参数

  参数一:字符串形式的命令
  参数二:全局作用域(字典形式),如果不指定默认使用globals()
  参数三:局部作用域(字典形式),如果不指定默认就使用locals()

# 格式:exec(object, globals, locals)
# 可以把exec命令的执行当成是一个函数的执行,会将执行期间产生的名字存放于局部名称空间中
g = {
'x':1,
'y':2
} l = {} exec("""
global x,z
x=100
z=200 m=300
""", g, l) print(g) # {'x': 100, 'y': 2,'z':200,......} print(l) # {'m': 300}

exec(object, globals, locals)

二、类也是对象,一切皆对象

class Foo:
pass f1=Foo() #f1是通过Foo类实例化的对象

  python一切皆对象,类本身也是一个对象,当使用关键字class的时候,python解释器在加载class的时候就会创建一个对象(这里的对象指的是类而非类的实例),因而我们可以将类当作一个对象去使用,同样满足第一类对象的概念,对象可以怎么用?
  1、都可以被引用,x=obj(类赋值给一个变量)
  2、都可以当作函数的参数传入(类作为函数参数进行传递)
  3、都可以当作函数的返回值(把类作为函数的返回值)
  4、都可以当作容器类的元素,l=[func, time, obj, 1] (在运行时动态地创建类)
  换句话说,符合上述条件就是一个对象,如果类Foo本身就是对象,那需要探究Foo是由哪个类产生的。

#type函数可以查看类型,也可以用来查看对象的类,二者是一样的
print(type(f1)) # 输出:<class '__main__.Foo'> 表示,obj 对象由Foo类创建
print(type(Foo)) # 输出:<type 'type'>

三、元类概念

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

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

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

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

四、创建类的两种方式

1、使用class关键字

class Chinese:  # 这个类其实是元类实例化的一个对象
country = 'China' def __init__(self, name, age):
self.name = name
self.age = age def talk(self):
print('%s is talking' % self.name)

2、手动模拟class创建类的过程

  将创建类的步骤拆分开,手动去创建。

#准备工作:

#创建类主要分为三部分

  1 类名

  2 类的父类

  3 类体

#类名
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)
"""

步骤一(先处理类体->名称空间):

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

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

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

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
'''

type接收三个参数:

  • 第 1 个参数是字符串 ‘Foo’,表示类名

  • 第 2 个参数是元组 (object, ),表示所有的父类

  • 第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法

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

五、自定义元类控制类的行为

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

egon5步学会元类:

  知识储备: 产生的新对象 = 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__方法,才能控制类实例化行为

  __call__()的作用是使实例能够像函数一样被调用,同时不影响实例本身的生命周期(__call__()不影响一个实例的构造和析构)。

  __call__()可以用来改变实例的内部成员的值。

class Foo:
pass obj=Foo()
obj()
# 没有__call__方法前,obj() 报错:TypeError: 'Foo' object is not callable(不可调用) class Foo:
def __call__(self, *args, **kwargs):
print(self)
print(args)
print(kwargs)
pass obj=Foo()
obj(1, 2, 3, a=1, b=2, c=3)
"""
<__main__.Foo object at 0x101eb6390>
(1, 2, 3)
{'a': 1, 'b': 2, 'c': 3}
"""

__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类实例化行为

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

步骤三:自定义元类,控制类的调用(即实例化)的过程

class Mymeta(type):   # 自定义元类,大多数属性依然是继承的type
def __init__(self, class_name, class_bases, class_dic):
# print(class_name) # 类名:Chinese
# print(class_bases) # 基类:(<class 'object'>,)
# print(class_dic) # 类的名称空间:{'__module__': '__main__', '__qualname__': 'Chinese', 'country': 'China', '__init__': <function Chinese.__init__ at 0x101f201e0>, 'talk': <function Chinese.talk at 0x101f20378>} # 自订制控制类的行为
if not class_name.istitle(): # istitle()判断首字母大写
raise TypeError('类名的首字母必须大写')# 主动报错的关键字是raise if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError("必须有注释,且注释不能为空") super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类功能 def __call__(self, *args, **kwargs): # obj = Chinese('egon',age=18)
print(self) # self = Chinese
print(args) # args = ('egon',)
print(kwargs) # kwargs = {'age': 18} # 第一件事:实例化先造一个空对象obj
obj = object.__new__(self)
# 第二件事:初始化obj
self.__init__(obj, *args, **kwargs)
# 第三件事:返回obj
return obj class Chinese(object, metaclass=Mymeta): # 元类为Mymeta的类的创建可受人控制
"""
中国人的类
"""
country = 'China' def __init__(self, name, age):
self.name = name
self.age = age def talk(self):
print('%s is talking' % self.name) # obj = Chinese('egon', 18) # Chinese.__call__(Chinese, 'egon', 18) obj = Chinese('egon', age=18) # 实例化的行为
"""
<class '__main__.Chinese'>
('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}

__new__函数

步骤五:基于元类实现单例模式,比如数据库对象,实例化时参数都一样,就没必要重复产生对象,浪费内存

方式一:

class MySQL:
__instance = None # 实例化后改为了__instance = obj1
def __init__(self):
self.host = '127.0.0.1'
self.port = 3306 @classmethod
def singleton(cls):
if not cls.__instance:
obj = cls()
cls.__instance = obj
return cls.__instance def conn(self):
pass def execute(self):
pass # obj1 = MySQL()
# obj2 = MySQL()
#
# print(obj1)
# print(obj2) obj1 = MySQL.singleton()
obj2 = MySQL.singleton()
obj3 = MySQL.singleton() print(obj1 is obj3)
"""
True
"""

定义一个类方法实现单例模式

方式二:

class Mymeta(type):
def __init__(self, class_name, class_bases, class_dic):
if not class_name.istitle(): # istitle()判断首字母大写
raise TypeError('类名的首字母必须大写') # 主动报错的关键字是raise if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
raise TypeError("必须有注释,且注释不能为空") super(Mymeta, self).__init__(class_name, class_bases, class_dic) # 重用父类功能
self.__instance = None def __call__(self, *args, **kwargs): # obj = Chinese('egon',age=18)
if not self.__instance:
obj=object.__new__(self) # self:Mysql
self.__init__(obj)
self.__instance=obj
return self.__instance class Mysql(object, metaclass=Mymeta):
"""
mysql
"""
def __init__(self):
self.host = '127.0.0.1'
self.port = 3306 def conn(self):
pass def execute(self):
pass obj1 = Mysql()
obj2 = Mysql()
obj3 = Mysql() print(obj1 is obj2 is obj3) # "True"

定制元类实现单例模式

方式三:

#settings.py文件内容如下
HOST='1.1.1.1'
PORT=3306 import settings def singleton(cls): #cls=Mysql
_instance=cls(settings.HOST,settings.PORT) def wrapper(*args,**kwargs):
if args or kwargs:
obj=cls(*args,**kwargs)
return obj
return _instance return wrapper @singleton # Mysql=singleton(Mysql)
class Mysql:
def __init__(self,host,port):
self.host=host
self.port=port obj1=Mysql()
obj2=Mysql()
obj3=Mysql()
print(obj1 is obj2 is obj3) #True obj4=Mysql('1.1.1.3',3307)
obj5=Mysql('1.1.1.4',3308)
print(obj3 is obj4) #False

定义一个装饰器实现单例模式

元类(metaclass)的更多相关文章

  1. 深刻理解Python中的元类metaclass(转)

    本文由 伯乐在线 - bigship 翻译 英文出处:stackoverflow 译文:http://blog.jobbole.com/21351/ 译注:这是一篇在Stack overflow上很热 ...

  2. 深刻理解Python中的元类(metaclass)

    译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...

  3. Python中的元类(metaclass)

    推荐+收藏:深刻理解Python中的元类(metaclass) 做一些笔记学习学习: 在大多数编程语言中,类就是用来描述如何生成一个对象的代码段,在Python中类也是一个对象,这个(类)对象自身拥有 ...

  4. [转] 深刻理解Python中的元类(metaclass)

    非常详细的一篇深入讲解Python中metaclass的文章,感谢伯乐在线-bigship翻译及作者,转载收藏. 本文由 伯乐在线 - bigship 翻译.未经许可,禁止转载!英文出处:stacko ...

  5. [转]深刻理解Python中的元类(metaclass)以及元类实现单例模式

    使用元类 深刻理解Python中的元类(metaclass)以及元类实现单例模式 在看一些框架源代码的过程中碰到很多元类的实例,看起来很吃力很晦涩:在看python cookbook中关于元类创建单例 ...

  6. 深刻理解Python中的元类(metaclass)【转】

    译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉得 ...

  7. 深入理解Python中的元类(metaclass)

    原文 译注:这是一篇在Stack overflow上很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍 ...

  8. python——深刻理解Python中的元类(metaclass)

    译注:这是一篇在Stack overflow上 很热的帖子.提问者自称已经掌握了有关Python OOP编程中的各种概念,但始终觉得元类(metaclass)难以理解.他知道这肯定和自省有关,但仍然觉 ...

  9. python中的元类metaclass

    本文是一个转载的,因为原文写的太好了,所以直接copy过来吧. 原文请看:http://blog.jobbole.com/21351/ 译注:这是一篇在Stack overflow上 很热的帖子.提问 ...

  10. 谈谈Python中元类Metaclass(二):ORM实践

    什么是ORM? ORM的英文全称是“Object Relational Mapping”,即对象-关系映射,从字面上直接理解,就是把“关系”给“对象”化. 对应到数据库,我们知道关系数据库(例如Mys ...

随机推荐

  1. ORA-03113 : end-of-file on communication channel

    现象一: 数据库startup时,出现数据库无法正常mount,并报ORA-03113错误. SQL> startup ORACLE instance started. Total System ...

  2. 如何删除/mnt/cdrom?|如何删除只读文件系统(Read-only files ystem)? failed !bh ? 挂载光盘?挂载usb?

    root权限下 : 首先用umount /mnt/文件夹 卸载文件系统,必要时可以用umount -f(可能丢失数据)然后rm -rf /mnt/cdrom mkdir /mnt/cdrom moun ...

  3. 优化浏览器默认scroll样式小技巧

    一个最简单的页面: <!DOCTYPE html> <html> <head> <title>优化scroll</title> <me ...

  4. Jenkins持续集成企业实战系列之Jenkins插件下载及邮件配置-----05

    注:原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处 .作者信息和本声明.否则将追究法律责任.    最初接触Jenkins也是由于公司需求,根据公司需求Java代码项目升级的.(公司是 ...

  5. Puppet全面详解

    1.  概述 puppet是一个开源的软件自动化配置和部署工具,它使用简单且功能强大,正得到了越来越多地关注,现在很多大型IT公司均在使用puppet对集群中的软件进行管理和部署,如google利用p ...

  6. P3596 [POI2015]MOD

    $ \color{#0066ff}{ 题目描述 }$ 给定一棵无根树,边权都是1,请去掉一条边并加上一条新边,定义直径为最远的两个点的距离,请输出所有可能的新树的直径的最小值和最大值 \(\color ...

  7. 了解一个名词——GTD

    概念:就是Getting Things Done的缩写,翻译过来就是“把事情做完”,是一个管理时间的方法. 核心理念概括:就是必须记录下来要做的事,然后整理安排并使自己一一去执行. 五个核心原则是:收 ...

  8. js 对象 浅拷贝 和 深拷贝

    网上发现一个比较好的博客 阮一峰的感觉很不错推荐大家看看. http://www.ruanyifeng.com/blog/it/javascript/ 接下来看一下这两个拷贝方法 1.浅拷贝 拷贝就是 ...

  9. CodeCraft-19 and Codeforces Round #537 (Div. 2) C. Creative Snap 分治

    Thanos wants to destroy the avengers base, but he needs to destroy the avengers along with their bas ...

  10. 蠕虫Worm virus

    美国CORNELL大学研究生莫里斯编写的蠕虫病毒 蠕虫病毒是一种常见的计算机病毒.它是利用网络进行复制和传播,传染途径是通过网络和电子邮件.最初的蠕虫病毒定义是因为在DOS环境下,病毒发作时会在屏幕上 ...