目录
2019/09/03 学习整理
python面向对象(进阶)
一、元类
1.什么是元类?
- 在python中一切皆对象,那么我们用class关键字定义的类本身也是一个对象,负责产生该对象的类称之为元类,即元类可以简称为类的类
Person类也是个对象,那他一定是由一个类实例化得到,这个类,就叫元类
type是内置的一个元类,所有的类都是由type实例化得到
2.为什么用元类?
- 元类是负责产生类的,所以我们学习元类或者自定义元类的目的:是为了控制类的产生过程,还可以控制对象的产生过程
就元类本身而言,它们其实是很简单的:
1) 拦截类的创建
2) 修改类
3) 返回修改之后的类
元类主要适用于以下场合:
值的域限制
隐式转换自定义类的值(您可能希望向用户隐藏编写类的所有这些复杂方面)
执行不同的命名约定和样式准则(比如,“每种方法都应有一个文档字符串”)
向类添加新的属性
3.如何找到元类
print(type(p))
#同理:type类是产生所有类的元类
二、class底层原理分析
class 类名 会把类构造出来
实际上是:元类实例化产生类 这个对象
类实例化产生对象,一定是: 类名()
Person 类是由type实例化产生,传一堆参数
type() 调用类的__init__方法
type()语法
type(object_or_name, bases, dict)
object_or_name:类的名字,是个字符串
bases:是它的所有父类,基类
dict:名称空间,是一个字典
通过type来直接产生类,不用class关键字了
补充:内置函数exec
cmd = """
x=1
print('exec函数运行了')
def func(self):
pass
"""
class_dic = {}
# 执行cmd中的代码,然后把产生的名字丢入class_dic字典中
exec(cmd, {}, class_dic)
# exec函数运行了
print(class_dic)
# {'x': 1, 'func': <function func at 0x10a0bc048>}
class 底层就是调用type来实例化产生类(对象)
class People: # People=type(...)
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))
# <class 'type'>
三、通过元类来控制类的产生
自定义元类 ;来控制类的产生:可以控制类名,可以控制类的继承父类,控制类的名称空间
class Mymeta(type):
def __call__(self, *args, **kwargs):
print(self) # self是People
print(args) # args = ('nick',)
print(kwargs) # kwargs = {'age':18}
# return 123
# 1. 先造出一个People的空对象,申请内存空间
# __new__方法接受的参数虽然也是和__init__一样,但__init__是在类实例创建之后调用,而 __new__方法正是创建这个类实例的方法。
obj = self.__new__(self) # 虽然和下面同样是People,但是People没有,找到的__new__是父类的
# 2. 为该对空对象初始化独有的属性
self.__init__(obj, *args, **kwargs)
# 3. 返回一个初始化好的对象
return obj
- People = Mymeta(),People()则会触发__call__
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)
# 在调用Mymeta的__call__的时候,首先会找自己(如下函数)的,自己的没有才会找父类的
# def __new__(cls, *args, **kwargs):
# # print(cls) # cls是People
# # cls.__new__(cls) # 错误,无限死循环,自己找自己的,会无限递归
# obj = super(People, cls).__new__(cls) # 使用父类的,则是去父类中找__new__
# return obj
- 类的调用,即类实例化就是元类的调用过程,可以通过元类Mymeta的__call__方法控制
- 分析:调用Pepole的目的
- 先造出一个People的空对象
- 为该对空对象初始化独有的属性
- 返回一个初始化好的对象
obj = People('nick', age=18)
<class '__main__.People'>
('nick',)
{'age': 18}
print(obj.__dict__)
{'name': 'nick', 'age': 18}
四、通过元类来控制类的调用过程
控制类的调用过程,实际上在控制:对象的产生
class Mymeta(type):
def __call__(self, *args, **kwargs):
# print('xxx')
return 1
class Person(object,metaclass=Mymeta):
school='oldboy'
def __init__(self,name):
self.name=name
def score(self):
print('分数是100')
p=Person('nick')
print(p.name)
把对象所有的属性都变成私有
class Mymeta(type):
def __call__(self, *args, **kwargs):
obj=object.__new__(self)
obj.__init__(*args, **kwargs)
# print(obj.__dict__)
obj.__dict__={ f'_{self.__name__}__{k}':v for k,v in obj.__dict__.items()}
# print(obj.__dict__)
return obj
class Person(object, metaclass=Mymeta):
school = 'oldboy'
def __init__(self, name):
self.name = name
def score(self):
print('分数是100')
p = Person(name='nick')
print(p.__dict__)
print(p.name)
# print(p)
# print(p.name)
五、有了元类之后的属性查找
类的属性查找顺序:
先从类本身中找
--->mro继承关系去父类中找
---->去自己定义的元类中找-
-->type中
--->报错
对象的属性查找顺序:
先从对象自身找
--->类中找
--->mro继承关系去父类中找
--->报错
class Mymeta(type):
n=444
def __call__(self, *args, **kwargs): #self=<class '__main__.OldboyTeacher'>
obj=self.__new__(self)
# print(self.__new__ is object.__new__) #True
obj.__init__(*args, **kwargs)
return obj
class Bar(object):
# n=333
pass
# def __new__(cls, *args, **kwargs):
# print('Bar.__new__')
class Foo(Bar):
# n=222
pass
# def __new__(cls, *args, **kwargs):
# print('Foo.__new__')
class 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)
# def __new__(cls, *args, **kwargs):
# print('OldboyTeacher.__new__')
o=OldboyTeacher('egon',18) #触发OldboyTeacher的类中的__call__方法的执行,进而执行self.__new__开始查找
print(OldboyTeacher.n)
# print(o.n)