面向对象高级B(元类)

元类

python一切皆对象,类实际上也是一个一个对象

类是一个对象,那他一定是由一个类实例化得到,这个类就叫元类

如何找元类

class Person:
    def __init__(self, name):
        self.name = name
    def score(self):
		print('分数是100')

print(type(Person))

#<class 'type'>

#所有类的元类都是type 

常规的class 类名 会把类构造出来,实际上是元类实例化产生类这个对象,

Person类是对象,一定是由一个类实例化产生的,所以type()产生对象,调用__init__方法,看源码里面需要穿三个参数

**class底层实现原理是: type(object_or_name, bases, dict), **

  1. object_or_name: 类的名字,是个字符串

  2. bases: 是它的所有父类,基类

  3. dict: 名称空间是一个字典

#通过type来直接产生类,不用class关键字
l = {}
exec{'''
school='oldboy'
def __init__(self, name):
	self.name = name
def score(self):
	print('分数是100')
''', {}, l}
def __init__(self, name):
    self.name=name

Person = type('Person', (object,), l) # 等同于生成一个Person类,其中具有school属性,对象有name属性,有score方法


### 下面几句可以生成一个Person类,与上面的语句实现相同的功能
#def __init__(self, name):
    #self.name = name
#Person = type('Person', (object,), {'school':'
# oldboy','__init__':__init__})



自定义元类

来控制类的产生,可以控制类名,可以控制类的继承关系 ,控制类的名称空间

自定义元类必须继承type,写一个类继承type,这种类都叫元类

class Mymeta(type):
    def __init__(self, name, bases,dic):
        #练习一:加限制 控制类名必须以sb 开头
        if not name.startswith('sb'):
            raise Exception(’类名没有以sb开头‘)# 如果不是以sb开头,就会抛异常
        #练习二:类必须加注释
        print(self.__dict__['__doc__']) # 有注释的话会把注释打印出来
#metaclass=Mymeta 指定这个类生成的时候,用自己写的Mymeta这个元类

class Person(object,metaclass=Mymeta):
    '''
    注释
    '''
    school = 'oldboy'
    def __init__(self, name):
        self.name = name
    def score(self):
        print('分数是100')
        
p = Person('nick')

  1. 自定义一个元类,控制类产生,类的名称空间必须有name字段才能创建成功,否则失败
class Mymeta(type):
    def __init__(self, x, y, z):
        for k, v in self.__dict__.items():
            print(k,v)
        # print(self.__dict__)
        if 'name' not in self.__dict__.values() and 'name'not in self.__dict__.keys():
            raise Exception('你的类没有“name”这个字段')
        else:
            print('名称空间已经存在"name"')
  1. 自定义一个元类,定义一个类,继承字典,使其具备 点取值和赋值功能,通过元类控制对象的产生,把所有对象的属性都放在attr字典中,属性全部删除

例如di=Mydict(name='lqz', age=18) di的名称空间中没有name和age属性,但是有attr字典属性,字典包含{'name':'lqz', 'age':18} (待修改)

class Mymeta(type):
    def __call__(self, name, age):#要有返回值
    	obj = object.__new__(self)
        obj.__dict__['attr'] = {'name':name,'age':age}
		return obj
class Person(metaclass=Mymeta):
    def __init__(self,name,age):
        self.name = name
        self.age = age
p = Person('lje', 18)
print(p.__dict__)
print(p.attr['name'])


'''
{'attr': {'name': 'lje', 'age': 18}}
lje
'''


##延伸(对类名称空间中的属性单独放到attr字典里,但是该字典依然在类名称空间的字典里)
class Mymeta(type):
    def __init__(self,name,bases,dic):
        pass
    def __new__(cls, name,bases,dic):#要有返回值
        dic2={'attr':{}}#单独列一个字典,用于盛放类的属性
        for k,v in dic.items():
            if not k.startswith('__'):#如果key值以__开头,就把大拿出来放到dic2中
                dic2['attr'][k]=v
        print('-------',dic2)
        return type.__new__(cls,name,bases,dic2)#把dic2当成名称空间返回,但是类的名称空间还是原来的dic,只不过属性放到attr小字典里了
class Person(metaclass=Mymeta);#Person=Mymeta(name,bases,dic) 调用type的__call__,内部调用了Mymeta.__new__,又调用了Mymeta的__init__
	school = 'Tshinghua'
    age = 10
    def __init__(self, name, age):
        self.name = name
        self.age = age
print(Person.__dict__)
print(Person.attr['school'])

'''
------- {'attr': {'school': 'oldboy', 'age': 10}}
{'attr': {'school': 'oldboy', 'age': 10}, '__module__': '__main__', '__dict__': <attribute '__dict__' of 'Person' objects>, '__weakref__': <attribute '__weakref__' of 'Person' objects>, '__doc__': None}
Tshinghua
'''

posted @ 2019-09-03 23:08  Micheal_L  阅读(147)  评论(0编辑  收藏  举报