元类

元类的介绍

产生类的类称之为元类,默认所以用class定义的类,他们的元类是type

exec的使用:

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

例子:

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

m = 400
''',g,l
)
print(g,l)
'''
输出结果:
{'x': 200, 'y': 2, '....}
{'m': 400}
'''

python中一切皆对象,类也是对象

满足四个条件的就是对象:

1、都可以被引用,x=obj
2、都可以当作函数的参数传入
3、都可以当作函数的返回值
4、都可以当作容器类的元素,l=[func,time,obj,1]

查看类的方法:

class Foo:
    pass

f1 = Foo()
print(type(f1))
print(type(Foo))
'''
输出结果:
<class '__main__.Foo'> # 可以看出对象由Foo类创建
<class 'type'># type是python的一个内建元类,用来直接控制生成类,python中任何class定义的类其实都是type类实例化的对象
'''

元类的创建方法:

一、使用关键字class来进行创建

class Zhugeliang:
    camp = 'red'
    def __init__(self,nickname,life_value,aggresivity):
        self.nickname = nickname
        self.life_value = life_value
        self.aggresivity = aggresivity

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

#手动创建类
'''
#定义类的三要素:
类名
类的基类
类的名称空间
'''
#类名
class_name = 'China'
#类的基类
class_base = (object,)
#类的名称空间
class_boy = '''
country='China'

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

def talk(self):
    print('%s is talking' %self.name)
'''
'''步骤一(先处理类体->名称空间):类体定义的名字都会存放于类的名称空间中(一个局部的名称空间),我们可以事先定义一个空字典,然后用exec去执行类体的代码(exec产生名称空间的过程与真正的class过程类似,只是后者会将__开头的属性变形),生成类的局部名称空间,即填充字典
'''
class_dic = {}
exec(class_boy,globals(),class_dic)
#print(class_dic)
#步骤二:调用元类type(也可以自定义)来产生类Chinense
Chinese = type(class_name,class_base,class_dic)
#print(Chinese)
Chinese1 = Chinese('egon',18)
#实例化type得到对象Chinese,即我们用class定义的类Chinese
print(Chinese1.__dict__)
'''
输出结果:
{'name': 'egon', 'age': 18}
'''

type 接收三个参数:

  • 第 1 个参数是字符串 ‘Foo’,表示类名
  • 第 2 个参数是元组 (object, ),表示所有的父类
  • 第 3 个参数是字典,这里是一个空字典,表示没有定义属性和方法
    补充:若Foo类有继承,即class Foo(Bar):.... 则等同于type('Foo',(Bar,),{})

自定义元类控制类的行为

import re
#自定义元类来控制首字母必须大写与必须加上注释
class Mymeta(type):
    '''
    定义自己的元类
    '''
    def __init__(self,class_name,class_base,class_dic):
        num = re.search("^[A-Z]\w+",class_name)
        if not num:
            raise TypeError('首字母必须大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise  TypeError('必须加上注释')
         super(Mymeta,self).__init__(class_name,class_base,class_dic)
class Chinese(object,metaclass=Mymeta):
    '''
    自己的元类
    '''
    country='China'
    def __init__(self,namem,age):
        self.name=namem
        self.age=age

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

f = Chinese('egno',18)

自定义元类来控制类的实例化

#单例模式
#方法一:
# class  Msql:
#     __instance = None
#     def __init__(self):
#         self.host = '127.0.0.1'
#         self.port = 3038
#
#     @classmethod
#     def singleton(cls):
#         if not cls.__instance:
#             obj = cls()
#             cls.__instance = obj
#         return cls.__instance
#
#     def connt(self):
#         pass
#
# m1 = Msql.singleton()
# m2 = Msql.singleton()
# m3 = Msql.singleton()
# print(m1 is m2 is m3)

#方法二:
import re
class Mymeta(type):
    '''
    定义自己的元类
    '''
    def __init__(self,class_name,class_base,class_dic):
        num = re.search("^[A-Z]\w+",class_name)
        if not num:
            raise TypeError('首字母必须大写')
        if '__doc__' not in class_dic or not class_dic['__doc__'].strip():
            raise  TypeError('必须加上注释')

        super(Mymeta,self).__init__(class_name,class_base,class_dic)
        self.__intstance = None

    def __call__(self, *args, **kwargs): #实例化的时候就会触发这个方法
        if not self.__intstance:
            # obj = object.__new__(self) # 产生对象
            # self.__init__(obj) # 初始化对象
            # self.__intstance = obj
            self.__intstance = super.__call__(*args,**kwargs)#合并成一步
        return self.__intstance


class  Msql(object,metaclass=Mymeta):
    '''
    控制实例化
    '''
    def __init__(self):
        self.host = '127.0.0.1'
        self.port = 3038
    def connt(self):
        pass

m1 = Msql()
m2 = Msql()
print(id(m1))
print(id(m2))
print(m1 is m2)

六 练习题

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

class Mymeta(type):
    '''
    定义自己的元类
    '''
    def __new__(cls,name,base,attrs):
        update_dic = {}
        for i,v in attrs.items():
            if not callable(v) and not i.startswith('__'):
                update_dic[i.upper()] = v
            else:
                update_dic[i] = v
        return type.__new__(cls,name,base,update_dic)

class China(object,metaclass=Mymeta):
    country = 'China'
    tag = 'Legend of the Dragon'
    def walk(self):
        print('%s is walk'%self.name)

print(China.__dict__)

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

1.元类帮其完成创建对象,以及初始化操作;
  2.要求实例化时传参必须为关键字形式,否则抛出异常TypeError: must use keyword argument
  3.key作为用户自定义类产生对象的属性,且所有属性变成大写

class Mymeta(type):
    '''
    定义自己的元类
    '''
    def __new__(cls,name,base,attrs):
        update_dic = {}
        for i,v in attrs.items():
            if not callable(v) and not i.startswith('__'):
                update_dic[i.upper()] = v
            else:
                update_dic[i] = v
        return type.__new__(cls,name,base,update_dic)
    def  __call__(self, *args, **kwargs):
        if args: #如果传入的值是元组就会报错
            raise  TypeError('must use keyword argument')
        obj = self.__new__(self) #产生对象
        for i,v in kwargs.items(): #循环传入的kwargs值
            obj.__dict__[i.upper()] = v
        return obj

class China(object,metaclass=Mymeta):
    country = 'China'
    tag = 'Legend of the Dragon'
    def walk(self):
        print('%s is walk'%self.name)

f = China(country='中国',name='egon',age=18)
print(f.__dict__)

<wiz_tmp_tag id="wiz-table-range-border" contenteditable="false" style="display: none;">

posted @ 2018-06-06 12:51  游走在边缘的人  阅读(85)  评论(0编辑  收藏  举报