有没有一种方法可以在Python中进入类命名空间?

3

我发现自己正在编写以下代码:

def dlt(translation):
    del translation.strands[translation.active][translation.locus]

我更喜欢这样的东西:

def dlt(translation):
    *something*(translation):
        del strands[active][locus]

有没有一种方法可以实现这个?

2个回答

4

命名空间只是Python对象,您可以将对象(包括属性查找结果)分配给本地变量名称:

strands = translation.strands
active = translation.active
locus = translation.locus

或者,您需要编写一个上下文管理器来修改locals(),如this answer所示。

以下代码可以实现这个功能:

import inspect

class Namespace(object):
    def __init__(self, namespaced):
        self.namespaced = namespaced

    def __enter__(self):
        """store the pre-contextmanager scope"""
        ns = globals()
        namespaced = self.namespaced.__dict__
        # keep track of what we add and what we replace
        self.scope_added = namespaced.keys()
        self.scope_before = {k: v for k, v in ns.items() if k in self.scope_added}
        globals().update(namespaced)
        return self

    def __exit__(self):
        ns = globals()
        # remove what we added, then reinstate what we replaced
        for name in self.scope_added:
            if name in ns:
                del ns[name]
        ns.update(self.scope_before)

然后像这样使用:

with Namespace(translation):
     del strands[active][locus]

while块中,translation.__dict__中的所有项目都可以全局使用。

请注意,这不是线程安全的,并且可能会给未来尝试阅读使用此代码的人(包括您自己)带来很多困惑。个人而言,我不会使用这个。


谢谢。实际上我想要的是一种不需要手写所有作业的解决方案,所以我可能会尝试你建议的方法。 - Michael Pankov
@Constantius:我已经更新了我的答案,并提供了一个应该适用于您的那个黑客版本。不过这只是一个黑客方法。 - Martijn Pieters
上下文管理器是一个巧妙的技巧!我认为在 __exit__ 中,你应该迭代 ns 而不是对象命名空间,因为后者可能已经在此期间发生了更改。显然,这样做需要一些小心谨慎。 - Jamey Sharp
@JameySharp:globals() 命名空间也可能已经发生了变化;我已经更新了清理代码,以考虑新名称可能已添加到命名空间对象中。 - Martijn Pieters
你说得对,两者都可能已经改变了。嗯...我更担心的是命名空间对象中被删除的名称。我想你需要在进入时保存命名空间键集的副本,然后在退出时删除该集合中的所有键,然后从保存的全局变量更新全局变量。如果相同的名称同时存在,这个顺序将恢复原始值。 - Jamey Sharp
@JameySharp:问题在于你可能已经将名称添加到了globals()中,而这些名称并不存在于你想要保留的命名空间对象中。 :-) 这些问题表明这确实是一种很巧妙的方法。你可能需要跟踪你已经添加的名称。更新答案以解决这个问题。 - Martijn Pieters

2

你应该使用Martijn的答案。但是如果你真的想要做你所问的事情,我认为这个(未经测试的)代码片段可以实现:

exec "del strands...", translation.__dict__

如果你不喜欢这个:那很好,说明你有品味。 :-)

这里还有另一个选项:

def within(obj, func):
    return func(**obj.__dict__)

这样调用:

def dostuff(strands, active, locus, **ignored):
    del ...
within(translation, dostuff)

在我提出问题之后,我想到的事情。我喜欢它,不过:它有什么缺陷吗?访问__dict__感觉可能会引起一些麻烦。 - Michael Pankov
主要的陷阱是你会得到你不知道对象拥有的字段。除了这些字段,你还会传递方法和其他通常不可见的东西。这就是为什么示例dostuff函数必须接受并忽略其他关键字参数的原因。 - Jamey Sharp

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接