1. 理解  class

  • 对于 class 来说,表示一个代码块规定了实例化后的 object 的属性和方法
  • 但是在 Python 中,class 本身也是对象。定义一个 class,就相当于在内存中实例化了一个名为 className 的对象
  • 作为一个对象,因此具备以下能力:
    • 赋值给一个变量
    • 对其拷贝
    • 作为函数参数
class Example(object):
pass object1 = Example()
print(object1)
# prints '<__main__.Example object at 0x102e26990>'
print(Example)
# prints '<class '__main__.Example'>'
Example_Mirror = Example
print(Example_Mirror)
# prints '<class '__main__.Example'>'

2. 动态创建 class

  • 由于 class 也是 object,因此可以像创建普通 object一样动态创建 class:
def Dynamic_Class_Creater(name):
if name == 'name1':
class Class1(object):
pass
return Class1
else:
class Class2(object):
pass
return Class2 first = Dynamic_Class_Creater('name1')
print(first)
# prints '<class '__main__.Class1'>'
print(first())
# prints '<__main__.Class1 object at 0x105452e10>'
  • 使用 class 关键字创建类的时候,Python 会自动创建对应的 object。像 Python 中其他大多数情况一样,我们也可以通过 type() 创建这个 class object
  • 通常可以通过 type 查看对象类型:
# prints '<type 'type'>'
print(type(Example()))
# prints '<class '__main__.Example'>'
  • type() 函数可以接收 class 的描述来作为参数并返回所生成的 class object。type 同时具有这两个迥异的功能是由于 Python 兼容性问题导致的,不做深究
  • 通过 type 创建 class 是,用法如下:
type(class_name, tuple_of_parent_class, dict_of_attribute_names_and_values)
  • 其中第二个参数 tuple_of_parent_class 用来表示继承关系,可以为空。第三个参数用来描述要创建的 class 所应该具有的属性:
Example = type('Example', (), {})
print(Example)
<class '__main__.Example'>
  • type 所接收的第一个参数 'Example' 是该 class 的名称,同时我们使用了 Example 作为存储该 class object 引用的变量。二者可以不同,但一般不采用不同的名字从而使得代码更加复杂:
Example = type('Example', (), {'val1': 1, 'val2': 2})

# 等价于

class Example(object):
val1 = 1
val2 = 2 ##################################################### ChildExample = type('ChildExample', (Example,), {}) # 等价于 class ChildExample(Example):
pass
  • 添加的属性也可以是方法:
def echo(self):
print(self.val1) ChildExample = type('ChildExample', (Example,), {'echo': echo}) print(hasattr(Example, 'echo'))
# prints 'False' print(hasattr(ChildExample, 'echo'))
# prints 'True'
  • 可以先创建对象,再添加属性:
ChildExample = type('ChildExample', (Example,), {})

def another(self):
print('another') ChildExample.another = another
  • 综上所述,class 也是 object,可以实现动态创建。Python 实现动态创建主要通过 metaclass

3. 理解 metaclass

  • 实例调用 __class__ 属性时会指向该实例对应的 class,然后可以再去调用其它类属性
  • metaclass 是 Python 中用来创建 class object 的 class,可以看做产生的 class 的工厂类:
class = metaclass()
object = class()
  • 所有的 class 都来自于 type。type 作为 metaclass 创建了所有 class object:
age = 24
print(age.__class__)
# prints '<type 'int'>'
print(age.__class__.__class__)
# prints '<type 'type'>'
  • type 实际上是 Python 用在幕后创建所有 class 的 metaclass,type 是 Python 预先定义好的,也可以自定义 metaclass

4. class 的 __metaclass__ 属性

  • 定义 class 时,可以通过 __metaclass__ 属性初始化当前 class 的 metaclass:
