什么是元编程

软件开发中很重要的一条原则就是“不要重复自己的工作(Don’t repeat youself)”,也就是说当我们需要复制粘贴代码时候,通常都需要寻找一个更加优雅的解决方案,在python中,这类问题常常会归类为“元编程”

元编程目的

是创建函数和类,并用他们操作代码(例如修改,生成,或者包装自己已有的代码)。尽可能的使代码优雅简洁。具体而言,通过编程的方法,在更高的抽象层次上对一种层次的抽象的特性进行修改

元编程应用

给函数添加一个包装(装饰器)

注意:对wraps装饰器的使用进行补充说明,在类装饰器中使用闭包会导致生成的对象不再是被装饰的类的实例,而是在装饰器函数创建的子类的实例,这会影响__name__和__doc__等属性,在上篇我们使用@wraps装饰器对函数装饰器进行操作让问题得到解决,但在类装饰器中这一方法无效。

元类

在理解元类之前,您需要掌握Python中的类。Python对于从Smalltalk语言借用的类是非常奇怪的。在大多数语言中,类只是描述如何生成对象的代码片段。在Python中也是如此:

>>> class ObjectCreator(object):
...       pass
...>>> my_object = ObjectCreator()
>>> print(my_object)
<__main__.ObjectCreator object at 0x8974f2c>

一旦使用关键字class,Python就会执行它并创建一个OBJECT。指示

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> class ObjectCreator(object):
...       pass
...

在内存中创建一个名为“ObjectCreator”的对象。这个对象(类)本身能够创建对象(实例),这就是为什么它是一个类。但是,它仍然是一个对象,因此:

  • 您可以将其分配给变量
  • 你可以复制它
  • 你可以添加属性
  • 您可以将其作为函数参数传递

例如:

>>> print(ObjectCreator) # you can print a class because it's an object
<class '__main__.ObjectCreator'>
>>> def echo(o):
...       print(o)
...
>>> echo(ObjectCreator) # you can pass a class as a parameter
<class '__main__.ObjectCreator'>
>>> print(hasattr(ObjectCreator, 'new_attribute'))
False
>>> ObjectCreator.new_attribute = 'foo' # you can add attributes to a class
>>> print(hasattr(ObjectCreator, 'new_attribute'))
True
>>> print(ObjectCreator.new_attribute)
foo
>>> ObjectCreatorMirror = ObjectCreator # you can assign a class to a variable
>>> print(ObjectCreatorMirror.new_attribute)
foo
>>> print(ObjectCreatorMirror())
<__main__.ObjectCreator object at 0x8997b4c>

动态创建类

由于类是对象,因此您可以像任何对象一样动态创建它们。首先,您可以使用class以下命令在函数中创建类:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> def choose_class(name):
...     if name == 'foo':
...         class Foo(object):
...             pass
...         return Foo # return the class, not an instance
...     else:
...         class Bar(object):
...             pass
...         return Bar
...
>>> MyClass = choose_class('foo')
>>> print(MyClass) # the function returns a class, not an instance
<class '__main__.Foo'>
>>> print(MyClass()) # you can create an object from this class
<__main__.Foo object at 0x89c6d4c>

但它还不是我们想要的,创建类应该有更优的方法,由于类是对象,因此它们必须由某些东西生成。

使用class关键字时,Python会自动创建此对象。但与Python中的大多数内容一样,它为您提供了手动执行此操作的方法。我们可用通过type函数查看对象的类型:

>>> print(type(1))
<type 'int'>
>>> print(type("1"))
<type 'str'>
>>> print(type(ObjectCreator))
<type 'type'>
>>> print(type(ObjectCreator()))
<class '__main__.ObjectCreator'>

type除了可以查看数据类型外,还有一个特殊的能力,它也可以动态创建类。type可以将类的描述作为参数,并返回一个类。查看type内部原理:

type(name of the class,tuple of the parent class (for inheritance, can be empty),dictionary containing attributes names and values)

例如:

>>> class MyShinyClass(object):
...       pass

