元类引入

在多数语言中,类就是一组用来描述如何生成对象的代码段,在python中同样如此,但是在python中把类也称为类对象,是的,你没听错,在这里你只要使用class关键字定义了类,其解释器在执行时就会创建一个对象,但是这个对象比较特殊,它可以创建自己的实例对象(像其他语言一样)。你可以对它进行下面的操作:

1. 你可以将它赋值给⼀个变量

2. 你可以拷⻉它

3. 你可以为它增加属性

4. 你可以将它作为函数参数进⾏传递

动态创建类

听起来python在这方面跟一些语言的差别了吧,没错它还可以在运行时动态地创建它们,你可以在函数中创建类,用class关键字即可。

def choose_class(name):
if name == 'foo':
class Foo(object):
pass
return Foo # 返回的是类, 不是类的实例
else:
class Bar(object):
pass
return Bar

如果你想知道python中的变量或者数值的类型,你可以用万能而强大的type,如下(注意它们的返回值)

>>> print type(1) #数值的类型
<type 'int'>
>>> print type("1") #字符串的类型
<type 'str'>
>>> print type(ObjectCreator()) #实例对象的类型
<class '__main__.ObjectCreator'>
>>> print type(ObjectCreator) #类的类型
<type 'type'>

使用type创建类

创建格式:type(类名, 由⽗类名称组成的元组( 针对继承的情况, 可以为空) ,包含属性的字典( 名称和值) )
可以⼿动像这样创建:
Test2 = type("Test2",(),{}) #定了⼀个Test2类
In [5]: Test2() #创建了⼀个Test2类的实例对象
Out[5]: <__main__.Test2 at 0x10d406b38>

上面等价于

class Test2:
pass

使⽤type创建带有属性的类

Foo = type('Foo', (), {'bar':True})

可以翻译为

>>> class Foo(object):
… bar = True

这个类跟普通创建的类一样使用,如继承这个类,你可以用下面的方法

>>> FooChild = type('FooChild', (Foo,),{})
>>> print FooChild
<class '__main__.FooChild'>
>>> print FooChild.bar # bar属性是由Foo继承⽽来
True

同样等价于

>>> class FooChild(Foo):
… pass

注意:添加的属性都是类属性,第二个参数是继承自哪儿,采用元组的形式,也要知道在python中元组中只有一个参数表示方法只能是(xx,),而不能是(xx)

使⽤type创建带有⽅法的类

如果你想为你的类增加方法,那么你创建一个签名函数,然后添加其为属性赋值即可。

添加实例方法

In [46]: def echo_bar(self): #定义了⼀个普通的函数
...: print(self.bar)
...:
In [47]: FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
In [48]: hasattr(Foo, 'echo_bar') #判断Foo类中, 是否有echo_bar这个属性
Out[48]: False
In [49]:
In [49]: hasattr(FooChild, 'echo_bar') #判断FooChild类中, 是否有echo_bar这个
Out[49]: True
In [50]: my_foo = FooChild()
In [51]: my_foo.echo_bar()
True

添加类方法

In [42]: @classmethod
...: def testClass(cls):
...: print(cls.bar)
...:
In [43]:
In [43]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar,"testClass":testClass})
In [44]:
In [44]: fooclid = Foochild()
In [45]: fooclid.testClass()
True

添加静态方法

In [36]: @staticmethod
...: def testStatic():
...: print("static method ....")
...:
In [37]: Foochild = type('Foochild', (Foo,), {"echo_bar":echo_bar,“testStatic”:testStatic})
In [38]: fooclid = Foochild()
In [39]: fooclid.testStatic
Out[39]: <function __main__.testStatic>
In [40]: fooclid.testStatic()
static method ....
In [41]: fooclid.echo_bar()
True

看到这么多实现动态类的方法,你该明白python是怎么做到的,在这里就涉及到元类,元类就是用来创建类的东西,而创建类就是为了创建类的实例对象,所以可以这样理解,元类就是为了创建类对象的,而type实际上就是一个元类,传统类的元类就是types.classType,在python中元类的介绍中也介绍了元类,有兴趣的读者可以去看看。

