本文共 4096 字,大约阅读时间需要 13 分钟。
元类(Metaclass)是Python面向对象编程的核心机制之一。它决定了类如何创建和行为,99%的开发者无法深入理解,甚至有些自以为了解的人也只是停留在表面。今天,我们将带你深入探索Python元类的核心原理。
在Python中,所有事物都是对象。用class关键字定义的类本身也是一个对象,而定义这个类的元类就是我们常说的meta class。简单来说,元类是类的类,它决定了类的创建方式。
class Foo: # Foo的元类是type pass
元类的主要作用是控制类的创建过程。通过自定义元类,我们可以掌控类的产生规则,甚至可以修改对象的行为。这种能力在开发框架和插件系统中非常有用。
exec的应用exec函数可以执行字符串代码,适用于动态代码执行。以下是一个示例:
cmd = """x=1print('exec函数运行了')def func(self): pass"""class_dic = {}exec(cmd, {}, class_dic) 执行后,class_dic会包含x和func两个键:
print(class_dic)#输出:{'x': 1, 'func': } 类的创建过程实际上是一个实例化的过程,而实例化是通过元类完成的。默认情况下,Python使用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) print(type(People)) # 输出:
type元类负责创建类,接受三个参数:类名、基类和名称空间。
class_name = 'People'class_bases = (object,)class_dic = {}class_body = """country='China'def __init__(self, name, age): self.name = name self.age = agedef eat(self): print('%s is eating' % self.name)"""exec(class_body, {}, class_dic) print(class_name) # 输出: Peopleprint(class_bases) # 输出: (,)print(class_dic) # 输出: {'country': 'China', '__init__': , 'eat': }
要使用自定义元类,我们需要继承type。
class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): print('self:', self) print('class_name:', class_name) print('class_bases:', class_bases) print('class_dic:', class_dic) super(Mymeta, self).__init__(class_name, class_bases, class_dic) class People(object, metaclass=Mymeta): country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) __call__方法使类成为可调用对象。例如:
class Foo: def __call__(self, *args, **kwargs): print(args) print(kwargs) print('__call__实现了,实例化对象可以加括号调用了')obj = Foo()obj('nick', age=18) 输出结果:
('nick',){'age': 18}__call__实现了,实例化对象可以加括号调用了 __new__方法是类实例化的关键:
class A: passclass B(A): def __new__(cls): print("__new__方法被执行") return cls.__new__(cls) def __init__(self): print("__init__方法被执行")b = B() 输出结果:
__new__方法被执行__init__方法被执行
通过自定义元类,我们可以控制实例化过程:
class Mymeta(type): def __call__(self, *args, **kwargs): print(self) print(args) print(kwargs) obj = self.__new__(self) self.__init__(obj, *args, **kwargs) return obj
class People(object, metaclass=Mymeta): country = 'China' def __init__(self, name, age): self.name = name self.age = age def eat(self): print('%s is eating' % self.name) obj = People('nick', 18)print(obj.__dict__)# 输出:{'name': 'nick', 'age': 18} 在自定义元类环境下,属性查找顺序会受到影响。例如:
class Mymeta(type): passclass Bar(object): n = 333class Foo(Bar): n = 222class OldboyTeacher(Foo, metaclass=Mymeta): n = 111 school = 'oldboy' def __init__(self, name, age): self.name = name self.age = age def say(self): print('%s says welcome to the oldboy to learn Python' % self.name) print(OldboyTeacher.n) # 输出: 111
查找顺序为:
OldboyTeacher -> Foo -> Bar -> object -> Mymeta -> type
需求:通过自定义元类,修改类的属性为隐藏属性。
class Mymeta(type): def __init__(self, class_name, class_bases, class_dic): super(Mymeta, self).__init__(class_name, class_bases, class_dic) def __call__(self, *args, **kwargs): obj = self.__new__(self) self.__init__(obj, *args, **kwargs) obj.__dict__ = { '_%s__%s' % (self.__name__, k): v for k, v in obj.__dict__.items() } return obj class Foo(object, metaclass=Mymeta): def __init__(self, name, age, sex): self.name = name self.age = age self.sex = sex
obj = Foo('nick', 18, 'male')print(obj.__dict__)# 输出:{'_Foo__name': 'nick', '_Foo__age': 18, '_Foo__sex': 'male'} 这就是通过自定义元类隐藏类属性的实现方法。
转载地址:http://pfgyz.baihongyu.com/