可以通过以下方式手动创建:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> MyShinyClass = type('MyShinyClass', (), {}) # returns a class object
>>> print(MyShinyClass)
<class '__main__.MyShinyClass'>
>>> print(MyShinyClass()) # create an instance with the class
<__main__.MyShinyClass object at 0x8997cec>

type接受字典来定义类的属性。所以:

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

可以翻译成:

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

并用作普通类:

>>> print(Foo)
<class '__main__.Foo'>
>>> print(Foo.bar)
True
>>> f = Foo()
>>> print(f)
<__main__.Foo object at 0x8a9b84c>
>>> print(f.bar)
True 

当然,你可以继承它,所以:

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>>   class FooChild(Foo):
...         pass

会解释成:

>>> FooChild = type('FooChild', (Foo,), {})
>>> print(FooChild)
<class '__main__.FooChild'>
>>> print(FooChild.bar) # bar is inherited from Foo
True  

最后,如果想要为我们创建的类添加方法,只需使用正确的签名定义函数并将其指定为属性即可。

会解释成:

>>> def echo_bar(self):
...       print(self.bar)
...
>>> FooChild = type('FooChild', (Foo,), {'echo_bar': echo_bar})
>>> hasattr(Foo, 'echo_bar')
False
>>> hasattr(FooChild, 'echo_bar')
True
>>> my_foo = FooChild()
>>> my_foo.echo_bar()
True

在动态创建类之后,您可以添加更多方法,就像向正常创建的类对象添加方法一样。

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> def echo_bar_more(self):
...       print('yet another method')
...
>>> FooChild.echo_bar_more = echo_bar_more
>>> hasattr(FooChild, 'echo_bar_more')
True

在Python中,类是对象,您可以动态地动态创建类。这是Python在您使用关键字时所执行的操作class,它通过使用元类来实现。

什么是元类(终于讲到重点了)

元类是创建类的“类”。我们可以定义类来创建实例,python一切皆对象,类也不列外,它是通过元类来创建。类是创建实例的蓝图,元类是创建类的蓝图。可以很容易地看出,Python类中也需要是第一类对象才能启用此行为。

例如:

MyClass = MetaClass()
my_object = MyClass()

通过type来创建:

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

这是因为该函数type实际上是一个元类。type是Python用于在幕后创建所有类的元类。

为什么是小写type而不是大学Type?

type与str创建字符串对象int的类创建整数对象的类类似,它也只是创建类对象的类。我们通过检查__class__属性来查看。

一切,一切,一切重要的事情说三遍,都是Python中的一个对象。这包括整数,字符串,函数和类。所有这些都是对象。所有这些都是从一个类创建的:

>>> age = 35
>>> age.__class__
<type 'int'>
>>> name = 'bob'
>>> name.__class__
<type 'str'>
>>> def foo(): pass
>>> foo.__class__
<type 'function'>
>>> class Bar(object): pass
>>> b = Bar()
>>> b.__class__
<class '__main__.Bar'>

那么__class__的__class__?

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
>>> age.__class__.__class__
<type 'type'>
>>> name.__class__.__class__
<type 'type'>
>>> foo.__class__.__class__
<type 'type'>
>>> b.__class__.__class__
<type 'type'>

因此,元类只是创建类对象的东西。我们也称它为类工厂

type 是Python使用的内置元类,我们也可以创建自己的元类。

__mataClass__属性

在Python 2中,我们在编写类时添加属性:

class Foo(object):__metaclass__ = something...[...]

如果引用__mataClass__属性,python将使用元类类创建Foo,但是这样class Foo(object),类对象Foo并不是在内存中创建的

Python将会在父类中查找__metaclass__,如果没有,就继续向父类的父类查找,如果还是没有,就在模块中找,还是没有的话就用缺省的MetaClass即type创建类。

当你这样做时:

class Foo(Bar):pass

Python会执行以下操作:

如果有__metaclass__属性,将会在内存中创建一个类对象,名称Foo使用是__metaclass__。如果Python找不到__metaclass__,它将__metaclass__在MODULE级别查找,并尝试执行相同的操作(但仅适用于不继承任何内容的类,基本上是旧式类)。