SO,结论就是Python中所有的东⻄, 注意, 我是指所有的东⻄——都是对象。 这包括整数、 字符串、 函数以及类。 它们全部都是对象,⽽且它们都是从⼀个类创建⽽来, 这个类就是type。

__metaclass__属性

你也可以定义自己的元类,用到的就是上面的属性,首先,你可以在定义⼀个类的时候为其添加__metaclass__属性。
class Foo(object):
__metaclass__ = something…
#...省略...

那么当python解释器看到下面代码时

class Foo(Bar):
pass

python做了如下的操作:

1. Foo中有__metaclass__这个属性吗? 如果是, Python会通过__metaclass__创建⼀个名字为Foo的类(对象)

2. 如果Python没有找到__metaclass__, 它会继续在Bar( ⽗类) 中寻找__metaclass__属性, 并尝试做和前⾯同样的操作。

3. 如果Python在任何⽗类中都找不到__metaclass__, 它就会在模块层次中去寻找__metaclass__, 并尝试做同样的操作。

4. 如果还是找不到__metaclass__,Python就会⽤内置的type来创建这个类对象。

在这个__metaclass__中可以放一些什么代码呢?------可以做任何涉及到类操作的事情,用type或者其子类来完成。
下面是用自定义类来当做元类
#coding=utf-8
class UpperAttrMetaClass(type):
# __new__ 是在__init__之前被调⽤的特殊⽅法
# __new__是⽤来创建对象并返回之的⽅法
# ⽽__init__只是⽤来将传⼊的参数初始化给对象
# 你很少⽤到__new__, 除⾮你希望能够控制对象的创建
# 这⾥, 创建的对象是类, 我们希望能够⾃定义它, 所以我们这⾥改写__new__
# 如果你希望的话, 你也可以在__init__中做些事情
# 还有⼀些⾼级的⽤法会涉及到改写__call__特殊⽅法, 但是我们这⾥不⽤
def __new__(cls, future_class_name, future_class_parents, future_clas
#遍历属性字典, 把不是__开头的属性名字变为⼤写
newAttr = {}
for name,value in future_class_attr.items():
if not name.startswith("__"):
newAttr[name.upper()] = value
# ⽅法1: 通过'type'来做类对象的创建
# return type(future_class_name, future_class_
parents, newAttr)
# ⽅法2: 复⽤type.__new__⽅法
# 这就是基本的OOP编程, 没什么魔法
# return type.__new__(cls, future_class_name, future_class_parent
# ⽅法3: 使⽤super⽅法
return super(UpperAttrMetaClass, cls).__new__(cls, future_class_n
#python2的⽤法
class Foo(object):
__metaclass__ = UpperAttrMetaClass
bar = 'bip'
# python3的⽤法
# class Foo(object, metaclass = UpperAttrMetaClass):
# bar = 'bip'
print(hasattr(Foo, 'bar'))
# 输出: False
print(hasattr(Foo, 'BAR'))
# 输出:True
f = Foo()
print(f.BAR)
# 输出:'bip'
其实简单地来讲,python中的元类用处一般就是下面三个

1. 拦截类的创建

2. 修改类

3. 返回修改之后的类

哈哈,我想说的是对于元类咱们99%的人不会用到。

python核心高级学习总结6------面向对象进阶之元类的更多相关文章

  1. python 面向对象进阶之元类metaclass

    一:知识储备 exec exec:三个参数 参数一:字符串形式的命令 参数二:全局作用域(字典形式),如果不指定,默认为globals() 参数三:局部作用域(字典形式),如果不指定,默认为local ...

  2. python核心高级学习总结5--------python实现线程

    在代码实现上,线程的实现与进程的实现很类似,创建对象的格式都差不多,然后执行的时候都是用到start()方法,与进程的区别是进程是资源分配和调度的基本单位,而线程是CPU调度和分派的基本单位.其中多线 ...

  3. python核心高级学习总结7---------正则表达式

    正则表达式在爬虫项目中应用很广泛,主要方面就是在字符串处理方面,经常会涉及到字符串格式的校验,用起来经常要查看文档才能完成,所以抽了个时间将正则的内容复习了一下. Start re---导入re模块使 ...

  4. python核心高级学习总结8------动态性、__slots__、生成器、迭代器、装饰、闭包

    python的动态性 什么是动态性呢,简单地来说就是可以在运行时可以改变其结构,如:新的函数.对象.代码都可以被引进或者修改,除了Python外,还有Ruby.PHP.javascript等也是动态语 ...

  5. python核心高级学习总结3-------python实现进程的三种方式及其区别

    python实现进程的三种方式及其区别 在python中有三种方式用于实现进程 多进程中, 每个进程中所有数据( 包括全局变量) 都各有拥有⼀份, 互不影响 1.fork()方法 ret = os.f ...

  6. python核心高级学习总结1---------*args和**kwargs

    *args 和 ** kwargs 的用法 首先,这两者在用法上都是用来补充python中对不定参数的接受. 比如下面的列子 def wrappedfunc(*args, **kwargs): pri ...

  7. python核心编程学习记录之面向对象编程

    未完待续525

  8. python核心高级学习总结4-------python实现进程通信

    Queue的使用 Queue在数据结构中也接触过,在操作系统里面叫消息队列. 使用示例 # coding=utf-8 from multiprocessing import Queue q = Que ...

  9. python核心高级学习总结2----------pdb的调试

    PDB调试 def getAverage(a,b): result =a+b print("result=%d"%result) return result a=100 b=200 ...

随机推荐

  1. 【Kata Daily 190929】Password Hashes(密码哈希)

    题目: When you sign up for an account somewhere, some websites do not actually store your password in ...

  2. Mybatis 批量更新遇到的小问题

    小问题 记一个开发过程中因为小细节的遗漏而引发的 "莫名其妙",公司中有个2B(to B)供应链项目,持久层用的是 JPA,这里我就不吐槽 JPA 了,这种 SQL 嵌入在代码里的 ...

  3. Flink SQL Client初探

    欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...

  4. Activit的心路历程:获取当前节点的下一节点【可能存在多个】的nodeId

    上一任务节点 在我的开发任务中,突然给我提出了一个待办任务需要获取当前任务节点下一任务节点的表单信息,刚开始搞得我有点措手不及,后来仔细是靠后,灵感一下,直接操作流程的bpmn信息就可以获取到节点信息 ...

  5. LOJ #6029. 「雅礼集训 2017 Day1」市场 线段树维护区间除法

    题目描述 从前有一个贸易市场,在一位执政官到来之前都是非常繁荣的,自从他来了之后,发布了一系列奇怪的政令,导致贸易市场的衰落. 有 \(n\) 个商贩,从\(0 \sim n - 1\) 编号,每个商 ...

  6. CSS动画animation

    transition: 过渡动画transition-property: 属性transition-duration: 间隔transition-timing-function: 曲线transiti ...

  7. leetcode bitmap系列问题整理

    1. 题目: 编写一个函数,输入是一个无符号整数,返回其二进制表达式中数字位数为 '1' 的个数(也被称为汉明重量). 示例 : 输入: 11输出: 3解释: 整数 11 的二进制表示为 000000 ...

  8. MySQL时间类型datetime、bigint及timestamp的查询效率

    前期数据准备 通过程序往数据库插入 50w 数据 数据表: CREATE TABLE `users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `time_dat ...

  9. BeatifulSoup在测试工作中的应用

    近期要做一个项目,重复性劳动比较多,小伙伴建议我用Jsoup,但是由于项目紧急,我直接选择了BeautifulSoup,关键原因是我Java语言不如Python掌握的熟练啊!所以,查了一圈它的中文文档 ...

  10. MongoDB笔记2018.2.6

    MongoDB笔记2018.2.6 1.先简单接触下linux,大部分教程是在linux环境下的操作,要不然看不懂教程 2.找教程或视频 教程推荐菜鸟教程 学习MongDB前需要懂一定的Mysql语法 ...