python定义一个圆类_Python中类创建的过程以及元类的理解

首先类是一个什么东西

类是数据与操作其数据方法的封装

一个定义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中的对象, 避免复杂化问题,这里简化出对象实际上是占用内存中的一个东西:

内存中对象.png

python的解析器是用c写的,那么,在c中描述这个对象的话,用的是一个c结构体,而这个结构体里面,不止上面对象那么简单,而是:

复杂一点点的对象.png

在这个对象(结构体)里面,有一个fn的东西叫做引用计数,这个暂时放一放,先来讨论class这个变量,这个变量说明(指向)了一个xx(暂时未知)的类型,在python设计的''游戏规则''里面,此时就能够告诉我们,这一个对象是一个xx类型的对象,既然在python里面处处都是对象,那么这个对象指向类型,也应该是一个对象,如图:

对象的类型.png

类型(对象2)也是一个对象,但是对象2的类型是谁呢?总不可能这样这样嵌套下去吧,对的,python的设计开始要把这个玩法''圆''起来了,那么是什么呢?

type

对象2的类型就是type,'自圆其说'的终点,这个type是python底层封装好的一个类型(结构体),按照设计的一致,type对象里面也有类型指向,那么怎么终止嵌套呢?type的类型指向了它自己.....嵌套结束....那么这个'游戏'的规则是这样的:

类型链.png

一个小验证:

class A:

pass

a = A()

print(a.__class__) #

print(A.__class__) #

print(type.__class__) #

也就是说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__)

#

......绕晕了,object的类型竟然是type,也就是说type要继承object,而object的类型是type,object不继承任何东西(继承的顶端)

果不其然,用起来简单的东西,里面的设计就是这么复杂且绕.....

先介绍类'玩法'的两个概念性的东西:

实例化:由类实例化出来对象的一个功能

继承: 子类继承于父类,也就是子类可以用父类的公有属性和方法

type和object就是为了满足这个游戏规则而设计出来的产物,都是为了结束嵌套而早就封装定义好的两个结构体(对象)

type: 我是实例化的顶端

object: 我是继承的顶端

纵观这个python设计里面,加上object之后就是这样的了:

关系.png

从图中看,现在的游戏规则就是:

所有'类型链'的顶端是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语言并且阅读其解析器的源代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值