如果还是找不到__metaclass__,它将使用Bar’s(第一个父级)自己的元类(可能是默认的type)来创建类对象。

Python中的元类3

在Python 3中更改了设置元类的语法:

class Foo(object, metaclass=something):...

即__metaclass__不再使用该属性,而是支持基类列表中的关键字参数。但是并不会影响元类的功能。

python3中我们可以将属性作为关键字参数传递给元类,如下所示:

class Foo(object, metaclass=something, kwarg1=value1, kwarg2=value2):...

自定义元类

一个类没有声明自己的元类,默认他的元类就是type,除了使用元类type,用户也可以通过继承type来自定义元类

自定义元类的主要目的是:

  • 拦截类的创建
  • 读取类的信息,可以做修改
  • 返回新的类

通过传入不同的字符串动态的创建不同的类

def create_class(name):if name == 'user':class User:def __str__(self):return "user"return Userelif name == "company":class Company:def __str__(self):return "company"return Companyif __name__ == '__main__':Myclass = create_class("user")my_obj = Myclass()print(my_obj)    #userprint(type(my_obj))     #<class '__main__.create_class.<locals>.User'>

用type创建

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
# 一个简单type创建类的例子
#type(object_or_name, bases, dict)
#type里面有三个参数,第一个类名,第二个基类名,第三个是属性
User = type("User",(),{"name":"derek"})my_obj = User()
print(my_obj.name)    #derek#带方法的创建<br><br>def say(self):     #必须加selfreturn "i am derek"User = type("User",(),{"name":"derek","say":say})my_obj = User()
print(my_obj.name)     #derek
print(my_obj.say())    #i am derek

让type创建的类继承一个基类

def say(self):     #必须加selfreturn "i am derek"class BaseClass:def answer(self):return "i am baseclass"#type里面有三个参数,第一个类名,第二个基类名,第三个是属性
User = type("User",(BaseClass,),{"name":"derek","say":say})if __name__ == '__main__':my_obj = User()print(my_obj.name)          #d erekprint(my_obj.say())         # i am derekprint(my_obj.answer())      # i am baseclass

但是在实际编码中,我们一般不直接用type去创建类,而是用元类的写法,自定义一个元类metaclass去创建

'''
遇到问题没人解答?小编创建了一个Python学习交流QQ群:579817333
寻找有志同道合的小伙伴,互帮互助,群里还有不错的视频学习教程和PDF电子书!
'''
# 把User类创建的过程委托给元类去做,这样代码的分离性比较好class MetaClass(type):def __new__(cls, *args, **kwargs):return super().__new__(cls,*args, **kwargs)class User(metaclass=MetaClass):def __init__(self,name):self.name = namedef __str__(self):return "test"if __name__ == '__main__':#python中类的实例化过程,会首先寻找metaclass,通过metaclass去创建User类my_obj = User(name="derek")print(my_obj)    #test

还有一个典型的自定义元类例子就是Django ORM。
元类的主要用例是创建API。

它允许您定义如下内容:

class Person(models.Model):name = models.CharField(max_length=30)age = models.IntegerField()

但是如果你这样做:

guy = Person(name='bob', age='35')
print(guy.age)

它不会返回一个IntegerField对象。它将返回一个int,甚至可以直接从数据库中获取它。

因为models.Model定义__metaclass__它会使用一些魔法将Person您刚刚使用简单语句定义的内容转换为数据库字段的复杂sql。

Django通过公开一个简单的API并使用元类,从这个API中重新创建代码来完成幕后的实际工作,从而使复杂的外观变得简单。

