揭开six库神秘的面纱

简介: 不得不说距离Python2.7退役的日子还剩下不到2个月的时间了,面对堆积成三的Django项目遗留代码还是少动为妙。真心要感谢six库的作者让迁移的工作减少了些困难,虽然实现的代码写的挺啰嗦的,几年前吓退了初学的我。而今天,打开揭开six库看似神秘的兼容过程。

01前言


不得不说距离Python2.7退役的日子还剩下不到2个月的时间了,面对堆积成三的Django项目遗留代码还是少动为妙。

真心要感谢six库的作者让迁移的工作减少了些困难,虽然实现的代码写的挺啰嗦的,几年前吓退了初学的我。而今天,打开揭开six库看似神秘的兼容过程。


02神秘six前传


为什么将这个库命名为six,在官方上有这样一段说明,因为要从2过渡3,其中2+3=5,而这个名字已经被zope项目的five库占了先机。而2 * 3=6,乘法看似来更强大,于是就命名为six了。

93.jpg


呵呵,我真没能领悟作者的幽默。


03神秘six正传


six库1个常见的用法类似如下:

from six.queue import Queue
from six import html_parser

很明显,我们是想导入队列Queue类及urlparse函数。

如果要手动进行兼容的话,那么就会写出类似如下一大段代码:

try:
   import HTMLParser
   from Queue import Queue
except:
   mport html.parser
   from queue import Queue

通过使用six库可以简化上述的操作。

如果你去查看了six库的源码,就会上述实现过程非常简单,完全没必要花上上千行的代码。


04six库迷雾


在six库中,有这样一段代码:

_moved_attributes = [
    MovedAttribute("cStringIO", "cStringIO", "io", "StringIO"),
    MovedModule("queue", "Queue"),
    MovedModule("html_parser","HTMLParser","html.parser")
]
for attr in _moved_attributes:
    setattr(_MovedItems, attr.name, attr)
del attr
_MovedItems._moved_attributes = _moved_attributes
moves = _MovedItems(__name__ + ".moves")

这里略去了大部分的内容,只挑重点来说说。

可以看到,我们定义了1个 _moved_attributes的变量,它是1个列表,存放着MovedModule及MovedAttribute类实例。我们遍历这个列表,之后调用setattr方法对 _MovedItems类设置其对应的属性。

而six的秘密就藏在上述不到10行的代码中。首先来看 _MovedItems类,其源代码为:

import types
class _LazyModule(types.ModuleType):
    def __init__(self, name):
        super(_LazyModule, self).__init__(name)
        self.__doc__ = self.__class__.__doc__
    def __dir__(self):
        attrs = ["__doc__", "__name__"]
        attrs += [attr.name for attr in self._moved_attributes]
        return attrs
    # Subclasses should override this
    _moved_attributes = []
class _MovedItems(_LazyModule):
    """Lazy loading of moved objects"""
    __path__ = []  # mark as package

基本没有什么干货可说,唯一有用的就是 __dir__方法中根据将其属性遍历出来。如果不存在对应的属性,则调用变量 _moved_attributes中的类。

接着来看MovedModule类,其源代码为:

import sys
PY3 = sys.version_info[0] == 3
def _import_module(name):
    __import__(name)
    return sys.modules[name]
class _LazyDescr(object):
    def __init__(self, name):
        self.name = name
    def __get__(self, obj, tp):
        result = self._resolve()
        setattr(obj, self.name, result)  # Invokes __set__.
        try:
            delattr(obj.__class__, self.name)
        except AttributeError:
            pass
        return result
class MovedModule(_LazyDescr):
    def __init__(self, name, old, new=None):
        super(MovedModule, self).__init__(name)
        if PY3:
            if new is None:
                new = name
            self.mod = new
        else:
            self.mod = old
    def _resolve(self):
        return _import_module(self.mod)
    def __getattr__(self, attr):
        _module = self._resolve()      
        value = getattr(_module, attr)    
        return value

这个类是唯一稍微有点内容可以说说的。在该类 __getattr__方法中,会调用其自身的 _resolve方法解决对应模块导入的问题。实际上还是如下的老套路:

name = '想要导入的模块'
__import__(name)
sys.modules[name]

解决了模块的导入问题外,之后调用getattr方法获取到模块对应的属性,并将其返回。


94.jpg

就是这样简单,简单的不能再简单。

下面我们再回过头对之前的代码再进行推演一遍,例如:

MovedModule("queue", "Queue")

在MovedModule类初始化时,如果是在Python3环境下则有:

new = "queue"
self.mod = new
_import_module(self.mod)

从而导入urllib.parse模块。而对于Python2的情况,则变为:

self.mod = "Queue"

