【python进阶】详解元类及其应用2

前言

在上一篇文章【python进阶】详解元类及其应用1中,我们提到了关于元类的一些前置知识,介绍了类对象,动态创建类,使用type创建类,这一节我们将继续接着上文来讲~~~

5.使?type创建带有?法的类

最终你会希望为你的类增加?法。只需要定义?个有着恰当签名的函数并将 其作为属性赋值就可以了。
添加实例?法

In [14]: def echo_bar(self):#定义了一个普通的函数
    ...:     print(self.bar)
    ...:

In [15]: FooChild = type(‘FooChild‘,(Foo,),{‘echo_bar‘:echo_bar})

In [16]: hasattr(Foo,‘echo_bar‘)#判断Foo类中,是否有echo_bar这个属性
Out[16]: False

In [17]: hasattr(FooChild,‘echo_bar‘)#判断FooChild类中,是否有echo_bar这个属性
Out[17]: True

In [18]: my_foo=FooChild()
    ...:

In [19]: my_foo.echo_bar()
True

添加静态?法

In [20]: @staticmethod
    ...: def testStatic():
    ...:     print("static method ....")
    ...:

In [21]: Foochild=type(‘Foochild‘,(Foo,),{"echo_bar":echo_bar,"testStatic":testStatic})

In [22]: fooclid=Foochild()

In [23]: fooclid.testStatic
Out[23]: <function __main__.testStatic>

In [24]: fooclid.testStatic()
static method ....

In [25]: fooclid.echo_bar()
True

添加类?法

In [26]: @classmethod
    ...: def testClass(cls):
    ...:     print(cls.bar)
    ...:

In [27]: Foochild=type(‘Foochild‘,(Foo,),{"echo_bar":echo_bar,"testStatic":testStatic,"testClass":testClass})

In [28]: fooclid = Foochild()

In [29]: fooclid.testClass()
True

你可以看到,在Python中,类也是对象,你可以动态的创建类。这就是当你 使?关键字class时Python在幕后做的事情,?这就是通过元类来实现的。

6.到底什么是元类(终于到主题了)

元类就是?来创建类的“东?”。你创建类就是为了创建类的实例对象,不是吗?但是我们已经学习到了Python中的类也是对象。
元类就是?来创建这些类(对象)的,元类就是类的类,你可以这样理解为:

MyClass = MetaClass()#使?元类创建出?个对象,这个对象称为“类”
MyObject = MyClass()#使?“类”来创建出实例对象

你已经看到了type可以让你像这样做:

MyClass=type(‘MyClass‘,(),{})

这是因为函数type实际上是?个元类。type就是Python在背后?来创建所有类的元类。现在你想知道那为什么type会全部采??写形式?不是Type呢? 好吧,我猜这是为了和str保持?致性,str是?来创建字符串对象的类,?int 是?来创建整数对象的类。type就是创建类对象的类。你可以通过检查 __class__属性来看到这?点。Python中所有的东?,注意,我是指所有的东 ?——都是对象。这包括整数、字符串、函数以及类。它们全部都是对象, ?且它们都是从?个类创建?来,这个类就是type。

In [30]: age = 35

In [31]: age.__class__
Out[31]: int

In [32]: name = ‘bob‘

In [33]: name.__class__
Out[33]: str

In [34]: def foo():
    ...:     pass
    ...:

In [35]: foo.__class__
Out[35]: function

In [36]: class Bar(object):
    ...:     pass
    ...:

In [37]: b = Bar()

In [38]: b.__class__
Out[38]: __main__.Bar

现在,对于任何?个__class__的__class__属性?是什么呢?

In [40]: name.__class__.__class__
Out[40]: type

In [41]: age.__class__.__class__
Out[41]: type

In [42]: foo.__class__.__class__
Out[42]: type

In [43]: b.__class__.__class__
Out[43]: type

因此,元类就是创建类这种对象的东?。type就是Python的内建元类,当然 了,你也可以创建??的元类。

7.__metaclass__属性

你可以在定义?个类的时候为其添加__metaclass__属性。

class Foo(object):
      __metaclass__ = something...
      ...省略...

如果你这么做了,Python就会?元类来创建类Foo。??点,这??有些技 巧。你?先写下class Foo(object),但是类Foo还没有在内存中创建。Python 会在类的定义中寻找__metaclass__属性,如果找到了,Python就会?它来创建类Foo,如果没有找到,就会?内建的type来创建这个类。把下?这段话反复读?次。当你写如下代码时 :

