元类介绍

# 元类
# 说python当中一切皆对象,
# 用class关键字定义的类其实本质也是一个对象。
# class Foo:
# pass
# 说Foo是一个对象的话,对象肯定是由一个类实例化而来的。
# 也就是说在class的时候,调class关键字的时候,必然是这个关键字去调了一个类的类,类的类就称之为元类。
# 就是调了一个元类加括号实例化,元类加括号的结果就是用class定义的类
# class Foo: # Foo = 元类()
# pass
# 所以说元类就是负责产生类的那一个类,是类的老祖宗。

# 1.什么是元类?
# 在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象
# 负责产生该对象的类称之为元类,即元类可以简称为类的类


# 2.为何要用元类?
# 什么情况下用元类?
# 元类是负责产生类的,可以控制类的产生过程,还可以控制对象的产生过程。
# 什么叫对象的产生过程?
# class Foo:
# pass
# Foo()
# 对Foo这个类的产生控制,以及对调用Foo的这个过程的控制,因为调用Foo的过程就是产生Foo对象的过程


# 3.如何用元类

# 3.1 需要储备几点知识
# 第一点知识: exec()这个内置函数

# exec()这个内置函数用来干什么的呢?
# 将一个字符串中的代码提出出来运行一下

# cmd = '''
# x=1
# y=2
# print('我爱老婆杨静,哈哈哈')
# '''
# local_dic = {}
# exec(cmd,)
# 模拟的就是执行python代码的过程,会产生一个名字,必然往名称空间里面去丢
# 名称空间的格式是以字典的形式展现的
# 所以造两个名称空间,一个全局名称空间
# 怎么查看全局的名称空间:
# print(globals())
# 造一个空字典
# local_dic = {}
# exec(第一个参数是你的字符串,第二个参数是你要引用的那个全局作用域(不需要定义个空字典就好了),第三个是local_dic)
# exec(cmd,{},local_dic)
# print(local_dic)
# 会发生什么是呢?
# exec会执行字符串中的代码,将执行过程中产生的名字都往local_dic = {}里面丢
# exec干了一件事:给你一段代码,只要执行过程中产生名字都丢到一个名称空间字典里面去。
# 函数也可以!!
# 函数体代码提取出来
# def func():
# x= 1
# y=2
# print('杨静爱老公张仁国,哈哈哈')

# cmd = '''
# x = 1
# y = 2
# print('杨静爱老公张仁国,哈哈哈')
# '''
# func_dic = {}
# exec(cmd,{},func_dic)
#
# print(func_dic)

# 类也可以!!
# 类体代码提取出来

# cmd = '''
# x=1
# def func():
# y = 2
# '''
# class_dic = {}
# exec(cmd,{},class_dic)
# print(class_dic)
# 类体执行过程中产生的名字都丢到名称空间里面去了
# 这是模拟了类创建过程中类定义阶段造名称空间那一步操作


# 用这个功能干嘛使?
# 可以用来模拟造名称空间这个过程。


# 3.2创建类过程当中有几个关键的要素
# 创建类的方法有两种
# 第一种class关键字去创建,用的是默认的元类:type
# class People():
# country = 'china'
# def __init__(self,name,age):
# self.name = name
# self.age = age
#
# def eat(self):
# print('%s is eating'%self.name)

# # 查看People的类是谁:
# print(type(People))
# # <class 'type'>
#
# print(People)
# obj = People('zrg',24)
# print(obj)
# obj.eat()
# print(type(obj))

# 大前提:如果说类也是对象的话,那么用class关键字去创建类的过程也是一个实例化的过程
# 该实例化的目的是为了得到一个类,调用的是元类
# 调用的元类又分为两种:
# 一种是默认的元类type
# 另一种是自定义的

# 默认的元类的造类的过程:
# 类的名字 class_name
# 类的基类 class_bases
# 类的名称空间 class_dic
#
# class_name = 'People' #类名必须是字符串形式
# class_bases = (object,) #不继承任何类默认就是object逗号才叫一个元组
# class_dic = {} #最关键的最难得,类的名称空间一个字典的形式
# 类的名称空间从何而来----->从类体的执行而来,而类体就是一堆字符串
# class_body = '''
# country = 'china'
# def __init__(self,name,age):
# self.name = name
# self.age = age
#
# def eat(self):
# print('%s is eating'%self.name)
# '''
# 要做的事根据类体代码得到字典让他里面有值,就用exec函数
# exec(class_body,{},class_dic)
# 查看造类的三要素
# print(class_name)
# print(class_bases)
# print(class_dic)

