numpy公式的临时通配符导入/可读性

时间:2017-03-17 15:56:58

标签: python import contextmanager

使用numpy时代码中有很多np.*,例如

import numpy as np
y = np.sin(np.abs(np.linspace(0, 2*np.pi)))

这会使公式变得混乱,使它们的可读性降低。可以使用通配符导入来解决这个问题

from numpy import *
y = sin(abs(linspace(0, 2*pi)))

但是,通配符导入几乎总是一个坏主意。

我想知道是否有可能将通配符导入到仅限于公式(或数学代码块)的上下文中。这将保持可读性并将命名空间污染限制在可以更容易控制的小代码区域。我想要这样的东西:

with import_wildcard(numpy):
    y2 = sin(abs(linspace(0, 2*pi)))

问题:

  1. 是否有一些语言结构允许这样做。
  2. 请求本身是合理的还是我忽略了潜在的问题?

2 个答案:

答案 0 :(得分:2)

解决方案1:临时通配符导入:

class import_wildcard(object):
    """Contextmanager to temporary import a package content into the global namespace."""
    def __init__(self, packagename):
        self.packagename = packagename
        self.package = __import__(self.packagename, globals(), locals())
        self.globals_backup = {}

    def __enter__(self):
        _globals = globals()
        for name in self.package.__dict__:
            if name in _globals:
                self.globals_backup[name] = _globals[name]
        _globals.update(self.package.__dict__)

    def __exit__(self, exc_type, exc_value, exc_tb):
        _globals = globals()
        for name in self.package.__dict__:
            if name not in self.globals_backup:
                del _globals[name]
        _globals.update(self.globals_backup)
        self.globals_backup.clear()


with import_wildcard('numpy'):
    y = sin(abs(linspace(0, 2*pi)))

到目前为止,我没有遇到过明显的缺点。除了当然在上下文中定义的变量与numpy中的某个函数同名的变量将无法在上下文中访问。

答案 1 :(得分:1)

解决方案2:暂时提升指定对象

根据反馈,这是另一种更明确的方法。我们只是暂时将指定对象提升为全局命名空间,而不是进行临时通配符导入。

class global_context(object):
    def __init__(self, *objects):
        """Return a context manager that has the given objects available in the global namespace.

        You can directly pass in an object if it has a __name__, otherwise use the string name.
        """
        def parse_object(obj):
            if isinstance(obj, str):
                ns, name = obj.split('.')
                return name, getattr(globals()[ns], name)
            else:
                return obj.__name__, obj
        self.identifiers = dict(parse_object(o) for o in objects)
        self.globals_backup = {}

    def __enter__(self):
        _globals = globals()
        for name, fn in self.identifiers.items():
            if name in _globals:
                self.globals_backup[name] = _globals[name]
        _globals.update(self.identifiers)

    def __exit__(self, exc_type, exc_value, exc_tb):
        _globals = globals()
        for name in self.identifiers:
            if name not in self.globals_backup:
                del _globals[name]
        _globals.update(self.globals_backup)
        self.globals_backup.clear()

用法:

import numpy as np
with global_context(np.sin, np.abs, np.linspace, 'np.pi'):
    y = sin(abs(linspace(0, 2*pi)))

我将保留第一个解决方案,以便更容易讨论每种方法的优缺点,人们可以为每个解决方案投票。