In [44]: 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,或者任何使?到type或者?类化type的东东都可以。

8.?定义元类

元类的主要?的就是为了当创建类时能够?动地改变类。通常,你会为API做 这样的事情,你希望可以创建符合当前上下?的类。
假想?个很傻的例?,你决定在你的模块?所有的类的属性都应该是?写形 式。有好?种?法可以办到,但其中?种就是通过在模块级别设定 __metaclass__。采?这种?法,这个模块中的所有类都会通过这个元类来创建,我们只需要告诉元类把所有的属性都改成?写形式就万事?吉了。
幸运的是,__metaclass__实际上可以被任意调?,它并不需要是?个正式的类。所以,我们这?就先以?个简单的函数作为例?开始。

python3中

#-*-coding:utf-8-*-
def upper_attr(future_class_name,future_class_parents,future_class_attr):

    #遍历属性字典,把不是__开头的属性名字变为?写
    newAttr = {}
    for name,value in future_class_attr.items():
        if not name.startswith("__"):
            newAttr[name.upper()] = value

    #调?type来创建?个类
    return type(future_class_name,future_class_parents,newAttr)

class Foo(object,metaclass=upper_attr):
    bar = ‘bip‘

print(hasattr(Foo,‘bar‘))
print(hasattr(Foo,‘BAR‘))
f = Foo()
print(f.BAR)

现在让我们再做?次,这?次??个真正的class来当做元类。

#-*-coding:utf-8-*-
class UpperAttrMetaClass(type):

    #__new__是在__init__之前被调?的特殊?法
    #__new__是?来创建对象并返回之的?法
    #?__init__只是?来将传?的参数初始化给对象
    #你很少?到__new__,除?你希望能够控制对象的创建
    #这?,创建的对象是类,我们希望能够?定义它,所以我们这?改写__new__
    #如果你希望的话,你也可以在__init__中做些事情
    #还有?些?级的?法会涉及到改写__call__特殊?法,但是我们这?不?
    def __new__(cls,future_class_name,future_class_parents,future_class_attr):
        #遍历属性字典,把不是__开头的属性名字变为?写
        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_parents,future_class_attr)

        #?法3:使?super?法
        return super(UpperAttrMetaClass,cls).__new__(cls,future_class_name,future_class_parents,future_class_attr)

#python2的?法
#class Foo(object):
#    __metaclass__ = upper_attr#设置Foo类的元类为upper_attr
#    bar = ‘bip‘

#python3的?法
class Foo(object,metaclass=upper_attr):
    bar = ‘bip‘

print(hasattr(Foo,‘bar‘))
#输出:False
print(hasattr(Foo,‘BAR‘))
#输出:True
f = Foo()
print(f.BAR)
#输出:‘bip‘

就是这样,除此之外,关于元类真的没有别的可说的了。但就元类本身? ?,它们其实是很简单的:

  1. 拦截类的创建
  2. 修改类
  3. 返回修改之后的类

究竟为什么要使?元类?

现在回到我们的?主题上来,究竟是为什么你会去使?这样?种容易出错且晦涩的特性?好吧,?般来说,你根本就?不上它:
“元类就是深度的魔法,99%的?户应该根本不必为此操?。如果你想搞清楚 究竟是否需要?到元类,那么你就不需要它。那些实际?到元类的?都?常 清楚地知道他们需要做什么,?且根本不需要解释为什么要?元类。” —— Python界的领袖 Tim Peters

原文地址:https://www.cnblogs.com/ECJTUACM-873284962/p/8886528.html

时间: 2024-08-24 03:32:01

【python进阶】详解元类及其应用2的相关文章

python difflib详解

difflib -帮助进行差异化比较 这个模块提供的类和方法用来进行差异化比较,它能够生成文本或者html格式的差异化比较结果,如果需要比较目录的不同,可以使用filecmp模块. class difflib.SequenceMatcher 这是可以用来比较任何类型片段的类,只要比较的片段是可hash的,都可以用来比较,使用非常灵活.他源于1980,s的“完形匹配算法”,并且进行了一系列的优化和改进. 通过对算法的复杂度比较,它由于原始的完形匹配算法,在最坏情况下有n的平方次运算,在最好情况下,

python正则表达式详解