class Example(object):
__metaclass__ = something
[other statements...] class Foo(Bar):
pass
  • 上述代码中,Python 首先在 Foo 中寻找是否存在 __metaclass__ 属性
  • 如果存在的话,Python 将使用这个 metaclass 在内存中创建一个名字为 Foo 的 class object
  • 如果 class 定义中不存在 __metaclass__ 且没用继承任何类,Python将会寻找 MODULE 级别的 __metaclass__
  • 如果存在的话,就进行与前述相同的操作
  • 注意:只有我们定义的 class 没有继承任何类的情况下,Python才会在 MODULE 级别寻找 __metaclass__
  • 如果存在继承类且没有定义 __metaclass__ 属性,Python 会使用当前类的父类的 metaclass 来创建当前类
  • 在 class 中定义的 __metaclass__ 属性并不会被子类继承。被子类继承的是父类的 metaclass,也就是父类的 __class__ 属性,比如上面的例子中,Bar.__class__ 将会被 Foo 继承
  • 也就是说,如果Bar定义了一个 __metaclass__ 属性来创建 Bar 的 class object,那么 Bar 的子类(也就是 Foo)并不会继承这一行为

5. 自定义 metaclass

  • 上述的 __metaclass__ 属性实际上定义的是 type 或者 type 的子类用于创建 class
  • 目的在于 class 被创建的时候对生成的 class 进行自定义修改
  • 通常用于 API 中,根据当前内容创建相匹配的 clas,比如:当前 MODULE 下的 class 的属性名称均大写
  • __metaclass__ 可以是任何 Python 的 callable,不一定是一个正式的 class 也可以是 function:
# the metaclass will automatically get passed the same argument
# that is passed to `type()`
def upper_attr(class_name, class_parents, class_attr):
'''Return a class object, with the list of its attribute turned into
uppercase.
'''
# pick up any attribute that doesn't start with '__' and turn it into uppercase.
uppercase_attr = {}
for name, val in class_attr.items():
if name.startswith('__'):
uppercase_attr[name] = val
else:
uppercase_attr[name.upper()] = val # let `type` do the class creation
return type(class_name, class_parents, uppercase_attr) class Foo(object):
# this __metaclass__ will affect the creation of this new style class
__metaclass__ = upper_attr
bar = 'bar' print(hasattr(Foo), 'bar')
# prints 'False' print(hasattr(Foo), 'BAR')
# print 'True' f = Foo()
print(f.BAR)
# print 'bar'
  • 也可以通过继承 type 的方式实现一个真正的 class 形式的 metaclass:
class UpperAttrMetaclass(type):
def __new__(cls, cls_name, bases, attr_dict):
uppercase_attr = {}
for name, val in attr_dict.items():
if name.startswith('__'):
uppercase_attr[name] = val
else:
uppercase_attr[name.upper()] = val
return super(UpperAttrMetaclass, cls).__new__(cls, cls_name, bases, uppercase_attr)

6. 应用场景

  • 通常使用 metaclass 类实现一些逻辑上复杂的操作,如修改继承以及 class 的属性等操作
  • metaclass 主要的使用情况就是用来创建 API。使用 metaclass 的一个典型的例子是 Django ORM:
class Person(models.Model):
name = models.CharField(max_length=30)
age = models.IntegerField() guy = Person(name='bob', age='')
print(guy.age)
# prints '35'
  • 上述操作并不会返回一个 IntegerField 对象,而是会返回一个 int,甚至可以直接从数据库中调用这个值
  • 正是因为 models.Model 定义了 __metaclass__,并使用了一些操作来将我们使用简单的语句定义的 Person 转化成了与数据库相应的域相联系的类,这种逻辑才成为可能
  • Django 使得很多复杂的逻辑仅暴露一个简单的 API 接口就可以调用,这正是通过 metaclass 实现的
  • metaclass 会根据需要重新实现这些复杂操作所需要的真正的代码

7. 参考文献

