python作为一门万物皆对象的语言,每一个方法都由类产生,可类是如何产生的呢!这就必须提到提到元类,而理解元类,只需知道两句话:
- 道生一,一生二,二生三,三生万物
- 我是谁,我从哪里来,我要干什么
1、道,type
(python的一切对象,都由type产生)
2、一,metaclass
(元类,也叫类生成器)
3、二,class
(也就是常用的类)
4、三,instance
(类的实例)
5、四,method
和attribute
(实例方法和属性)
先看一个简单的,也是最常用的类。
# 创建一个Info类,二的源头
class Info():
def __init__(self, name):
self.name = name
def output_name(self):
print("My name is %s"%self.name)
# 从Info类实例化一个对象,二生三
a=Info("tom")
# 调用实例方法,三生万物
a.output_name()
输出结果
>>>My name is tom
其实,class Info只是一个函数的另一种写法。
def fn1(self):
print("My name is %s" % self.name)
def fn2(self, name):
self.name = name
Info = type('Info', (object,), {
"output_name": fn1,
"__init__": fn2})
a = Info("tom")
a.output_name()
输出结果
>>>My name is tom
以上,便是道生二的过程,由type直接生成一个类,而他的三个参数(name,bases,dict),则刚好映正:我是谁,我从哪里来,我要干什么。
name
:我是谁,区分与其他一切的命名,这里为Info
bases
:我从哪里来,当前类所继承的父类,object为python中最初级的一个类
dict
:我要干什么,将类的方法和属性包装成一个字典传入,具体事件的实现方法
上面只是type直接生成了class,也就是"道直接生成二"的过程,现在讲讲道生一,一生二的过程。
假如要写一个类,在不定义一个output方法的同时,又想该类的实例成员自带output的天赋(即自带output方法)。
class InfoMetaClass(type):
def __new__(cls, name, bases, attrs):
attrs["output_name"]=lambda self,value:print("My name is %s"%value)
return type.__new__(cls, name, bases, attrs)
# 创建一个Info类,二的源头
class Info(object, metaclass=InfoMetaClass):
pass
# 从Info类实例化一个对象,二生三
a=Info()
# 调用实例方法,三生万物
a.output_name("tom")
输出结果
>>>
My name is tom
可以看到,即使Info类中未定义output_name方法,但是他的实例化对象是有该方法的,因为他的元类赋予了它该方法。
其中元类的__new__方法中
cls
:未元类本身name
:为使用该元类的类的类名bases
:继承的父类attrs
: 包含类的方法和属性的字典
下面为一具体的例子,统计一个类中所有以”a“开头的方法和属性。
# 由type创建一个元类,道生一
class InfoMetaClass(type):
def __new__(cls, name, bases, attrs):
# 定义一个my_dict的字典存放所有以”a“开头的方法和属性
attrs["my_dict"]={
}
for k,v in attrs.items():
if k.startswith("a"):
attrs["my_dict"][k]=v
return type.__new__(cls, name, bases, attrs)
# 由元类创建一个类,一生二
class Info(object, metaclass=InfoMetaClass):
a_name="tom"
b_name="jenny"
def a_output_name(self):
return a.a_name
def a_output_age(self):
return 25
def s_hello(self):
return "hello"
# 从Info类实例化一个对象,二生三
a=Info()
# 调用实例方法,三生万物
print(a.my_dict)
输出结果
>>>
{
'a_name': 'tom', 'a_output_name': <function Info.a_output_name at 0x0000014FD2DDE950>, 'a_output_age': <function Info.a_output_age at 0x0000014FD2DDE9D8>}