python正则表达式详解 正则表达式是一个很强大的字符串处理工具,几乎任何关于字符串的操作都可以使用正则表达式来完成,作为一个爬虫工作者,每天和字符串打交道,正则表达式更是不可或缺的技能,正则表达式的在不同的语言中使用方式可能不一样,不过只要学会了任意一门语言的正则表达式用法,其他语言中大部分也只是换了个函数的名称而已,本质都是一样的.下面,我来介绍一下python中的正则表达式是怎么使用的. 首先,python中的正则表达式大致分为以下几部分: 元字符 模式 函数 re 内置对象用法 分组用

python线程详解

#线程状态 #线程同步(锁)#多线程的优势在于可以同时运行多个任务,至少感觉起来是这样,但是当线程需要共享数据时,可能存在数据不同步的问题. #threading模块#常用方法:'''threading.currentThread():返回当前的线程变量threading.enumerate():返回一个包含正在运行的线程的list,正在运行指:线程启动后,结束前,不包含启动前和终止后的线程threading.activeCount():返回正在运行的线程数量,与len(threading.en

Unity3D游戏开发之详解 Animation类和Animator类

Unity3D游戏开发之详解 Animation类和Animator类 Animation类 animation组件用于播放动画.可以指定动画剪辑到动画组件并从脚本控制动画播放.在Unity的动画系统基于权重并且支持动画融合,叠加动画,动画混合,标签和完全控制动画播放的各个方面. 如果想播放一个简单的动画,可以使用Animation.Play:如果想在动画之间交叉淡入,可以使用Animation.CrossFade:如果想改变动画模式(循环,一次,乒乓),可以改变动画导入设置里面的动画帧的Wra

Unity3D - 详解Quaternion类(二)

OK,不做引子了,接上篇Unity3D - 详解Quaternion类(一)走起! 四.Quaternion类静态方法 Quaternion中的静态方法有9个即:Angle方法.Dot方法.Euler方法.FromToRotation方法.Inverse方法.Lerp方法.LookRotation方法.RotateToWards方法和Slerp方法.关于静态的方法的使用就是直接用类名调用其静态方法,例如Quaternion.Angle(q1,q2);下面对这些静态方法做下分析. 1.Angle方

PHP面向对象编程详解:类和对象

PHP面向对象编程详解:类和对象 从OOP的视角看,不应区分语言.无论是C++.无论是Java.无论是.net还有更多面向对象的语言,只要你了解了OO的真谛,便可以跨越语言,让你的思想轻松的跳跃.便没有对于Java..net.PHP 之间谁强谁弱的争执了. 希望这个介绍PHP5面向对象编程(OOP)的资料能让初学者受益,能让更多的PHPer开始转向OO的编程过程. 相对PHP4,PHP5在面向对象方面改变了很多.我们将只介绍PHP5环境下的面向对象.而我们必须改变自己来跟随PHP5的发展.如果代

转 python数据类型详解

python数据类型详解 目录 1.字符串 2.布尔类型 3.整数 4.浮点数 5.数字 6.列表 7.元组 8.字典 9.日期 1.字符串 1.1.如何在Python中使用字符串 a.使用单引号(') 用单引号括起来表示字符串,例如: str='this is string'; print str; b.使用双引号(") 双引号中的字符串与单引号中的字符串用法完全相同,例如: str="this is string"; print str; c.使用三引号(''') 利用三

Python——五分钟理解元类(metaclasses)

“元类的魔幻变化比 99% 的用户所担心的更多,当你搞不懂是否真的需要用它的时候,就是不需要.” —Tim Peters 本文源于在 PyCon UK 2008 上的一个快速演讲. 元类被称为 Python 中的“深奥的巫术”.尽管你需要用到它的地方极少(除非你基于zope 编程),可事实上它的基础理论其实令人惊讶地易懂. 一切皆对象 一切皆对象 一切都有类型 “class”和“type”之间本质上并无不同 类也是对象 它们的类型是 type 以前,术语 type 用于内置类型,而术语 clas

Python列表详解

Python列表详解: 创建一个列表,只要把逗号分隔的不同数据项使用方括号括起来即可. 比如:    list = [1, 2, 3, 4, 5 ]; 与字符串的索引一样,列表索引从0开始. Python列表函数即方法: Python所包含的函数: 1.cmp() 描述: cmp()用于比较两个列表的元素. 语法: cmp (list1,list2) 返回值: 如果比较的元素是同类型的,则比较其值,返回结果. 如果两个元素不是同一种类型,则检查它们是否是数字. 如果是数字,执行必要的数字强制类型