首先类是一个什么东西
类是数据与操作其数据方法的封装
一个定义python类的例子:
class B:
def rename(self, newname):
self.name = newname
class A(B): # 继承B,能用B的公有属性和方法
name = 'A' # 公有属性
def __init__(self):
self.name = 'a' # 私有属性
def get_name(self): # 方法(公有)
return self.name
a = A()
print(a.get_name()) # a
a.rename('b')
print(a.get_name()) # b
于此,能解决python编程中90%以上的应用
python中类的创建与其实例化如此简单,背后却是因为底层做好了足够的封装,那么究竟在python里面类是种什么样的东西呢?
一步步来...
在python中:
处处都是对象
因此我们上面实例出的a是一个对象,定义的类A、B也是对象,相信看到此处很多初学者都是一脸懵逼的,那么它怎么是一个对象呢?
先来介绍python中的对象, 避免复杂化问题,这里简化出对象实际上是占用内存中的一个东西:
python的解析器是用c写的,那么,在c中描述这个对象的话,用的是一个c结构体,而这个结构体里面,不止上面对象那么简单,而是:
在这个对象(结构体)里面,有一个fn的东西叫做引用计数,这个暂时放一放,先来讨论class这个变量,这个变量说明(指向)了一个xx(暂时未知)的类型,在python设计的''游戏规则''里面,此时就能够告诉我们,这一个对象是一个xx类型的对象,既然在python里面处处都是对象,那么这个对象指向类型,也应该是一个对象,如图:
类型(对象2)也是一个对象,但是对象2的类型是谁呢?总不可能这样这样嵌套下去吧,对的,python的设计开始要把这个玩法''圆''起来了,那么是什么呢?
type
对象2的类型就是type,'自圆其说'的终点,这个type是python底层封装好的一个类型(结构体),按照设计的一致,type对象里面也有类型指向,那么怎么终止嵌套呢?type的类型指向了它自己.....嵌套结束....那么这个'游戏'的规则是这样的:
一个小验证:
class A:
pass
a = A()
print(a.__class__) # <class '__main__.A'>
print(A.__class__) # <class 'type'>
print(type.__class__) # <class 'type'>
也就是说a的类型是A,A的类型是type,type的类型还是type
感觉越跑越偏了,python中的类创建和元类跟这些'游戏规则'有什么联系?现在也没涉及到'创建'这个概念啊,那是因为还缺少一个东西,让'游戏'拥有'创建'的功能...
object
缺少的东西就是这个----object,我们知道,在python2.x的时候看见一些代码在写类的时候必须继承一个object,如下:
class human(object):
def run(self):
print 'i am running...'
就连type中也要....
class type(object):
...
那么这个object很有可能就是拥有创建一切能力的东西,类都要继承于它(暂时看来),当然,python底层也封装好了这个object类对象,那么它应该也有对应指向的类型啊,它的类型是什么呢?
print(object.__class__)
# <class 'type'>
......绕晕了,object的类型竟然是type,也就是说type要继承object,而object的类型是type,object不继承任何东西(继承的顶端)
果不其然,用起来简单的东西,里面的设计就是这么复杂且绕.....
先介绍类'玩法'的两个概念性的东西:
实例化:由类实例化出来对象的一个功能
继承: 子类继承于父类,也就是子类可以用父类的公有属性和方法
type和object就是为了满足这个游戏规则而设计出来的产物,都是为了结束嵌套而早就封装定义好的两个结构体(对象)
type: 我是实例化的顶端
object: 我是继承的顶端
纵观这个python设计里面,加上object之后就是这样的了:
从图中看,现在的游戏规则就是:
所有'类型链'的顶端是type 。所有'继承链'的顶端是object
类型是能够'提供'给使用者创建出实例的功能,图中的type和对象2都需要有这样的一个功能,在图中看出他们都继承自object,那么根据继承的玩法,如果object中有这样的一个'创建出'实例的功能,那就皆大欢喜了。
没错,object就是有这样的功能------在内存中建立'对象'
也就是图中的黑色框那玩意,是object._new_()出来的,被object._new_()出来之后也只是一个空壳,并没有我们想要定义的东西啊例如自定义属性、方法之类的,当然,在object创建出的空框之后,就会调用我们定义类的_new_方法,而我们常用的_init_方法则是之后才调用,保证让你想怎么样就怎么样
实例一个类的过程
class A(object):
# 我们自定义这个类的__new__方法
def __new__(cls, *args, **kwargs):
print('创建的时候用父类object的__new__方法获得一个实例(空)')
instance = super().__new__(cls, *args, **kwargs)
print('在此自定义实例化后增加的东西')
return instance
def __init__(self, *args, **kwargs):
print('最后用到自定义的__init__')
pass
a = A()
得出:
# 创建的时候用父类object的__new__方法获得一个实例(空)
# 在此自定义实例化后增加的东西
# 最后用到自定义的__init__
实例化调用链:
my_class._new_() ------> 父类(object)._new_() ------> instance(实例)
此时解决了我们正常使用的时候定义的class xxx实例化的过程,但是,我们定义的类也是一个对象啊,也需要它的类型进行实例化这样的一个过程,上面讲到,类对象的类型是type,人家封装好了,我们还怎么进去分析甚至是用它? 想要它的能力的话,那就继承它吧....也是接下来说的----元类...
元类metaclass
概念不难懂,就是上述定义的类型A的类型, 简称类的类(很绕),也就是元类...,要使用元类的话必须要有type的'功能',那我们就继承它吧....
class my_meta(type):
pass
咋一看又是一个class......看到此处也是懵逼的,那么先把疑问抛弃,看一下这玩意能够实例出什么来,运用上面类进行实例的过程!
调用my_meta._new_()方法的时候,找父类(继承于type),父类是type,就是要调用type._new_(),type也有父类啊(继承于object),又调用object._new_()去创建一个实例空壳,回来之后这个实例空壳回到了type的_new_方法中,由于type被设计者封装了,里面就是把这个实例'赋予'类属性和方法,也就是最上面图中对象2里面的属性跟方法。
class my_meta(type):
# 这个实例过程需要传入3个参数
# 元类,类名, 继承, 属性方法
def __new__(mcls, name, bases, attrs):
# 调用type的.__new__方法,其中调用object生成空壳
# 对该空壳根据传入type中的参数mcls, name, bases, attrs进行填补进去
# cls = super().__new__(mcls, name, bases, attrs)
cls = type.__new__(mcls, name, bases, attrs)
return cls
my_class = my_meta('my_class', (), {'name':'my_class'})
print(my_class.name)
# my_class拥有实例功能(证明我是一个类)
m = my_class()
print(m.name)
# my_class
# my_class
我们由自定义my_meta类继承于type,实例出来的就像我们平时定义的类,也就是说,我们现在可以动态控制类的生成。
更通用的写法:
class my_meta(type):
# 这个实例过程需要传入3个参数
# 元类,类名, 继承, 属性方法
def __new__(mcls, name, bases, attrs):
# 调用type的.__new__方法,其中调用object生成空壳
# 对该空壳根据传入type中的参数mcls, name, bases, attrs进行填补进去
# cls = super().__new__(mcls, name, bases, attrs)
print('改你想改的(传入参数修改)!')
cls = type.__new__(mcls, name, bases, attrs)
print('改你想改的(返回之后的修改)!')
return cls
# 显式传入metaclass参数,否则仍然把这个类对象用type实例出来
class A(metaclass=my_meta):
# 程序这里的名字A,该类继承于xx,类方法__new__和__init__和speak
# 将会传入my_meta中作为参数(name, bases, attrs)
def __new__(cls, *args, **kwargs):
return super().__new__(cls, *args, **kwargs)
def __init__(self, *args, **kwargs):
pass
def speak(self):
print('speaking..')
# 改你想改的(传入参数修改)!
# 改你想改的(返回之后的修改)!
最后的输出就说明了我们写好了这个类时候,会经过my_meta的创建过程!
解决一些坑
1、有人会问,我们不是还写了class my_meta吗,那它这是谁创建的啊?
根据游戏规则,my_meta的类是谁?查找到父类type,它的类是type自身,new出这个my_meta就是调用了type的new再往上object的new,而type是底层封装好的,终止嵌套。。。
2、实例化的时候,调用的是类加括号进行实例化(A()),是怎么回事?
当调用A()的时候,python会从A的类中查找其_call_方法,当A的类为type的时候,也就是调用type._call_(A, *args, **kwargs),里面调用A._new_(A, *args, **kwargs),同上进行实例化过程...
也就是为什么我们在定义一个类的时候,定义了其_call_方法就可以是其类的实例拥有'可调用'的功能:
class A:
def __init__(self):
self.name = 'a'
def __call__(self, sth):
return self.name + ' speak ' + sth
a = A()
print(a('hello'))
# a speak hello
这就是同理在type中控制了其实例出来的东西拥有'可调用'的功能,并且调用之后是实例化该类,故此我们实例一个类的时候直接A()即可,伪代码:
class type:
def __new__(...):
...
def __call__(cls, *args, **kwargs):
# 调用其__new__方法
instance = cls.__new__(cls, *args, **kwargs)
# 调用其__init__方法进行初始化
instance.__init__(*args, **kwargs)
return instance
3、type和object这样的鸡生蛋蛋生鸡的关系,究竟为什么要这么绕?
type和object都是为了python这样的对象系统而存在的,至于为什么要这么设计,只能说,游戏规则由设计者定...javascript甚至其他语言的对象系统都有自己的一套方法,type和object正是为了给这个游戏提供支持而已。
最后
这里只是显浅地对python中类创建过程和元类的理解,并不完全严谨,要理解其语言的强大之处还需要懂c语言并且阅读其解析器的源代码