面向对象之元类
一、什么是元类
- 产生已知类的类就叫做元类,
type
class Person(object):
...
def func():
...
people = Person()
# 产生对象的是类
print(type(people)) # <class '__main__.Person'>
print(type(func)) # <class 'function'>
# 产生类的就是元类
print(type(object)) # <class 'type'>
print(type(Person)) # <class 'type'>
- 通过查看每一种数据的数据类型,我们会发现都共同拥有一个类
- 这个类就是 type 我们也称之为元类
二、产生类的两种方法
[1]使用关键字声明
- 这是我们最常用的
- 语法
class 类名(继承的父类)
class Person(object):
...
print(Person) # <class '__main__.Person'>
print(type(Person)) # <class 'type'>
[2]通过type
创建
- 语法
类名 = type('类的名字',(类继承的父类,使用元组),{当前类内置的数据属性和函数属性})
Person = type('Person', (object,), {'name': 'Xanadu', 'run': run})
people = Person()
print(Person) # <class '__main__.Person'>
print(Person.__dict__)
# 在创建类时添加的函数数据与使用关键字定义的类内定义的
# {'name': 'Xanadu', 'run': <function run at 0x000002B17C9628C0>, ....'__doc__': None}
print(people) # <__main__.Person object at 0x000001846987B760>
print(people.name) # Xanadu
print(people.__dir__) # ['__class__', .....'__weakref__', 'name']
[3]为什么要使用元类
- 元类可以控制类的创建,也就意味着我们可以高度定制类的具体行为
- 比如,当我们掌控了食品的生产过程,我们就可以在里面随便动手脚
[4]元类的基本使用
-
尝试实现功能:让所有类名都是大写
-
先要知道怎么样产生的类,再通过定义类名是进行修改
class MyType(type):
# 其实我们自建的这个元类只是一个从type里派生方法的中间平台
def __init__(cls, class_name, class_bases, class_dict):
print('这是 MyType 里的 init 方法')
# 在使用我们的元类定义类时就调用了这个函数,先于call方法
# 这里使用type里的__init__来创建一个空的类,方便创建类时进行类名,继承父类,和名称空间的定义
super().__init__(class_name, class_bases, class_dict)
def __call__(cls, *args, **kwargs):
# 在使用类名加括号是会触发这个方法,这个实现实例化的最核心的方法
# 其产生的obj就是我们实例化所得到的对象
# 这个使用type里的__call__方法产生了一个对象并返回了出来
obj = super().__call__(*args, **kwargs)
print('这是 MyType 里的 call 方法')
# 所以我们也要返回出去
return obj
# 通过上面我们自己定义的元类来产生类
# 这个过程与继承相似又不同
class MyClass(object, metaclass=MyType):
def __init__(self):
print('这是MyClass里的init方法')
# 对象加括号调用了这个方法
def __call__(self, *args, **kwargs):
print('这是MyClass里面的call方法')
stu = MyClass()
stu()
# 这是 MyType 里的 init 方法
# 这是MyClass里的init方法
# 这是 MyType 里的 call 方法
# 这是MyClass里面的call方法
- 使用关键字创建类时在类中直接定义的方法与使用type创建类时在名称空间中直接添加的方法(函数)是不同的
class Person1(object):
def run_1(self):
...
@classmethod
def run_2(cls):
...
@staticmethod
def run_3():
...
def run_4():
...
Person2 = type("Person", (object,), {'run': run})
print(Person1.__dict__)
# {'__module__': '__main__',
# 'run_1': <function Person1.run_1 at 0x000002048B8E36D0>,
# 'run_2': <classmethod(<function Person1.run_2 at 0x000002048BB51AB0>)>,
# 'run_3': <staticmethod(<function Person1.run_3 at 0x000002048BB51B40>)>,
# ...'__doc__': None}
print(Person2.__dict__)
# {'run': <function run at 0x000002048B3E3EB0>,
# ...'__doc__': None}
[5]自定义类
- 尝试实现功能:让所有类名都是大写
# 会自动更正,将类名转化成大写
class MyType(type):
def __init__(cls, class_name, class_bases, class_dict):
if not class_name.isupper():
# raise TypeError('请输入全大写类名')
class_name.upper()
else:
...
super().__init__(class_name, class_bases, class_dict)
def __call__(cls, *args, **kwargs):
obj = super().__call__(*args, **kwargs)
return obj
class MyClass(object, metaclass=MyType):
def __init__(self):
...
# 对象加括号调用了这个方法
def __call__(self, *args, **kwargs):
...
stu = MyClass()
# 查看类名已经转换大写
print(type(stu)) # <class '__main__.MyClass'>
# 强制输入群大写类名,不然报错
class MyType(type):
def __init__(cls, class_name, class_bases, class_dict):
if not class_name.isupper():
raise TypeError('请输入全大写类名')
# class_name.upper()
else:
...
super().__init__(class_name, class_bases, class_dict)
def __call__(cls, *args, **kwargs):
obj = super().__call__(*args, **kwargs)
return obj
class MyClass(object, metaclass=MyType):
def __init__(self):
...
# 对象加括号调用了这个方法
def __call__(self, *args, **kwargs):
...
stu = MyClass()
print(type(stu))
# 使用了不符合全大写的类名就报错了
# TypeError: 请输入全大写类名
[6]总结
- 元类属于面向对象中比较高阶的用法,无特殊需求和特殊情况外不建议使用
- 如果你想高度定制类的产生过程
- 那么编写元类里面的
__init__
方法
- 那么编写元类里面的
- 如果你想高度定制对象的产生过程
- 那么编写元类里面的
__call__
方法
- 那么编写元类里面的
补充、__new__
方法
- 元类中直接调用
__new__
class MyType(type):
# 其实我们自建的这个元类只是一个从type里派生方法的中间平台
def __init__(cls, class_name, class_bases, class_dict):
print('这是 MyType 里的 init 方法')
# 在使用我们的元类定义类时就调用了这个函数先于call方法
# 这里使用type里的__init__来创建一个空的类,方便创建类时进行类名,继承父类,和名称空间的定义
super().__init__(class_name, class_bases, class_dict)
def __call__(cls, *args, **kwargs):
# 在使用类名加括号是会触发这个方法
# 这个使用type里的__call__方法产生了一个对象并返回了出来
obj = super().__call__(*args, **kwargs)
print('这是 MyType 里的 call 方法')
# 所以我们也要返回出去
return obj
def __new__(cls, *args, **kwargs):
print('这是 MyType 中的 new 方法')
# obj = super().__new__(cls, *args, **kwargs)
obj = type.__new__(cls, *args, **kwargs)
# ('MyClass', (<class 'object'>,), {'__module__': '__main__', '__qualname__': 'MyClass',
# '__init__': <function MyClass.__init__ at 0x000001F9BFB21D80>,
# '__call__': <function MyClass.__call__ at 0x000001F9BFB21E10>})
return obj
# 通过上面我们自己定义的元类来产生类
# 这个过程与继承相似又不同
class MyClass(object, metaclass=MyType):
def __init__(self):
print('这是 MyClass 里的 init 方法')
# 对象加括号调用了这个方法
def __call__(self, *args, **kwargs):
print('这是 MyClass 中的 call方法')
def __new__(cls, *args, **kwargs):
print('这是 MyClass 中的 new 方法')
obj = super().__new__(cls)
return obj
stu = MyClass()
stu()
'''
这是 MyType 中的 new 方法
这是 MyType 里的 init 方法
这是 MyClass 中的 new 方法
这是 MyClass 里的 init 方法
这是 MyType 里的 call 方法
这是 MyClass 中的 call方法
'''
- 使用
__call__
间接调用
标签:__,面向对象,init,call,print,type,class,之元类
From: https://www.cnblogs.com/taoyuanshi/p/17994498