# type(类名,基类,类的名称空间)得的结果给一个变量名People1
# People1 = type(class_name,class_bases,class_dic)
# print(People1)
# obj1 = People1('yj',25)
# obj1.eat()


# 第二种 自定义元类
# 自定义的话就是把type换成自己写的类名字

# 定义自己的元类不一定所有的功能都要自己写,大部分都是源自type的所以继承
# class Mymeta(type): #只有继承了type类才能称之为一个元类,否则就是一个普通的自定义的类
# def __init__(self,class_name,class_bases,class_dic):
#控制逻辑
# if class_dic.get('__doc__') is None or len(class_dic.get('__doc__').strip()) == 0:
# raise TypeError('类中必须有文档注释,并且文档注释不能为空')
# if not class_name.istitle():
# raise TypeError('类名首字母必须大写')
# print(self) #现在是 People
# print(class_name)
# print(class_bases)
# print(class_dic)
# 上面的改写的功能是把父类的覆盖掉了,父类的可能还有些其他有用的代码
# 所以super括号括起来,自己的类名,逗号self就是People,然后括号__init__也需要把三个参数传进去
# 这是保险起见把父类的功能也重用一下
# super(Mymeta,self).__init__(class_name,class_bases,class_dic)


# 分析class的运行原理:
# 1.拿到一个字符串格式的类名 class_name
# 2.拿到一个类的基类们 class_bases
# 3.执行类体代码,拿到一个类的名称空间 class_dic
# 4.调用type(class_name,class_bases,class_dic)将得到的结果赋值给个变量名People
# People = type(class_name,class_bases,class_dic)

# class People(object,metaclass=Mymeta): # People = Mymeta(类名,基类们,名称空间)
#加文档注释 不写 为空都是不合法的
# '''这是一个People类'''
# country = 'china'
# def __init__(self,name,age):
# self.name = name
# self.age = age

# def eat(self):
# print('%s is eating'%self.name)

# print(People.__dict__) #看名称空间
# 文档注释是空
# {'__module__': '__main__', 'country': 'china', '__init__': <function People.__init__ at 0x000001E1BAFD9950>, 'eat': <function People.eat at 0x000001E1BAFD99D8>, '__dict__': <attribute '__dict__' of 'People' objects>, '__weakref__': <attribute '__weakref__' of 'People' objects>, '__doc__': None}
# 应用:自定义元类控制类的产生过程,类的产生过程其实就是元类的调用过程





# 3.3储备知识 __call__
# __开头__结尾 会在满足某种条件时自动触发执行
# class Foo:
# def __call__(self, *args, **kwargs):
# print('ok')
# print(self)
# print(args) #args就是传的位置参数
# print(kwargs) #kwargs就是传的关键字参数
#
# obj = Foo()
# # obj() #要想让obj这个对象变成一个可调用的对象,需要早该对象的类中定义一个方法__call__方法
# # 该方法会在调用对象是自动触发
# obj(1,2,3,x=1,y=2)



# 自定义元类控制类的产生过程
# 其实自定义类定义完了之后,还要去调用那个类,还想要去控制自定义类的那个调用过程
# 也就是自定义类创造对象的过程
# 4. 自定义元类来控制类的调用过程,即类的实例化过程

class Mymeta(type):
def __call__(self, *args, **kwargs):
# print(self) #self是People
# print(args)
# print(kwargs)
# 1.先造出一个People空对象
obj = self.__new__(self)
# 2.为该空对象初始化独有的属性
self.__init__(obj,*args,**kwargs)
# 3.返回一个初始化好的obj
return obj



class People(object,metaclass=Mymeta): # People = Mymeta(类名,基类们,名称空间)
# 加文档注释 不写 为空都是不合法的
'''这是一个People类'''
country = 'china'
def __init__(self,name,age):
self.name = name
self.age = age

def eat(self):
print('%s is eating'%self.name)

# 分析:调用People的目的
# 1.先造出一个People空对象
# 2.为该空对象初始化独有的属性

obj = People('zrg',age=24)
print(obj.__dict__)
print(obj.name)
obj.eat()


# 调对象就是调对象内的call方法,调对象拿到的返回值就是call方法的返回值
# 不写返回值就是一个None
posted @ 2018-11-30 09:44  张仁国  阅读(165)  评论(0编辑  收藏  举报
目录代码