鱼C论坛

 找回密码
 立即注册
查看: 1267|回复: 8

[已解决]python函数问题

[复制链接]
发表于 2018-4-24 10:22:51 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能^_^

您需要 登录 才可以下载或查看,没有账号?立即注册

x
def funX():
    x =
5
   
def funY():
        
nonlocal x
        x +=
1
        
return x
   
return funY
a = funX()
print(a())
print(a())
print(a()) type a <class'function'>
6
7
8
def funX():
    x =
5
   
def funY():
        
nonlocal x
        x +=
1
        
return x
   
return funY
a = funX()()
print(a)
print(a)
print(a) type a <class'int'>
6
6
6
解释下为什么上面是678,下面的是666
最佳答案
2018-4-24 10:46:40
本帖最后由 ABC23 于 2018-4-24 11:06 编辑

一个简单的闭包问题。
对于新手来说很常见的疑问。
==================
def funX():
    x = 5
    def funY():
        nonlocal x
        x += 1
        return x
    return funY

def f():
    a = funcX()
    for i in range(3):
            print(a())
    print('='*12)
    b = funcX()()
    for i in range(3):
            print(b)
================
&#10140;  desktop python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from dis import dis
>>> import pp
>>> dis(pp.f)
10           0 LOAD_GLOBAL              0 (funcX)
              2 CALL_FUNCTION            0
              4 STORE_FAST               0 (a)

11           6 SETUP_LOOP              26 (to 34)
              8 LOAD_GLOBAL              1 (range)
             10 LOAD_CONST               1 (3)
             12 CALL_FUNCTION            1
             14 GET_ITER
        >>   16 FOR_ITER                14 (to 32)
             18 STORE_FAST               1 (i)

12          20 LOAD_GLOBAL              2 (print)
             22 LOAD_FAST                0 (a)
             24 CALL_FUNCTION            0
             26 CALL_FUNCTION            1
             28 POP_TOP
             30 JUMP_ABSOLUTE           16
        >>   32 POP_BLOCK

13     >>   34 LOAD_GLOBAL              2 (print)
             36 LOAD_CONST               4 ('============')
             38 CALL_FUNCTION            1
             40 POP_TOP

14          42 LOAD_GLOBAL              0 (funcX)
             44 CALL_FUNCTION            0
             46 CALL_FUNCTION            0
             48 STORE_FAST               2 (b)

15          50 SETUP_LOOP              24 (to 76)
             52 LOAD_GLOBAL              1 (range)
             54 LOAD_CONST               1 (3)
             56 CALL_FUNCTION            1
             58 GET_ITER
        >>   60 FOR_ITER                12 (to 74)
             62 STORE_FAST               1 (i)

16          64 LOAD_GLOBAL              2 (print)
             66 LOAD_FAST                2 (b)
             68 CALL_FUNCTION            1
             70 POP_TOP
             72 JUMP_ABSOLUTE           60
        >>   74 POP_BLOCK
        >>   76 LOAD_CONST               0 (None)
             78 RETURN_VALUE
