返回顶部
首页 > 资讯 > 后端开发 > Python >Python中的元编程
  • 418
分享到

Python中的元编程

Python 2023-01-31 02:01:26 418人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

就像元数据是关于数据的数据一样,元编程是编写程序来操作程序(Just like metadata is data about data, metaprogramming is writing programs that manipulate

就像元数据是关于数据的数据一样,元编程是编写程序来操作程序(Just like metadata is data about data, metaprogramming is writing programs that manipulate programs)。一个常见的看法是元编程是用来成成其他程序的程序,但是实际上它的含义更为广泛(It's a common perception that metaprograms are the programs that generate other programs. But the paradigm is even broader)。所有用于读取、分析、转换或修改自身的程序都是元编程的例子。比如:

  • Domain-specific languages (DSLs)
  • Parsers
  • Interpreters
  • Compilers
  • Theorem provers
  • Term rewriters

这篇教程介绍python中的元编程,它通过对Python特性的回顾来更新您的Python知识,这样您就可以更好地理解本文中的概念。本文也解释了Python中的type函数除了返回一个对象(上层的)的类之外是如何拥有更重要的意义的。然后,讨论了在Python中元编程的方法以及元编程如何简化某些特定类型的任务。

一点自我反省

如果你已经由一些Python编程经历,你可能知道那句话:Python中一切皆对象,类创建对象。但是如果一切皆对象(则类也是对象),那么是谁创建了类呢?这正是我要回答的问题。

我们来验证一下前面的说法是否正确

>>> class SomeClass:
...     pass
>>> some_object = SomeClass()
>>> type(some_obj)
<__main__.SomeClass instance at 0x7f8de4432f80>

可见,type()函数作用于一个对象时,返回这个对象的类(即该对象由哪个类创建)

>>> import inspect
>>>inspect.isclass(SomeClass)
True
>>>inspect.isclass(some_object)
False
>>>inspect.isclass(type(some_object))
True

inspect.isclass函数返回True如果传给它一个类,对于其他类型返回False。因为some_object不是类(它是类的一个实例),所以 inspect.isclass() 返回False。而type(some_object)返回了创建 some_object 的类,因此inspect.isclass(type(some_object))返回True:

>>> type(SomeClass)
<type 'classobj'>>>>
inspect.isclass(type(SomeClass))
True

classobj是一个特殊的类,在python3中所有的类都默认继承自它。现在一切变得有道理了,但是 classobj 呢,对它调用type()又会如何呢?

>>> type(type(SomeClass))
<type 'type'>
>>>inspect.isclass(type(type(SomeClass)))
True
>>>type(type(type(SomeClass)))
<type 'type'>
>>>inspect.isclass(type(type(type(SomeClass))))
True

有点意思是么?再来看那个关于Python的名言(一切皆对象)好像并不是那么精确,这样说可能会更好:
Python中除了type以外一切皆对象,他们要么是类的对象,要么是元类的对象。

来验证这个观点:

>>> some_obj = SomeClass()
>>> isinstance(some_obj,SomeClass)
True
>>> isinstance(SomeClass, type)
True

因此我们可以知道实例是一个类的实例化,而类是一个元类的实例化。

type并不是我们以为的那样

type 本身就是一个类,并且它是他自己的 type,它是一个元类。元类可以实例化为类并且定义类的行为,就像类可以实例化为对象并且定义对象的行为一样。

type 是 Python 中一个内建的元类,来控制Python中类的行为,我们可以通过继承自 type 来自定义一个元类。元类是Python中进行元编程的途径。

定义一个类时发生了什么

让我们先复习一下我们已知的知识,在Python中构成代码的基本单元有:

  • Statements
  • Functions
  • Classes

在代码中由 Statements 来完成实际的工作,Statements 可以在全局范围(module level)或是本地范围(within a function)。函数是包含一条或多条语句,用来执行特定任务的,可复用的代码单元。函数同样可以定义在全局范围或本地范围,也可以作为类的方法。类提供了“面向对象编程”的能力,类定义了对象如何被实例化以及他们实例化后将会拥有的属性和方法。

类的命名空间存储于字典中,例如

>>> class SomeClass:
...     class_var = 1
...     def __init__(self):
...         self.some_var = 'Some value'

>>> SomeClass.__dict__
{'__doc__': None,
 '__init__': <function __main__.__init__>,
 '__module__': '__main__',
 'class_var': 1}

>>> s = SomeClass()

>>> s.__dict__
{'some_var': 'Some value'}

下面详细介绍下当遇到class关键字时,会发生什么:

  • 类的主体(语句和函数)被隔离(The body (statements and functions) of the class is isolated.)
  • 类的命名空间字典被创建(但是还未向字典中添加键值对)
  • 类中的代码开始执行,然后代码中定义的所有属性和方法以及一些其他信息(如'__doc__')被添加到命名空间字典中
  • 将要被创建的这个类的元类被识别(这里是简译了,请看原句)(The metaclass is identified in the base classes or the metaclass hooks (explained later) of the class to be created)
  • The metaclass is then called with the name, bases, and attributes of the class to instantiate(实例化) it

由于 type 是Python中默认的元类,所以你可以用 type 去创建类。

type的另一面

type(),当只跟一个参数时,产生现有类的类型信息(produces the type infORMation of an existing class)。当 type() 跟三个参数时,它创建一个新的类对象(type called with three arguments creates a new class object)。三个参数分别是:要创建的类的名称,一个包含基类(父类)的列表,和一个表示类命名空间的字典。

因此

class SomeClass: pass

等价于

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

并且

class ParentClass:
    pass

class SomeClass(ParentClass):
    some_var = 5
    def some_function(self):
        print("Hello!")

等价于

def some_function(self):
    print("Hello")
ParentClass = type('ParentClass', (), {})
SomeClass = type('SomeClass',
                 [ParentClass],
                 {'some_function': some_function,
                  'some_var':5})

因此,通过我们自定义的元类而不是 type,我们可以给类注入一些行为(we can inject some behavior to the classes that wouldn't have been possible)。但是,在我们实现通过元类注入行为之前,让我们来看看Python中更常见的实现元编程的方法。

装饰器(Decorators):Python中元编程的一个常见示例

装饰器是一种修改函数行为或者类行为的方法。装饰器的使用看起来大概是这个样子:

@some_decorator
def some_func(*args, **kwargs):
    pass

@some_decorator只是一种语法糖,表示函数some_func被另一个函数some_decorator封装起来。我们知道函数和类(除了 type 这个元类)在Python中都是对象,这意味着它们可以:

  • 分配给一个变量(Assigned to a variable)
  • 复制(copied)
  • 作为参数传递给另一个函数(Passed as parameters to other functions)

上面的写法等同于

some_func = some_decorator(some_func)

你可能会想知道 some_decorator 是如何定义的

def some_decorator(f):
    """
    The decorator receives function as a parameter.
    """
    def wrapper(*args, **kwargs):
        # doing something before calling the function
        f(*args, **kwargs)
        # doing something after the function is called
    return wrapper

现在假设我们有一个从URL抓取数据的函数。被抓取服务器上有限流机制当它检测到同一个IP地址发来过多的请求并且请求间隔都一样时,会限制当前IP的请求。为了让我们的抓取程序表现的更随机一些,我们会让程序在每次请求之后暂定一小段随机时间来“欺骗”被抓取服务器。这个需求我们能通过装饰器来实现么?看代码

from functools import wraps
import random
import time

def wait_random(min_wait=1, max_wait=5):
    def inner_function(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            time.sleep(random.randint(min_wait, max_wait))
            return func(*args, **kwargs)
        return wrapper
    return inner_function

@wait_random(10, 15)
def function_to_scrape():
    # some scraping stuff

其中 inner_function 和 @wraps 装饰器可能对你来说还比较新。如果你仔细看,inner_function 和我们上文中定义的 some_decorator 类似。之所以用了三层def关键字,是因为装饰器wait_random要接受参数(min_wait和max_wait)。@wraps是个很好用的装饰器,他保存原函数(这里是func)的元数据(例如name, doc string, and function attributes)。如果我们没有用 @wraps,当我们对装饰之后的函数调用 help() 时 将不能得到有用的(期望的)结果,它将返回 wrapper 函数的 docstring,而不是 func 函数的(正常我们期望是func的)。

但是如果你有一个爬虫类包含多个类似的函数呢:

class Scraper:
    def func_to_scrape_1(self):
        # some scraping stuff
        pass
    def func_to_scrape_2(self):
        # some scraping stuff
        pass
    def func_to_scrape_3(self):
        # some scraping stuff
        pass

一种方案是对每个方法前都用 @wait_random 进行装饰。但是我们可以做的更优雅:我们可以创建一个“类装饰器”。思路是遍历类的名称空间,识别出函数,然后用我们的装饰器进行封装

def classwrapper(cls):
    for name, val in vars(cls).items():
        # `callable` return `True` if the argument is callable
        # i.e. implements the `__call`
        if callable(val):
            # instead of val, wrap it with our decorator.
            setattr(cls, name, wait_random()(val))
    return cls

现在我们可以用 @classwrapper 来封装整个Scraper类。但是再进一步,如果我们有很多和Scraper相似的类呢?当然你可以分别对每个类用 @classwrapper 进行装饰,但是也可以更优雅:创建一个元类。

元类(Metaclasses)

编写一个元类包含两步:

  1. 创建一个子类继承自元类 type(Write a subclass of the metaclass type)
  2. 通过“元类钩子”将新的元类插入到类创建过程(Insert the new metaclass into the class creation process using the metaclass hook)

我们创建 type 元类的子类,修改一些魔术方法,像__init____new____prepare__以及__call__以实现在创建类的过程中修改类的行为。这些方法包含了像父类,类名,属性等信息。Python2中,元类钩子(metaclass hook)是类中一个名为__metaclass__的静态属性(the metaclass hook is a static field in the class called metaclass)。Python3中, 你可以在类的基类列表中指定元类作为元类参数(you can specify the metaclass as a metaclass argument in the base-class list of a class)。

>>> class CustomMetaClass(type):
...     def __init__(cls, name, bases, attrs):  
...         for name, value in attrs.items():
                # do some stuff
...             print('{} :{}'.format(name, value))
>>> class SomeClass(metaclass=CustomMetaClass):
...     class_attribute = "Some string"

__module__ :__main__
__metaclass__ :<class '__main__.CustomMetaClass'>
class_attribute :Some string

属性被自动打印出来由于 CustomMetaClass 中的 __init__方法。我们来假设一下在你的Python项目中有一位“烦人”的伙伴习惯用 camelCase(驼峰法)方式来命名类中的属性和方法。你知道这不是一条好的实践,应该用 snake_case(即下划线方式)方式。那么我们可以编写一个元类来讲所有驼峰法的属性名称和方法名称修改为下划线方式吗?

def camel_to_snake(name):
    """
    A function that converts camelCase to snake_case.
    Referred from: https://stackoverflow.com/questions/1175208/elegant-python-function-to-convert-camelcase-to-snake-case
    """
    import re
    s1 = re.sub('(.)([A-Z][a-z]+)', r'\1_\2', name)
    return re.sub('([a-z0-9])([A-Z])', r'\1_\2', s1).lower()

class SnakeCaseMetaclass(type):
    def __new__(snakecase_metaclass, future_class_name,
                future_class_parents, future_class_attr):
        snakecase_attrs = {}
        for name, val in future_class_attr.items():
            snakecase_attrs[camel_to_snake(name)] = val
        return type(future_class_name, future_class_parents,
                    snakecase_attrs)

你可能已经注意到这里用了__new__方法而不是__init__。实际上 __new是创建一个实例过程的第一步,它负责返回由类实例化而来的实例。另一方面, \init并不返回任何东西,它仅仅负责在实例创建之后对实例进行各种初始化。记住一个简单的法则:**当你需要控制一个实例的创建过程时用`new;当你需要对一个新创建的实例进行初始化时用init__`**。

一般在实现元类的时候不用 __init,因为他“不够强大”:在实际调用 \init之前类的创建过程已经完成。你可以理解`init`就像一个类装饰器,但不同的是 \init__在创建子类的时候会被调用,而装饰器则不会。

由于我们的任务包含创建一个新的实例(防止这些驼峰法的属性名称潜入到类中),重写我自定义元类 SnakeCaseMetaClass 中的 __new__方法。让我们来检查一下这是否按预期工作了:

>>> class SomeClass(metaclass=SnakeCaseMetaclass):
...     camelCaseVar = 5
>>> SomeClass.camelCaseVar
AttributeError: type object 'SomeClass' has no attribute 'camelCaseVar'
>>> SomeClass.camel_case_var
5

结果是预期的。现在你知道了Python中如何编写元类。

总结

在这篇文章中,介绍了Python中实例元类的关系。也展示了元编程的知识,这是一种操作代码的方法。我们还讨论了装饰器类装饰器用来对类和方法(函数)注入一些额外的行为。然后我们展示了如何通过继承默认的元类type来创建自定义的元类。最后我们展示了一些用到元类的场景。关于是否使用元类,在网上也有比较大的争议。但是通过本文我们应该能分析什么类型的问题用元编程来解决可能会更好。

由于本人能力有限,若有有不精准或模糊的地方,请见原文链接。

--结束END--

本文标题: Python中的元编程

本文链接: https://www.lsjlt.com/news/185997.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Python中的元编程
    就像元数据是关于数据的数据一样,元编程是编写程序来操作程序(Just like metadata is data about data, metaprogramming is writing programs that manipulate...
    99+
    2023-01-31
    Python
  • Python中的元编程怎么应用
    这篇“Python中的元编程怎么应用”文章的知识点大部分人都不太理解,所以小编给大家总结了以下内容,内容详细,步骤清晰,具有一定的借鉴价值,希望大家阅读完这篇文章能有所收获,下面我们一起来看看这篇“Python中的元编程怎么应用”文章吧。什...
    99+
    2023-07-05
  • 浅谈Python的元编程
    目录一、装饰器二、装饰器的执行顺序三、元类四、descriptor 类(描述符类)五、总结 相应的元编程就是描述代码本身的代码,元编程就是关于创建操作源代码(比如修改、生成或包装原来...
    99+
    2024-04-02
  • OpenERP与Python 元编程
    Python元编程被称为“黑魔法”。Python界的传奇人物Tim Peters有云: 引用 Python的元编程这种黑魔法99%的人都无需了解,如果你拿不准是否应该用到它时,你不需要它. OpenERP基本遵循了Tim Peters的教诲...
    99+
    2023-01-31
    OpenERP Python
  • 如何使用Python中的元编程技巧
    如何使用Python中的元编程技巧导语:元编程是一种编程范式,指的是在运行时创建或修改代码的能力。Python作为一门动态语言,具备强大的元编程能力。本文将介绍Python中常用的元编程技巧,并给出具体的代码示例。一、使用元类元类是用于创建...
    99+
    2023-10-22
    反射(Reflection) 元类(Metaclass) 装饰器(Decorator)
  • python元编程指的是什么
    Python元编程是指在Python中编写能够操作、创建和修改Python代码的代码。它允许程序在运行时动态地创建和修改代码,以便实...
    99+
    2023-10-25
    python
  • python元类编程的基本使用
    目录1.1.propety动态属性1.2.__getattr__和__getattribute__的区别1.3.属性描述符1.4.__new__和__init__的区别1.5.自定义...
    99+
    2023-02-22
    python元类编程 python元类
  • Python元编程:赋予你编程超能力的工具
    Python元编程是一种强大的技术,它允许你对Python语言本身进行操作,赋予你编程超能力。元编程可以通过使用元类和装饰器来实现。元类是一种特殊的类,它负责创建其他类。装饰器是一种函数,它可以修改另一个函数的行为。 元编程的一个常见用途...
    99+
    2024-02-14
    Python 元编程 元类 装饰器 类生成 动态语言 编程技巧
  • Python元编程:开启极客编程的颠覆之旅
    Python 元编程:无限可能与极致优雅的结合 进入 Python 元编程的殿堂,你将踏上一次颠覆传统编程观念的奇妙旅程。元编程,又称元编程或元元编程,是一种强大的技术,允许开发人员以一种前所未有的方式操纵和修改 Python 代码。它本...
    99+
    2024-02-14
    Python 元编程 元类 动态编程 反射 代码生成 装饰器
  • python元类编程如何使用
    本文小编为大家详细介绍“python元类编程如何使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“python元类编程如何使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.1.propety动态属性在面向对...
    99+
    2023-07-05
  • Python编程Day6——元组类型、字
    1、用途:记录多个值,当多个值没有改变的需求此时元组更为合适 2、定义:在()内用逗号分隔开多个任意类型的值(参数为for可以循环的对象) 3、常用操作: 索引(正取向+反取向)且只能取值不能改变 t=('egon',123,['a'...
    99+
    2023-01-31
    类型 Python
  • Python元编程技术一览表
    元编程是Python中一种非常强大的技术,它允许程序员在运行时修改类和函数的定义。这可以用于实现许多有趣的特性,如动态生成代码、动态修改代码、动态生成类、动态修改类等。 1. 元类 元类是Python中用于创建类的类。当我们创建一个类时...
    99+
    2024-02-14
    Python 元编程 元类 动态生成 动态修改
  • 掌握Python元编程,成就编码大师
    文章 什么是元编程? 在学习元编程之前,我们需要先了解两个重要的概念:类和元类。类是一个蓝图,它可以用来创建对象。而元类则是类的蓝图,它可以用来创建类。你可以把元类理解成一个工厂,它可以生产出各种各样的类。 元编程的优势: 更加强大的...
    99+
    2024-02-14
    : Python 元编程 class metaclass type
  • 使用Python怎么实现元编程
    本篇文章为大家展示了使用Python怎么实现元编程,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。一、前言首先说,Python中一切皆对象,老生常谈。还有,Python提供了许多特殊方法、元类等等这样...
    99+
    2023-06-15
  • 换个角度理解python元编程
    元编程这个概念本身不新,只是没有发现原来很早就在用这个东西,所以python等下再聊,先理一理怎么理解编程这个事情。我仔细思考,其实是在做一件设计想法,纪录想法,实现想法的过程。怎么样设计想法?应该需要一些图形,文字通过一定格式纪录下来,反...
    99+
    2023-01-31
    换个 角度 python
  • Python元类编程实现一个简单的ORM
    目录概述效果步骤结束语完整代码概述 什么是ORM    ORM全称“Object Relational Mapping”,即对象-关系映射,就是把关系数据库的...
    99+
    2023-03-06
    Python元类编程ORM Python ORM
  • Python基础之元编程知识总结
    目录一、前言二、ImportTime vs RunTime三、元类四、装饰器五、对数据的抽象–描述符六、控制子类的创建——代替元类的方法一、前言 首先说,Python中一切皆对象,老...
    99+
    2024-04-02
  • 超越Python边界:元编程的力量与优雅
    元编程是Python中一项功能强大的技术,它允许程序员在运行时动态地修改和扩展程序的行为。它提供了一种创建代码的代码的机制,从而可以生成定制和通用的应用程序。 元编程的主要优势之一是其灵活性。它允许程序员创建自定义的数据结构和算法,并轻...
    99+
    2024-02-14
    元编程 运行时 动态修改 定制 通用应用程序
  • 翻转Python魔术帽:理解元编程的哲学
    Python元编程,也就是所谓的“代码操控代码”,是一种强大的编程范例,它允许程序员在运行时创建和修改代码。这一特性,使Python能够比许多其他编程语言更加灵活和动态,也使得Python进入到了一个具有更高层级的抽象境界。 元编程可以...
    99+
    2024-02-14
    Python 元编程 代码生成 动态类
  • python 编程中的__doc__的使
            今天给大家介绍个我们在编程中需要的一个小玩意,我们在运行程序的时候,经常需要是否这个脚本是否需要输入各种参数等等,才能正常运行等,今天就给大家介绍个全局的变量的使用        下面看下第一种写法: 1: [r...
    99+
    2023-01-31
    python
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作