在Python中查找变量的前一个值

2024-06-15 01:50:50 发布

您现在位置:Python中文网/ 问答频道 /正文

这可能是一个很奇怪的问题,但不管怎样

考虑一个名为a的变量。 现在让我们给它赋值如下:

a = 1

现在让我们改变a的值。在

^{pr2}$

在Python中,有没有办法知道一个变量的前一个值,而不将它存储在另一个变量中变量。确实如此python在内部维护一种分类帐,记录变量在其生存期内的所有值,可以访问这些值?在


Tags: 记录赋值办法分类帐生存期pr2
3条回答

。如果不给对象分配另一个变量,就无法知道变量的前一个值。只有引用的存在才能使对象在内存中保持活动状态。当对象的引用计数达到零时,垃圾回收器将处理该对象。在

但是,根据Fluent Python by Luciano Ramalho中的示例,在控制台会话中可能使用对对象的弱引用(弱引用不会增加对象的引用计数!)公司名称:

>>> import weakref
>>> a = {0, 1}
>>> wref = weakref.ref(a)
>>> wref()
{0, 1}
>>> a = {2, 3}
>>> wref()
{0, 1}

VS

^{pr2}$

输出:

{0, 1}
None

技巧是Python控制台自动将变量_分配给表达式的结果,而表达式不是None

回答:实际上,我们可以

但不是一般情况。在

你需要一些魔法。在

magick被称为“自定义名称空间”。在

整个想法来自阿明·罗纳彻的演讲5 years of Bad Ideas。在

Magick:具有值历史记录的自定义命名空间

让我们创建保存值历史记录的自定义命名空间。在

为了演示起见,让我们更改__del__的规则,而不是删除值,而是插入None。在

from collections import MutableMapping
class HistoryNamespace(MutableMapping):
    def __init__(self):
        self.ns = {}        
    def __getitem__(self, key):
        return self.ns[key][-1]  # Rule 1. We return last value in history 
    def __delitem__(self, key):
        self.ns[key].append(None) # Rule 4. Instead of delete we will insert None in history
    def __setitem__(self, key, value): # Rule 3. Instead of update we insert value in history
        if key in self.ns:
            self.ns[key].append(value)            
        else:
            self.ns[key] = list([value,]) # Rule 2. Instead of insert we create history list
    def __len__(self):
         return len(self.ns)
    def __iter__(self):
         return iter(self.ns)    

history_locals = HistoryNamespace()        
exec('''
foo=2
foo=3
del foo  
foo=4
print(foo)
''', {}, history_locals)
print("History of foo:", history_locals.ns['foo'])

高兴吧!在

自定义名称空间是非常强大的技术,但几乎从未使用过。在

我觉得这个事实有点令人费解。在

简短回答,长回答是,如果你自己做的话。在


local names的精确处理(dict,array,…)是由实现定义的,但是对于所有的意图和目的,都不会跟踪名称的历史记录。没有一个主要的实现提供这种功能。在

就Python语言而言,没有指定名称的对象已经不存在了。实际上,垃圾回收可以自由声明任何当前未绑定到名称的环境对象。无论是immediately还是at an arbitrary time都不会改变这样的对象是Python代码的禁区。否则,Python将不得不保留大量死掉的对象。在

一旦名称被重新命名,它以前的引用对象就会从中消失。


是的,但请不要这样做,除非您真的需要

有多种方法可以钩住Python代码的执行。例如,可以使用^{}截获调用/行/返回;调试器就是这样工作的,可以检查任何内容。如果您可以控制实际代码的执行方式,请参见Alex Yu's answer以获取仅钩住命名空间的方法。在

import sys

class LocalsTracer:
    """Tracer for local assignment that prints the history of names"""
    def __init__(self, frame, event, arg):
        assert event == "call"
        # identifier for the call we are tracing - name, file, line
        self.subject = "%s (%s:%s)" % (frame.f_code.co_name, frame.f_code.co_filename, frame.f_code.co_firstlineno)
        self.names = {}

    # tracer gets *called* by the interpreter
    def __call__(self, frame, event, arg):
        if event == "line":
            self.trace_names(frame, event, arg)
        elif event in ("return", "exception"):
            self.trace_names(frame, event, arg)
            self.trace_exit(frame, event, arg)
        else:
            raise RuntimeError("Invalid event: %r" % event)

    def trace_names(self, frame, event, arg):
        """Capture modifications of names and store their history"""
        for name, value in frame.f_locals.items():
            try:
                if self.names[name][-1] != value:
                    self.names[name].append(value)
            except KeyError:
                self.names[name] = [value]
        return self

    def trace_exit(self, frame, event, arg):
        """Report the current trace on exit"""
        print("exit", self.subject, "via", event)
        print(self.names)

# trace targets can be defined regularly, anywhere
def bar(b):  # tracer captures function parameters
    b = 4

def foo():
    a = 1
    b = 2
    bar(27)  # tracer can recurse
    a = 3

sys.settrace(LocalsTracer)  # start tracing locals assignment
foo()
sys.settrace(None)          # stop tracing

相关问题 更多 >