python元类的应用_[python 进阶] 详解元类及其应用 2

[python 进阶] 详解元类及其应用 2

前言

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

5. 使 type 创建带有法的类

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

添加实例法In[14]:defecho_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

...:deftestStatic():

...: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]:

In[24]:fooclid.testStatic()

staticmethod....

In[25]:fooclid.echo_bar()

True

添加类法In[26]:@classmethod

...:deftestClass(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]:deffoo():

...:pass

...:

In[35]:foo.__class__

Out[35]:function

In[36]:classBar(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]:classFoo(Bar):

...:pass

...:

Python 做了如下的操作:

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

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

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

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

现在的问题就是, 你可以在__metaclass__中放置些什么代码呢? 答案就 是: 可以创建个类的东. 那么什么可以来创建个类呢? type, 或者任何使到 type 或者类化 type 的东东都可以.

8. 定义元类

元类的主要的就是为了当创建类时能够动地改变类. 通常, 你会为 API 做 这样的事情, 你希望可以创建符合当前上下的类.

假想个很傻的例, 你决定在你的模块所有的类的属性都应该是写形 式. 有好种法可以办到, 但其中种就是通过在模块级别设定 __metaclass__. 采这种法, 这个模块中的所有类都会通过这个元类来创建, 我们只需要告诉元类把所有的属性都改成写形式就万事吉了.

幸运的是,__metaclass__实际上可以被任意调, 它并不需要是个正式的类. 所以, 我们这就先以个简单的函数作为例开始.

python3 中#-*-coding:utf-8-*-

defupper_attr(future_class_name,future_class_parents,future_class_attr):

#遍历属性字典, 把不是__开头的属性名字变为写

newAttr={}

forname,valueinfuture_class_attr.items():

ifnotname.startswith("__"):

newAttr[name.upper()]=value

#调 type 来创建个类

returntype(future_class_name,future_class_parents,newAttr)

classFoo(object,metaclass=upper_attr):

bar='bip'

print(hasattr(Foo,'bar'))

print(hasattr(Foo,'BAR'))

f=Foo()

print(f.BAR)

现在让我们再做次, 这次个真正的 class 来当做元类.#-*-coding:utf-8-*-

classUpperAttrMetaClass(type):

#__new__是在__init__之前被调的特殊法

#__new__是来创建对象并返回之的法

#__init__只是来将传的参数初始化给对象

#你很少到__new__, 除你希望能够控制对象的创建

#这, 创建的对象是类, 我们希望能够定义它, 所以我们这改写__new__

#如果你希望的话, 你也可以在__init__中做些事情

#还有些级的法会涉及到改写__call__特殊法, 但是我们这不

def__new__(cls,future_class_name,future_class_parents,future_class_attr):

#遍历属性字典, 把不是__开头的属性名字变为写

newAttr={}

forname,valueinfuture_class_attr.items():

ifnotname.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 法

returnsuper(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 的法

classFoo(object,metaclass=upper_attr):

bar='bip'

print(hasattr(Foo,'bar'))

# 输出: False

print(hasattr(Foo,'BAR'))

# 输出: True

f=Foo()

print(f.BAR)

# 输出:'bip'

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

拦截类的创建

修改类

返回修改之后的类

究竟为什么要使元类?

现在回到我们的主题上来, 究竟是为什么你会去使这样种容易出错且晦涩的特性? 好吧, 般来说, 你根本就不上它:

"元类就是深度的魔法, 99% 的户应该根本不必为此操. 如果你想搞清楚 究竟是否需要到元类, 那么你就不需要它. 那些实际到元类的都常 清楚地知道他们需要做什么, 且根本不需要解释为什么要元类."-- Python 界的领袖TimPeters

来源: https://www.cnblogs.com/ECJTUACM-873284962/p/8886528.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值