从而在不同版本中成功导入对应的模块。至于MovedAttribute类,其过程与之类似,也是先导入对应的模块再获取到对应的属性。


05结语


实际上six库虽然看起来很不思议,实际上也是基础知识堆积而成,只要花点心思就能看破其手法。

最后还是那句话,写的真心够啰嗦的。

本文作者:本人笔名玉面玲珑颜如玉,1个多年滚打于Web开发的研发工程师。熟悉PHP、Java、C++等编程语言,以编程作为乐趣。

声明:本文为 脚本之家专栏作者 投稿,未经允许请勿转载。

相关文章
|
7月前
|
JavaScript 前端开发 编译器
解密Vue 3:透过原理看框架,揭开它的神秘面纱
解密Vue 3:透过原理看框架,揭开它的神秘面纱
|
4月前
|
存储 开发框架 .NET
C#语言究竟隐藏了哪些秘密?一文带你揭开编程界的神秘面纱
【8月更文挑战第22天】C#是微软推出的面向对象编程语言,以其简洁的语法和强大的功能,在软件开发领域占据重要地位。作为一种强类型语言,C#确保了代码的可读性和可维护性。它支持多种数据类型,如整型、浮点型及复合类型如类和结构体。类是核心概念,用于定义对象的属性和行为。C#还包括方法、异常处理、集合类型如列表和字典,以及泛型和LINQ等高级特性,支持异步编程以提高应用响应性。.NET Core的推出进一步增强了C#的跨平台能力。
73 3
|
2月前
|
开发框架 Java 程序员
揭开Java反射的神秘面纱:从原理到实战应用!
本文介绍了Java反射的基本概念、原理及应用场景。反射允许程序在运行时动态获取类的信息并操作其属性和方法,广泛应用于开发框架、动态代理和自定义注解等领域。通过反射,可以实现更灵活的代码设计,但也需注意其性能开销。
52 1
|
4月前
|
测试技术 开发者 Python
FastAPI的神奇之处:如何用Python引领Web开发的新浪潮,让你的项目一鸣惊人?
【8月更文挑战第31天】在现代软件开发中,Web应用至关重要,而FastAPI作为高性能Python Web框架,凭借简洁的语法与高效的开发体验,备受开发者青睐。本文将介绍FastAPI的基础概念、使用方法及最佳实践,涵盖路由、模板、请求对象等核心概念,并探讨其优势与社区扩展,助您高效构建Web应用。
127 1
|
4月前
|
测试技术 持续交付 数据安全/隐私保护
自动化测试的奥秘:揭开软件质量保证的面纱
【8月更文挑战第24天】在软件开发的海洋中,自动化测试如同一座灯塔,指引着项目安全地航行。本文将深入浅出地探讨自动化测试的重要性、实施策略以及它如何成为现代软件开发不可或缺的一部分。通过生动的例子和实用的建议,我们将一起探索自动化测试的世界,解锁其对提升软件质量和开发效率的秘密。
|
4月前
|
Rust 网络协议 安全
揭开Rust网络编程的神秘面纱:全新的Socket体验,让你告别内存泄漏的噩梦!
【8月更文挑战第31天】Rust语言凭借其卓越的内存安全性和高性能,在网络编程领域展现出独特优势。本文将带你探索Rust中的Socket编程,展示如何使用标准库`std::net`模块轻松实现TCP服务器与客户端。通过简洁的代码示例,你将看到Rust如何简化网络通信流程,并通过`async/await`异步模型高效处理并发连接。此外,Rust社区提供的优秀库如`tokio`和`async-std`进一步增强了异步网络编程的能力。无论是从基础示例还是高级应用,Rust都将为你带来耳目一新的网络编程体验。
351 0
|
5月前
|
网络协议 开发者 Python
深度探索Python Socket编程:从理论到实践,进阶篇带你领略网络编程的魅力!
【7月更文挑战第25天】在网络编程中, Python Socket编程因灵活性强而广受青睐。本文采用问答形式深入探讨其进阶技巧。**问题一**: Socket编程基于TCP/IP,通过创建Socket对象实现通信,支持客户端和服务器间的数据交换。**问题二**: 提升并发处理能力的方法包括多线程(适用于I/O密集型任务)、多进程(绕过GIL限制)和异步IO(asyncio)。**问题三**: 提供了一个使用asyncio库实现的异步Socket服务器示例,展示如何接收及响应客户端消息。通过这些内容,希望能激发读者对网络编程的兴趣并引导进一步探索。
60 4
|
编译器 C++
函数之道:探索C++函数的奥秘
函数之道:探索C++函数的奥秘
|
JavaScript 前端开发 C语言
聊一聊编程中的函数
聊一聊编程中的函数
78 0
|
算法 数据库 C语言
聊一聊编程
聊一聊编程
96 0