Python动态函数构建和闭包规则

时间:2016-10-15 09:03:43

标签: python functional-programming closures

http://pythontutor.com/visualize.html中以交互方式执行以下代码时,每个build_match_and_apply_functions调用的框架在图形视图中显示为灰色:

screenshot from pythontutor

这个程序用于获取复数单词,引自Python 3中DIVE的章节

代码:

import re

def build_match_and_apply_functions(pattern, search, replace):
    def matches_rule(word):                                   
        return re.search(pattern, word)
    def apply_rule(word):                                     
        return re.sub(search, replace, word)
    return (matches_rule, apply_rule)        

patterns =  \
    (
    ('[sxz]$',           '$',  'es'),
    ('[^aeioudgkprt]h$', '$',  'es'),
    ('(qu|[^aeiou])y$',  'y$', 'ies'),
    ('$',                '$',  's')
  )
rules = [build_match_and_apply_functions(pattern, search, replace)  
         for (pattern, search, replace) in patterns]


def plural(noun):
    for matches_rule, apply_rule in rules: 
        if matches_rule(noun):
            return apply_rule(noun)


plural('vacancy')

问题:

1)灰框是什么意思?它是一个仍然记忆的闭包吗?

2)我可以进入内存块。所以我可以在对象区域找出,所有matches_rule函数是否相同?如果它们相同,那么f2 / f3 / f4 / f5应该提供模式/搜索/替换值。

如果没有,如果所有的match_rules函数都已经被改为不同的函数,那么f2 3 4 5可能会结束生效并且消失。他们没用了。

我不知道,这就是动态语言的工作原理和构建方式。

pythontutor.com分析DIGRAM真让我感到惊讶,导师做得很好

如果你没有使它过期,请复制下面的链接并将我的代码粘贴到其中。我打赌你很开心。

http://pythontutor.com/visualize.html

1 个答案:

答案 0 :(得分:1)

灰色帧是执行帧,包含闭包。从理论上讲,它们可以从内存中读取。

例如,我设法使用以下代码(CPython v3.5)从帧中读取数据:

首先使用一些实用程序:

import gc, inspect, ctypes, random

def all_live_ids():
    """Get all valid object IDs"""
    for obj in gc.get_objects():
        yield id(obj)

def getobj(obj_id):
    """Get the object from object ID"""
    if obj_id in all_live_ids():
        return ctypes.cast(obj_id, ctypes.py_object).value
    else:
        return None

frame_ids = []

def print_frames():
    for fid in frame_ids:
        frame = getobj(fid)
        if frame:
            print('frame', hex(fid), 'is alive and has pattern:',
                   frame.f_locals['pattern'])
        else:
            print('frame', hex(fid), 'is dead')

然后是问题代码的简化版本:

def build_match_and_apply_functions(pattern, search, replace):
    frame = inspect.currentframe()
    frame_ids.append(id(frame))
    print('executing frame', frame, 'pattern:', pattern)
    def matches_rule(word):                                   
        return re.search(pattern, word)
    return matches_rule

rule1 = build_match_and_apply_functions('[sxz]$', '$', 'es')
rule2 = build_match_and_apply_functions('[^aeioudgkprt]h$', '$', 'es')

print('\nbefore gc.collect():')
print_frames()

gc.collect()

print('\nafter gc.collect():')
print_frames()

就我而言,它印有:

executing frame <frame object at 0x0071EDB0> pattern: [sxz]$
executing frame <frame object at 0x00C0E4B0> pattern: [^aeioudgkprt]h$

before gc.collect():
frame 0x71edb0 is alive and has pattern: [sxz]$
frame 0xc0e4b0 is alive and has pattern: [^aeioudgkprt]h$

after gc.collect():
frame 0x71edb0 is dead
frame 0xc0e4b0 is dead

我做了什么?

我记得frame_ids中所有帧的ID。在CPython中,id返回对象的内存地址。

然后我使用this solution将ID转换回对象。

我必须这样做,以便能够在不存储对框架的引用的情况下获取框架(Usualy weakref执行此操作,但不执行frame个对象。)

有一个问题

请注意gc.collect后框架消失了。一旦函数完成,就不需要这些帧,但是有一些循环引用阻止了它们的立即删除。 gc.collect足够聪明,可以检测到这些并删除不需要的帧。使用不同的代码,即使不调用gc.collect也可能发生这种情况,我相信它也应该发生在http://pythontutor.com/中,但是它们的内部与garbace收集器存在某种冲突。