Python(一)对 meta class 的理解的更多相关文章

  1. python中对 函数 闭包 的理解

    最近学到 函数 闭包的时候,似懂非懂.迷迷糊糊的样子,很是头疼,今天就特意查了下关于闭包的知识,现将我自己的理解分享如下! 一.python 闭包定义 首先,关于闭包,百度百科是这样解释的: 闭包是指 ...

  2. python中self cls init的理解

    原创文章,未经允许禁止转载! python中self cls init的理解 python中self cls init的理解

  3. Python 线性回归(Linear Regression) 基本理解

    背景 学习 Linear Regression in Python – Real Python,对线性回归理论上的理解做个回顾,文章是前天读完,今天凭着记忆和理解写一遍,再回温更正. 线性回归(Lin ...

  4. python 中 深拷贝和浅拷贝的理解

    在总结 python 对象和引用的时候,想到其实 对于python的深拷贝和浅拷贝也可以很好对其的进行理解. 在python中,对象的赋值的其实就是对象的引用.也就是说,当创建一个对象,然后赋给另外一 ...

  5. meta标签的理解

    一直习惯的使用meta标签,还真么认真理解过,至少英文意思都还没弄明白... 下面是摘自网络的解释: 互动百科: 元素可提供相关页面的元信息(meta-information),比如针对搜索引擎和更新 ...

  6. python中的深拷贝和浅拷贝理解

    在python中,对象赋值实际上是对象的引用.当创建一个对象,然后把它赋给另一个变量的时候,python并没有拷贝这个对象,而只是拷贝了这个对象的引用.以下分两个思路来分别理解浅拷贝和深拷贝: 利用切 ...

  7. Python中变量和常量的理解

    一.变量的定义:把程序运算的中间结果临时存到内存里,以备后面的代码继续调用,这几个名字的学名就叫做"变量". 二.变量的作用:变量用于存储要在计算机程序中引用和操作的信息.它提供了 ...

  8. Python中if __name__ == "__main__": 的理解

    1.在很多python脚本中在最后的部分会执行一个判断语句if __name__ == "__main__:",之后还可能会有一些执行语句.那添加这个判断的目的何在? 在pytho ...

  9. python模块collections中namedtuple()的理解

    Python中存储系列数据,比较常见的数据类型有list,除此之外,还有tuple数据类型.相比与list,tuple中的元素不可修改,在映射中可以当键使用.tuple元组的item只能通过index ...

随机推荐

  1. 大数据基础总结---MapReduce和YARN技术原理

    Map Reduce和YARN技术原理 学习目标 熟悉MapReduce和YARN是什么 掌握MapReduce使用的场景及其原理 掌握MapReduce和YARN功能与架构 熟悉YARN的新特性 M ...

  2. Dubbo(一):dubbo-spring-boot-starter

    dubbo-spring-boot-starter English Dubbo Spring Boot Starter.也可以关注dubbo官方的dubbo-spring-boot-project 支 ...

  3. 设计模式之(八)组合模式(COMPOSITE)

    初始印象 在开发中存在很多整体和部分的关系,这个方式最大的体现就是树形结构.组合模式就是为了更好地解决这类业务场景的问题.先看下组合模式的定义: 将对象组合成树形结构以表示“整体—部分”的层次关系.组 ...

  4. 【开发笔记】- AbstractRoutingDataSource动态数据源切换,AOP实现动态数据源切换

    AbstractRoutingDataSource动态数据源切换 上周末,室友通宵达旦的敲代码处理他的多数据源的问题,搞的非常的紧张,也和我聊了聊天,大概的了解了他的业务的需求.一般的情况下我们都是使 ...

  5. em与rem之间的区别以及移动设备中的rem适配方案

    em与rem之间的区别: 共同点: 它们都是像素单位 它们都是相对单位 不同点: em大小是基于父元素的字体大小 rem大小是基于根元素(html)的字体的大小 实例: <!DOCTYPE ht ...

  6. mybatis中的高级查询

    Mybatis作为一个ORM框架,肯定是支持sql高级查询的. 下面的一个案例来为大家详细讲解mybatis中的高级查询. 案例说明: 此案例的业务关系是用户,订单,订单详情与商品之间的关系. 以订单 ...

  7. (原)Ubuntu连接远程服务器时connection reset by peer

    转载请注明出处: https://www.cnblogs.com/darkknightzh/p/11086935.html 最近使用ubuntu通过ssh连接服务器时,由于密码错误,多次连接失败后,在 ...

  8. JMETER 使用断言

    断言概念 断言就是在执行某个请求后,根据返回的结果,判断返回是否正确,如果不正确,则表示事务失败. 添加断言 启动流程时返回的数据是一个 json对象,结构为 {success:true,msg:&q ...

  9. Nginx 高级配置-压缩功能

    Nginx 高级配置-压缩功能 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Nginx压缩相关参数概述 1>.gzip on | off; Nginx支持对指定类型的文 ...

  10. 五、Xpath与lxml类库

    什么是XML XML 指可扩展标记语言(EXtensible Markup Language) XML 是一种标记语言,很类似 HTML XML 的设计宗旨是传输数据,而非显示数据 XML 的标签需要 ...