元类
exec方法
- exec()方法可以执行字符串形式的python代码块
- 使用方法
exec(object, global=None, local=None)
- object: 字符串类型的python代码块
- global: 代表全局名称空间, 必须是字典, 默认为None, 如传参则表明该代码块在全局名称空间中运行
- local: 代表局部名称空间, 可以是任何映射, 默认为None, 如传参则表明该代码块在局部空间中运行
code = '''
x = 0
sum = x + y + z
print(sum)
'''
y = 1
z = 2
global_dict = {'y': 2, 'z': 3}
local_dict = {'y': 3, 'z': 4}
exec(code)
'''
y = 1
z = 2
x = 0
sum = x + y + z
print(sum)
'''
exec(code, global_dict)
'''
相当于
y = 2
z = 3
x = 0
sum = x + y + z
print(sum)
'''
exec(code, global_dict, local_dict)
'''
相当于
y = 2
z = 3
def exec_func():
y = 3
z = 4
x = 0
sum = x + y + z
print(sum)
exec_func()
'''
'''
3
5
7
'''
type元类
- 类本身也是对象, 是通过实例化元类得到的对象, 元类是类的类
- python默认的元类是type, 也可以自定义元类来实现控制类的创建
- 通过type元类来创建类
MyClass = type(class_name, class_bases, class_dict)
- class_name 类名
- class_bases 父类, 以元祖形式传入
- class_dict 类的属性, 以字典的形式传入 (类的名称空间)
# 用type元类来创建一个Chinese类
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
class_name = 'Chinese'
class_bases = (object,)
class_dict = {'country': 'China', '__init__': __init__}
Chinese = type(class_name, class_bases, class_dict)
c1 = Chinese('bigb', 'male', 18)
print(c1.name) # bigb
print(c1.country) # China
自定义元类
自定义元类控制类的创建
- 我们可以通过自定义元类来实现控制类的创建过程
- 自定义的元类必须继承type, 并且覆盖type的
__init__
的方法
'''
通过自定义元类来实现:
1. 类名首字母必须大写
2. 类中必须有文档注释
'''
class MyMeta(type):
def __init__(self, class_name, class_bases, class_dict):
print(class_name) # chinese
print(class_bases) # (<class 'object'>,)
print(class_dict) # {'__module__': '__main__', '__qualname__': 'chinese', 'country': 'China', '__init__': <function chinese.__init__ at 0x0000000009FBFD90>, 'kongfu': <function chinese.kongfu at 0x0000000009FBFE18>}
# 类名首字母必须大写
if not class_name.istitle():
raise TypeError('类的首字母必须大写!')
# 类中必须有注释
if not class_dict.get('__doc__'):
raise TypeError('类中必须有文档注释!')
# 调用type中的__init__方法初始化对象
super().__init__(class_name, class_bases, class_dict)
class chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})
country = 'China'
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def kongfu(self):
print('降龙十八掌!')
'''
raise TypeError('类的首字母必须大写!')
TypeError: 类的首字母必须大写!
'''
# 将类名大写, 再运行
class Chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})
country = 'China'
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def kongfu(self):
print('降龙十八掌!')
'''
raise TypeError('类中必须有文档注释!')
TypeError: 类中必须有文档注释!
'''
自定义元类控制类的调用
- 调用一个对象时, 会触发对象的类当中的
__call__
方法
- 类本身也是个对象, 因此在调用类实例化对象的时候, 就会触发元类当中的
__call__
方法
class MyMeta(type):
def __call__(self, *args, **kwargs):
# 产生一个空对象
obj = self.__new__(self) # self是类对象
# 初始化空对象
self.__init__(obj, *args, **kwargs)
# 返回初始化好的对象
return obj
class Chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})
country = 'China'
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def kongfu(self):
print('降龙十八掌!')
# 这里调用了类对象Chinese, 因此会触发Chinese的类(元类)中的__call__方法
c1 = Chinese('bigb', 'male', 18)
print(c1.name)
'''
1. __call__中的__new__生成了一个空对象
2. __call__中的__init__初始化这个空对象
3. __call__返回了这个对象,并赋值给了c1
'''
- 现在 我们可以在此基础上通过修改
__call__
的逻辑从而控制类的调用过程
# 通过元类让Chinese类实例化出来的对象的属性变为私有属性
class MyMeta(type):
def __call__(self, *args, **kwargs):
# 产生一个空对象
obj = self.__new__(self) # self是类对象
# 初始化空对象
self.__init__(obj, *args, **kwargs)
# 将对象的属性变成私有属性(对象._类__属性名)
obj.__dict__ = {f'_{self.__name__}__{k}': v for k, v in obj.__dict__.items()}
# 返回初始化好的对象
return obj
class Chinese(object, metaclass=MyMeta): # foo = MyMeta('foo', (object, ) {...})
country = 'China'
def __init__(self, name, gender, age):
self.name = name
self.gender = gender
self.age = age
def kongfu(self):
print('降龙十八掌!')
# 这里调用了类对象Chinese, 因此会触发Chinese的类(元类)中的__call__方法
c1 = Chinese('bigb', 'male', 18)
print(c1._Chinese__name) # bigb