>>>
===========================
你看看上面的字节码可能看懂?
解释:第一次测试,因为a = funcX(),返回内层函数,在Python中函数是一等公民,与类是平起平坐的,闭包保存环境信息(x=5),于是每次调用x +=1,5、6、7……好,在看第二个。
这次因为b = funcX()(),返回的是一个常量(变量名对应的常量对象6),这就不存在闭包环境了。于是:666(呵呵
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复

使用道具 举报

发表于 2018-4-24 10:45:10 | 显示全部楼层
因为上面的那个是直接获得内部函数指针,可以直接调用内部函数
下面的那个是从外部调用内部函数
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-24 10:46:40 | 显示全部楼层    本楼为最佳答案   
本帖最后由 ABC23 于 2018-4-24 11:06 编辑

一个简单的闭包问题。
对于新手来说很常见的疑问。
==================
def funX():
    x = 5
    def funY():
        nonlocal x
        x += 1
        return x
    return funY

def f():
    a = funcX()
    for i in range(3):
            print(a())
    print('='*12)
    b = funcX()()
    for i in range(3):
            print(b)
================
&#10140;  desktop python3
Python 3.6.0 (v3.6.0:41df79263a11, Dec 22 2016, 17:23:13)
[GCC 4.2.1 (Apple Inc. build 5666) (dot 3)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from dis import dis
>>> import pp
>>> dis(pp.f)
10           0 LOAD_GLOBAL              0 (funcX)
              2 CALL_FUNCTION            0
              4 STORE_FAST               0 (a)

11           6 SETUP_LOOP              26 (to 34)
              8 LOAD_GLOBAL              1 (range)
             10 LOAD_CONST               1 (3)
             12 CALL_FUNCTION            1
             14 GET_ITER
        >>   16 FOR_ITER                14 (to 32)
             18 STORE_FAST               1 (i)

12          20 LOAD_GLOBAL              2 (print)
             22 LOAD_FAST                0 (a)
             24 CALL_FUNCTION            0
             26 CALL_FUNCTION            1
             28 POP_TOP
             30 JUMP_ABSOLUTE           16
        >>   32 POP_BLOCK

13     >>   34 LOAD_GLOBAL              2 (print)
             36 LOAD_CONST               4 ('============')
             38 CALL_FUNCTION            1
             40 POP_TOP

14          42 LOAD_GLOBAL              0 (funcX)
             44 CALL_FUNCTION            0
             46 CALL_FUNCTION            0
             48 STORE_FAST               2 (b)

15          50 SETUP_LOOP              24 (to 76)
             52 LOAD_GLOBAL              1 (range)
             54 LOAD_CONST               1 (3)
             56 CALL_FUNCTION            1
             58 GET_ITER
        >>   60 FOR_ITER                12 (to 74)
             62 STORE_FAST               1 (i)

16          64 LOAD_GLOBAL              2 (print)
             66 LOAD_FAST                2 (b)
             68 CALL_FUNCTION            1
             70 POP_TOP
             72 JUMP_ABSOLUTE           60
        >>   74 POP_BLOCK
        >>   76 LOAD_CONST               0 (None)
             78 RETURN_VALUE
>>>
===========================
你看看上面的字节码可能看懂?
解释:第一次测试,因为a = funcX(),返回内层函数,在Python中函数是一等公民,与类是平起平坐的,闭包保存环境信息(x=5),于是每次调用x +=1,5、6、7……好,在看第二个。
这次因为b = funcX()(),返回的是一个常量(变量名对应的常量对象6),这就不存在闭包环境了。于是:666(呵呵
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 1 反对 0

使用道具 举报

 楼主| 发表于 2018-4-24 11:09:56 | 显示全部楼层
def funX():
    x = 5
    def funY():
        nonlocal x
        x += 1
        return x
    a=funY
    print(a())
funX()
funX()
funX()
这个和第一个不一样么
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-4-24 11:31:32 | 显示全部楼层
ABC23 发表于 2018-4-24 10:46
一个简单的闭包问题。
对于新手来说很常见的疑问。
==================

仍然有疑问,
感觉a()是在funX外部调用funY,但是每次调用,x都是上次funY的结果。现在代码改成这样
def funX():
    x = 5
    def funY():
        nonlocal x
        x += 1
        return x
    print(funY())
funX()
funX()
funX()
这个结果还是666等于说每次funX,它内部变量x都x=5了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-24 11:54:13 | 显示全部楼层
>>> dis(pp.funX)
  2           0 LOAD_CONST               1 (5)
              2 STORE_DEREF              0 (x)

  3           4 LOAD_CLOSURE             0 (x)
              6 BUILD_TUPLE              1
              8 LOAD_CONST               2 (<code object funY at 0x103a0fdb0, file "/Users/macbookpro/Desktop/pp.py", line 3>)
             10 LOAD_CONST               3 ('funX.<locals>.funY')
             12 MAKE_FUNCTION            8
             14 STORE_FAST               0 (funY)

  7          16 LOAD_GLOBAL              0 (print)
             18 LOAD_FAST                0 (funY)
             20 CALL_FUNCTION            0
             22 CALL_FUNCTION            1
             24 POP_TOP
             26 LOAD_CONST               0 (None)
             28 RETURN_VALUE
>>>
===================================
看到3标注的哪一行吗?CLOSURE,就是闭包的意思。
函数的调用是基于栈的,首先加载funX,接着加载funY,因为这时候funX还没有结束,于是保存它的环境状态(闭包)。
接着调用funcY,funY弹栈,销毁状态(等于6的状态)。
我们调用了三次funcX,每次都从等于5的状态开始,结合闭包,就不难理解了。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-4-24 12:05:58 From FishC Mobile | 显示全部楼层
BngThea 发表于 2018-4-24 10:45
因为上面的那个是直接获得内部函数指针,可以直接调用内部函数
下面的那个是从外部调用内部函数

了解了一下python,存在闭包的情况下,闭包不会被回收。很隐蔽的问题。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-24 12:48:58 | 显示全部楼层
qq925141439 发表于 2018-4-24 12:05
了解了一下python,存在闭包的情况下,闭包不会被回收。很隐蔽的问题。

。。。
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-4-24 15:25:34 From FishC Mobile | 显示全部楼层
学习了
想知道小甲鱼最近在做啥?请访问 -> ilovefishc.com
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Archiver|鱼C工作室 ( 粤ICP备18085999号-1 | 粤公网安备 44051102000585号)

GMT+8, 2024-4-26 14:23

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

快速回复 返回顶部 返回列表