python元编程详解相关推荐

  1. Python元类详解

    文章目录 Python元类详解 Python谜团 元类的本质 调用一个类时发生了什么 再探元类 自定义元类 彩蛋:跳过python解释器 Python元类详解 元类比99%的用户所担心的魔法要更深,如 ...

  2. python 元类 详解_Python 元类详解 __new__、__init__、__call__、__metacalss__

    了解元类之前,先了解几个魔术方法: __new__.__init__.__call__ __new__: 对象的创建,是一个静态方法,第一个参数是cls.(想想也是,不可能是self,对象还没创建,哪 ...

  3. Python异步编程详解

    一.异步编程相关概念 1.I/O模型 IO操作实际过程涉及到内核和调用这个IO操作的进程.对于一次IO访问(以read举例),数据会先被拷贝到操作系统内核的缓冲区中,然后才会从操作系统内核的缓冲区拷贝 ...

  4. python2异步编程_最新Python异步编程详解

    我们都知道对于I/O相关的程序来说,异步编程可以大幅度的提高系统的吞吐量,因为在某个I/O操作的读写过程中,系统可以先去处理其它的操作(通常是其它的I/O操作),那么Python中是如何实现异步编程的 ...

  5. 最新Python异步编程详解

    我们都知道对于I/O相关的程序来说,异步编程可以大幅度的提高系统的吞吐量,因为在某个I/O操作的读写过程中,系统可以先去处理其它的操作(通常是其它的I/O操作),那么Python中是如何实现异步编程的 ...

  6. python元编程_python元编程详解(3)

    今天转载一片非常精彩的文章供大家欣赏:参考文章链接. python开发者门户一个很好的学习python的网站,大家有时间可以多看看. 下面正式开始今天的内容: 在理解元类之前,你需要先掌握Python ...

  7. Python多线程编程详解,文章比较长,需耐心浏览

    文章目录 前言 创建多线程 守护线程 线程实例 零基础Python学习资源介绍 一.Python所有方向的学习路线 二.Python学习软件 三.Python入门学习视频 四.Python练习题 五. ...

  8. linux很多python进程,Python多进程编程详解

    序. multiprocessing Python中的多线程其实并不是真正的多线程,如果想要充分地使用多核CPU的资源,在Python中大部分情况需要使用多进程.Python提供了非常好用的多进程包m ...

  9. Python IO编程详解

    一.文件系统操作 1.os.os.path和pathlib的对比 Python中处理文件路径和文件系统操作的传统方式,是通过os和os.path模块中的函数来完成的.这些函数完全能够胜任需求,但往往会 ...

最新文章

  1. English Spoken Math
  2. ASP.NET页面动态添加js脚本
  3. 【论文解读】CVPR 2021 | 旷视提出GID:用于目标检测的通用实例蒸馏
  4. 如何形成统一设计风格-实践篇
  5. 生产是什么意思_俗话说:“牛遇无春年,出力好耕田”是啥意思?有什么道理?...
  6. 关于get请求的乱码问题
  7. Hbase如何实现增删查改?
  8. Oracle-数据库
  9. 我的世界java版地牢种子_《我的世界》12个地牢种子位置解析
  10. KnockOutJS(一)
  11. Matlab信号处理,小波降噪
  12. SQL中的随机函数-笔记
  13. css相对图片加文字,html+css怎么在图片上添加文字
  14. 用Photoshop制作电子签
  15. 如何使公式和编号上下对齐?
  16. Libgdx之国际化 中英文菜单切换
  17. Oracle的常见错误及解决办法
  18. 盘一盘 QuantLib 系列 4 - CDS/CDX/iTraxx/中国 CRM 和 CDS
  19. 《菊次郎的夏天》---温情的北野武,诗意的久石让
  20. 王选院士谈院士增选:院士未必总是学术权威

热门文章

  1. Springmvc的helloworld实例
  2. 改善EF代码的方法(下)
  3. iphone ios 如何开发升级适配 iphone5 4inch
  4. 基于corosync和NFS服务器实现LNMP的高可用
  5. [转]默认构造函数的作用
  6. android怎么关联geny,Android studio下添加genymotion神器
  7. 释疑のSmartforms点打印无反应
  8. SD-销售订单中装运点确认
  9. 伟大:看谷歌如何造福人类健康事业
  10. ABAP程序设计的一点建议