临时通配符导入/numpy 公式的可读性
Temporarily wildcard import / readability of numpy formulas
当使用 numpy
时,代码中有很多 np.*
,例如
import numpy as np
y = np.sin(np.abs(np.linspace(0, 2*np.pi)))
这会使公式变得混乱并降低它们的可读性。人们可以使用通配符 import
来解决这个问题
from numpy import *
y = sin(abs(linspace(0, 2*pi)))
但是,通配符导入几乎总是一个坏主意。
我想知道是否有可能将通配符导入到仅限于公式(或数学代码块)的上下文中。这将保持可读性并将命名空间污染限制在更容易控制的小代码区域。我想要这样的东西:
with import_wildcard(numpy):
y2 = sin(abs(linspace(0, 2*pi)))
问题:
- 是否有某种语言结构允许这样做。
- 请求本身是否合理,还是我忽略了一个潜在的问题?
解决方案 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 中某些函数同名的变量将无法在上下文中访问。
方案二:临时提升指定对象
根据反馈,这是另一种更明确的方法。我们只是暂时将指定的对象提升到全局命名空间,而不是进行临时通配符导入。
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)))
我也会留下第一个解决方案,这样可以更轻松地讨论每种方法的优缺点,并且人们可以为每个解决方案投票。
当使用 numpy
时,代码中有很多 np.*
,例如
import numpy as np
y = np.sin(np.abs(np.linspace(0, 2*np.pi)))
这会使公式变得混乱并降低它们的可读性。人们可以使用通配符 import
来解决这个问题from numpy import *
y = sin(abs(linspace(0, 2*pi)))
但是,通配符导入几乎总是一个坏主意。
我想知道是否有可能将通配符导入到仅限于公式(或数学代码块)的上下文中。这将保持可读性并将命名空间污染限制在更容易控制的小代码区域。我想要这样的东西:
with import_wildcard(numpy):
y2 = sin(abs(linspace(0, 2*pi)))
问题:
- 是否有某种语言结构允许这样做。
- 请求本身是否合理,还是我忽略了一个潜在的问题?
解决方案 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 中某些函数同名的变量将无法在上下文中访问。
方案二:临时提升指定对象
根据反馈,这是另一种更明确的方法。我们只是暂时将指定的对象提升到全局命名空间,而不是进行临时通配符导入。
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)))
我也会留下第一个解决方案,这样可以更轻松地讨论每种方法的优缺点,并且人们可以为每个解决方案投票。