V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
tioover
V2EX  ›  Python

这种时候怎么实现?我只能觉得用类模拟指针了……

  •  
  •   tioover ·
    tioover · 2014-03-30 22:09:30 +08:00 · 4344 次点击
    这是一个创建于 3921 天前的主题,其中的信息可能已经有所发展或是发生改变。
    不小心发出来了,我在回复里面说
    第 1 条附言  ·  2014-03-30 22:44:49 +08:00
    比如说

    def foobar():
    ....foo = 1
    ....def bar():
    ........return foo

    在这种情况下,foobar() 的环境是 {"foo": 1}
    按照一般思路,bar() 里面找不到 foo,就往上层作用域找。

    但是 bar() 这个闭包函数是一个独立的,不应该依赖于 foobar() 的环境,不然只要 bar 存在,foobar 就不能被回收。
    所以应该 bar 的环境里包括 bar 里面的自由变量 {"foo": parent_env["foo"]}。

    但是这样的话上层的变量变更了以后bar里面的foo还是1


    def foobar():
    ....foo = 1
    ....def bar():
    ........return foo
    ....foo = 2

    所以应该传递指向数据的指针,Python 里面没有指针,但是被容器(class)容纳的数据是必然传递指针的,比如说 []

    所以我想可以用 class Ptr:ptr=None 来模拟指针

    env["foo"] = Ptr(data)


    在 bar 的闭包中,只需要

    env["foo"] = parent["foo"] 就行

    复制就是 parent["foo"].ptr = data 这样就行了。

    但是用类模拟指针总感觉很糟。
    28 条回复    2018-10-27 14:59:39 +08:00
    bcxx
        1
    bcxx  
       2014-03-30 22:15:23 +08:00
    是的,你说的对
    casparchen
        2
    casparchen  
       2014-03-30 22:16:42 +08:00
    对的
    tioover
        3
    tioover  
    OP
       2014-03-30 22:18:09 +08:00
    嗯,刚刚写了一大堆,提交编辑却说没办法,我重新写。
    tioover
        4
    tioover  
    OP
       2014-03-30 22:18:49 +08:00
    是这样的,我在写一个 Lisp 解释器,里面要实现闭包,可是 Python 里面没有指针
    tioover
        5
    tioover  
    OP
       2014-03-30 22:21:17 +08:00
    比如说

    def foobar():
    ....foo = 1
    ....def bar():
    ........return foo

    在这种情况下,foobar() 的环境是 {"foo": 1}
    按照一般思路,bar() 里面找不到 foo,就往上层作用域找
    bcxx
        6
    bcxx  
       2014-03-30 22:24:28 +08:00
    你设计一个 context 将函数运行的状态(就是运行环境)注入进去就行(解释器解释的时候根据这个 context 来解释)
    tioover
        7
    tioover  
    OP
       2014-03-30 22:24:51 +08:00
    问题是 bar 如果作为一个返回值返回,foo 的环境应该删除
    bcxx
        8
    bcxx  
       2014-03-30 22:26:51 +08:00
    @tioover 应该是将 bar 的运行环境和 bar 绑定了一起返回(就是不返回 function,而是返回 function + context,也就是整个闭包。)
    tioover
        9
    tioover  
    OP
       2014-03-30 22:27:15 +08:00
    所以应该 bar 的环境里包括 bar 里面的自由变量 {"foo": parent_env["foo"]}
    但是这样的话上层的变量变更了以后bar里面的foo还是1


    def foobar():
    ....foo = 1
    ....def bar():
    ........return foo
    ....foo = 2
    tioover
        10
    tioover  
    OP
       2014-03-30 22:28:56 +08:00
    所以应该传递指向数据的指针,Python 里面没有指针,但是被容器(class)容纳的数据是必然传递指针的,比如说 []

    所以我想可以用 class Ptr 来模拟指针

    env["foo"] = Ptr(data)

    这样就可以了,但是感觉不美好
    tioover
        11
    tioover  
    OP
       2014-03-30 22:29:37 +08:00
    @bcxx 我就是这个意思……刚刚想说完再回复
    tioover
        12
    tioover  
    OP
       2014-03-30 22:32:01 +08:00
    接 #10
    在 bar 的闭包中,只需要

    env["foo"] = parent["foo"] 就行

    复制就是 parent["foo"].ptr = data 这样就行了。

    但是用指针模拟总感觉很糟
    bcxx
        13
    bcxx  
       2014-03-30 22:33:41 +08:00
    @tioover 你可以看看 lisp / racket 之类是怎样做的……
    tioover
        14
    tioover  
    OP
       2014-03-30 22:34:39 +08:00
    @bcxx 人家背后直接能用指针吧 = = 用 Python 只能用这种曲线的方法实现
    bcxx
        15
    bcxx  
       2014-03-30 22:39:57 +08:00
    @tioover 额,你太执着名字上的东西了= = 那时语义上的实现啊,你底层具体怎么实现是另外一回事啊
    tioover
        16
    tioover  
    OP
       2014-03-30 22:41:31 +08:00
    @bcxx 我是觉得用一个 class 去模拟指针不美……
    tioover
        17
    tioover  
    OP
       2014-03-30 22:45:07 +08:00
    忘了还有附言了,把内容放在附言里面……
    bcxx
        18
    bcxx  
       2014-03-30 22:56:04 +08:00
    @tioover = = 不能理解
    ctrlaltdeletel
        19
    ctrlaltdeletel  
       2014-03-31 01:01:35 +08:00 via Android
    额 没太清楚为什么foo的值会被修改,以及被修改了为什么要影响bar环境中foo的值
    tioover
        20
    tioover  
    OP
       2014-03-31 01:52:23 +08:00 via Android
    @ctrlaltdeletel 在 Python 里面你改一样东西的值,其实是新创建一个对象然后修改指针,不能直接修改指针的内容。
    后者是闭包的特性…
    rannnn
        21
    rannnn  
       2014-03-31 07:31:54 +08:00
    PO主是在写解释器还是写lisp->py的编译器?
    aristotle9
        22
    aristotle9  
       2014-03-31 11:11:33 +08:00
    "但是 bar() 这个闭包函数是一个独立的,不应该依赖于 foobar() 的环境,不然只要 bar 存在,foobar 就不能被回收。"

    这个说法不太对. 函数bar()依赖定义他的环境(词法作用域), 包括foobar的环境. foobar()可以回收, 但是他的环境还是会被引用到(如果bar()或者其他定义在其中的函数没有回收的话), 函数对环境的引用是单向的. 当所有引用某个环境的函数都被回收后, 该环境才会回收. 所以在设计上, 环境与函数是独立的, 可以把任意一个函数拿到任意一个环境中去求值.
    环境的结构是一个map: ENV = {parent: ENV(上一层), data: {foo: 1,...}}

    PO主可以去看看 ECMA-262或者http://www.eopl3.com/
    exch4nge
        23
    exch4nge  
       2014-03-31 13:20:42 +08:00
    额,回不回收的我不大懂。

    def foobar():
    ....foo = 1
    ....def bar():
    ........return foo
    ....foo = 2

    (话说如果bar被调用的话正确返回应该是2对吧……)
    这时候foobar的context里是有变量foo的。 getContext(foobar) --> { foo: ?? }
    建立bar的context的时候,bar的context只知道自己的context的parent是foobar的context。
    getContext(bar).parent === getContext(foobar)

    不管怎样,bar被调用的时候,肯定是在foobar函数里面所被调用的,调用的时候,foobar的context中的foo的变量是带着某个值的。然后bar里面出现foo,先从自己的context找foo变量,没有就继续递归往上找,直到找到一个,如果没找到就报错。

    额,可能重点是lz的 “但是 bar() 这个闭包函数是一个独立的,不应该依赖于 foobar() 的环境”。这句话应该是不正确的吧……
    exch4nge
        24
    exch4nge  
       2014-03-31 13:26:43 +08:00
    补充一下。另外一个重点是遇到bar的定义的时候,不应该把foo的值传进去当作bar的context的。foo一直是foobar的context里面的变量
    exch4nge
        25
    exch4nge  
       2014-03-31 13:44:16 +08:00
    ……没法删评论,好吧,我上面的理解是错的,大家请无视……

    闭包是包含foo这样的东西的……
    tioover
        26
    tioover  
    OP
       2014-03-31 14:36:52 +08:00
    @exch4nge

    def foobar():
    ....foo = 1
    ....def bar():
    ........return foo
    ....foo = 2
    ....return bar()
    可以这样搞……
    tioover
        27
    tioover  
    OP
       2014-03-31 14:37:15 +08:00
    @exch4nge

    def foobar():
    ....foo = 1
    ....def bar():
    ........return foo
    ....foo = 2
    ....return bar
    上面的说错了……
    gladuo
        28
    gladuo  
       2018-10-27 14:59:39 +08:00
    想想不能手动控制传值传引用是很蛋疼。。。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   1102 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 23ms · UTC 18:53 · PVG 02:53 · LAX 10:53 · JFK 13:53
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.