鱼C论坛

 找回密码
 立即注册
查看: 3015|回复: 26

这个函数问题问了很多大神,还是没听明白。。谁能通俗易懂讲清楚啊

[复制链接]
发表于 2018-5-16 16:43:00 | 显示全部楼层 |阅读模式

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

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

x
funY还是funY(y).jpg
(1)这个图中上面那张,最后一步是return FunY,我试了下如果写return FunY()会报错,说缺失参数,好像侧面在指导我给一个参数就行了。于是我又写return FunY(y)还是不行,说y没被定义。但是只有return FunY是正确的,我知道FunY是函数名,可它也是要返回第二排def funY(y)的,然后返回x*y,那这里同样也有小y,怎么就没有红字说它没被定义呢。这个地方我始终想不通。
(2)我又在这个图下面那张试了下输入return FunY(x),可以看到funX(3)是可以得到9,但是funX()()给两个参数就报错了,报错类型那个int整型出来也是没懂

啊啊啊啊希望有大神能把我讲明白啊。
小甲鱼最新课程 -> https://ilovefishc.com
回复

使用道具 举报

 楼主| 发表于 2018-5-16 16:49:55 | 显示全部楼层
funY(x)就没有提示说x没被定义,按理说x也没被定义啊,没有对它赋值啊,只是一个函数的参数,怎么就没报错
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-16 17:57:43 | 显示全部楼层
那么,我来试试

def FunX(x):
        def FunY(y):
                return x * y
        return FunY

i = FunX(8)
i(5)


你可以把这个当成是函数的动态定义,当执行FunX时才定义FunY,还没有执行FunX时,FunY是没有定义的,只有执行完了FunX,FunY才被定义
当执行 i = FunX(8)时,把参数8传递给FunX,然后执行FunX函数,FunX函数做两件事,1.定义FunY函数(注意,是定义FunY函数,不是调用FunY)
例如
def FunX(x):
        def FunY(y):
                print(y)

FunX(8)
这样是不会有任何输出,因为调用FunX函数只是定义了FunY函数,而没有调用FunY函数

回到上面,
FunX函数做两件事,1.定义FunY函数,2.FunX函数返回,把FunY作为返回值
然后执行返回到了 i = FunX(8),因为已经调用完成了FunX,所以
i = FunX(8)
相当于
i = FunY
这时i就相当于是在FunX中返回的那个FunY函数

i(5)
调用在FunX中定义的那个FunY函数,把5作为参数

然后执行FunY函数,FunY函数只做一件事,计算 x * y,然后返回这个计算结果,这个变量y的值是5,是刚刚调用时传递过来的
那x是多少呢?

python中的变量(也许应该说是python中的对象?)更类似于标签,
调用FunX函数时把8作为参数传递给了FunX,那这个x就指向8,因为在FunX中还有一个FunY,FunY中的这个return x * y,
这个x被FunY中占用着,导致FunX函数执行完毕也不能销毁这个x,也就是说,在通过i(5)调用FunY时FunY中的x依然是上次调用FunX的结果,上次调用FunX时变量x是8,所以在通过i(5)调用FunY时FunY中的变量x就是8
也就是说
def FunY(y):
        return x * y
相当于
def FunY(y):
        return 8 * 5

所以调用i(5)的结果就是 40
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 1 反对 1

使用道具 举报

发表于 2018-5-16 18:11:33 | 显示全部楼层
i = FunX(8)
i(5)



FunX(8)(5)
效果一样

FunX返回一个函数(应该可以这么说吧,要么说成是返回一个函数指针?^_^),就可以直接在FunX(8)后面加括号调用从FunX返回的这个函数,FunX(8)(5)把5作为参数调用从FunX返回的这个函数FunY

这样的语法貌似是从C/C++继承过来的,在C/C++中就有这样的语法
例如

  1. #include <stdio.h>

  2. void Print(int n)
  3. {
  4.         while(n--)
  5.                 printf("hello\n");
  6. }

  7. void (*GetFun(void))(int n)
  8. {
  9.         return Print;
  10. }

  11. int main(void)
  12. {
  13.         GetFun()(3);
  14.        
  15.         return 0;
  16. }
复制代码

  1. hello
  2. hello
  3. hello
  4. 请按任意键继续. . .
复制代码


GetFun函数返回一个函数指针,通过 GetFun()(3);    在GetFun()后面加括号,可以直接调用从GetFun返回的函数,把3作为参数调用
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-16 18:27:54 | 显示全部楼层
def FunX(x):
        def FunY(y):
                return x * y
        return FunY(x)

FunX(3)
FunX(3)(2)

执行 FunX(3),调用FunX函数,3作为参数,然后执行FunX,FunX做两件事,1.定义FunY,2.返回FunY函数的结果
第一件事没什么好说的
第二件事
返回FunY函数的结果

返回(FunY函数的结果
需要调用FunY函数,才能得到 FunY函数的结果
在返回之前,先调用FunY,把x作为参数,x是之前由FunX(3)调用的,所以x是3,也就是说
在返回之前,先调用FunY,把3作为参数,然后执行FunY,FunY只做一件事,return x * y
x是3,是调用FunX函数时得到的,
y也是3,是在FunX中调用FunY时,把x作为参数传递的
那么返回 3 * 3
此时执行返回到FunX函数中的 return FunY(x),已经调用了FunY,得到了值 9
现在该返回了,把这个 9作为返回值
然后调用FunX结束,FunX得到了值是9

然后 FunX(3)(2)
???

FunX又没有返回函数(函数指针),怎么能在FunX(3)后面加括号,调用函数呢?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-5-17 15:50:11 | 显示全部楼层
人造人 发表于 2018-5-16 17:57
那么,我来试试

def FunX(x):

例如
def FunX(x):
        def FunY(y):
                print(y)

FunX(8)
这样是不会有任何输出,因为调用FunX函数只是定义了FunY函数,而没有调用FunY函数
谢谢你的回答,我还想问2个问题(1)这里你说只是定义funY没有调用,那要怎么写,才叫调用了funY函数
(2)当我们写return funY(y)不是会报错吗,说y没被定义,那么是程序运行到哪一步感受到的y没被定义呢,是第几排的y?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 16:25:40 | 显示全部楼层
leftjay 发表于 2018-5-17 15:50
例如
def FunX(x):
        def FunY(y):

360截图167510316782111.png



360截图18430709689587.png


运行 a = 1 没有问题
运行 a = 2 没有问题
运行 a = 3 没有问题
运行 a = z 然后报错


“那么是程序运行到哪一步感受到的y没被定义呢”
现在,这个问题你有答案了吗?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-5-17 16:46:30 | 显示全部楼层
人造人 发表于 2018-5-17 16:25
运行 a = 1 没有问题
运行 a = 2 没有问题
运行 a = 3 没有问题

啊,更晕了,我知道FunY(3)是直接把y=3带入了,然后print 3.这样来看程序是从下往上执行?以前我们知道的程序都是从上往下运行啊。。。。另外再问一个问题哈,return FunY和return FunY(2),前者是返回FunY的地址,后者是返回FunY的函数,但是最终结果都是输出一个数字。你能告诉我他们本质区别吗,结果是一样的,输出的都是x*y的数值。。
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-5-17 17:05:07 | 显示全部楼层
人造人 发表于 2018-5-17 16:25
运行 a = 1 没有问题
运行 a = 2 没有问题
运行 a = 3 没有问题

例子.png
我把你的例子运行了下,最后一步加了个return,结果一样,我现在最想弄明白的是程序的一步步运行顺序哈,。以FunX(0)为例,第一步是在全局里写FunX(0),第二部程序把X=0传递给第一排FunX函数,第三步进入定义FunY(y)并且打印y,但是直到这一步y的参数是多少,程序是不知道的。直到第四部return FunY(3)程序知道y=3,那又倒转回第三步去把y=3打出来?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 17:43:07 | 显示全部楼层
360截图17001022433084.png

程序从上往下执行

先执行第1行,第1行由 def 开头,是一个函数定义,那么执行这个函数定义,(再一次强调,是函数定义,不是调用)
定义完Fun1时已经到了第5行,第5行空,跳过
执行第6行,定义Fun2
执行完定义,到了12行,跳过
执行第13行,定义Fun3
执行完定义,到了19行,跳过
20行,跳过
执行21行,输出了 "调用Fun3之前"
执行22行,Fun3(0)这是一个函数调用,那么现在暂停当前执行的这个函数(在python中,当前执行的这个函数是main函数?)
去调用Fun3
现在执行到了第14行,输出了 "Fun3 开始"
程序继续向下执行
执行第15行,输出了 "开始调用Fun2"
执行第16行,Fun2(0)是函数调用,暂停当前函数(Fun3)的执行,转去执行Fun2
现在执行到了第7行,输出了 "Fun2 开始"
继续向下执行,执行第8行,输出了 "开始调用Fun1"
执行第9行,函数调用,暂停当前函数(Fun2)的执行,转去执行Fun1
现在执行到了第2行
第2行输出 "Fun1 开始"
第3行输出 0,因为函数Fun2调用函数Fun1时把0,作为参数
第4行输出 "Fun1 结束"

Fun1 执行结束,返回,返回到调用Fun1的下一行
也就是返回到了第10行,输出 "结束调用Fun1"
第11行输出 "Fun2 结束"
Fun2执行结束,返回,返回到调用Fun2的下一行
也就是返回到了第17行,输出 "结束调用Fun2"
第18行输出 "Fun3 结束"
Fun3执行结束,返回,返回到调用Fun3的下一行
也就是返回到了第23行,输出 "调用Fun3之后"
然后继续向下执行,下面没有了,然后python结束了这个程序


程序从上往下执行
如果有函数调用,暂停当前函数的执行,转去执行这个函数,这个函数执行结束,返回来继续向下执行

学python不能深入研究这些
python是属于顶层的一门语言
顶层就意味着封装
封装就意味着不能深入研究这些(你当然可以深入研究这些,但是顶层不是为了让你研究这些而存在的)

如果你非要深入研究这些,欢迎来C/C++版块,
来这里深入学C/C++,深入理解这些,深入底层

附上代码
  1. def Fun1(a):
  2.     print("Fun1 开始")
  3.     print(a)
  4.     print("Fun1 结束")

  5. def Fun2(b):
  6.     print("Fun2 开始")
  7.     print("开始调用Fun1")
  8.     Fun1(0)
  9.     print("结束调用Fun1")
  10.     print("Fun2 结束")

  11. def Fun3(c):
  12.     print("Fun3 开始")
  13.     print("开始调用Fun2")
  14.     Fun2(0)
  15.     print("结束调用Fun2")
  16.     print("Fun3 结束")


  17. print("调用Fun3之前")
  18. Fun3(0)
  19. print("调用Fun3之后")
复制代码
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-5-17 18:23:21 | 显示全部楼层
本帖最后由 leftjay 于 2018-5-17 18:38 编辑
人造人 发表于 2018-5-17 17:43
程序从上往下执行

先执行第1行,第1行由 def 开头,是一个函数定义,那么执行这个函数定义,(再一 ...


大神,你收下我膝盖吧,真的是小甲鱼本鱼了。但是我还是要问之前说return FunY(y)报错,我们如果也按步骤分析,第一步funX(0)把X=0传递给了FUnX函数,然后程序进入FunX函数后第一件事是定义FUnY,第二个事情就打印y,这里为什么存在y这个参数没被定义呢。。。如果是return FunY(3)就可以,其实这里的3和y没有本质区别,就是参数

我大胆理解下,你看对不对。y报错说没被定义是程序进行到最后一步return FunY(y)被检测出来的,前面进行到return x*y都不会报错,是因为程序出了局部函数,前面的return x*y已经被Python机制回收走了,所以才无法识别。那如果既然如此,同理分析return FunY(3),程序进行到return x*y并不知道y是什么,知道下一步才晓得y=3,这时就倒回到上一步x*y,把y=3带进去,那这个时候按照前面的分析,return x*y已经被Python机制回收走了,程序是怎么实现3*X的呢
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 18:52:30 | 显示全部楼层
leftjay 发表于 2018-5-17 18:23
大神,你收下我膝盖吧,真的是小甲鱼本鱼了。但是我还是要问之前说return FunY(y)报错,我们如果也按 ...

你现在问的这个是哪一个代码?
把你现在问的这个代码发出来
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-5-17 19:03:28 | 显示全部楼层
本帖最后由 leftjay 于 2018-5-17 19:10 编辑
人造人 发表于 2018-5-17 18:52
你现在问的这个是哪一个代码?
把你现在问的这个代码发出来
^_^


代码.png
就这个,我故意写了这三组,分别有不同结果,你能不能按照刚刚那样模仿程序的运行过程,一行一行给我写下程序是如何工作的,这样我晓得Y是怎么被识别没有定义的,也清楚为啥第三组敲下回车后没有反应(我以为会输入6的)。万分感激了
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 19:14:18 | 显示全部楼层
leftjay 发表于 2018-5-17 19:03
就这个,我故意写了这三组,分别有不同结果,你能不能按照刚刚那样模仿程序的运行过程,一行一行给我 ...

无标题.png


这个描述和刚刚发的代码不统一
我认为你这个描述不对,发一下这个描述的代码,我确认一下

小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

 楼主| 发表于 2018-5-17 21:05:25 | 显示全部楼层
人造人 发表于 2018-5-17 19:14
这个描述和刚刚发的代码不统一
我认为你这个描述不对,发一下这个描述的代码,我确认一下

这个就是对应的那三个框里的第二组
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 21:25:14 | 显示全部楼层
leftjay 发表于 2018-5-17 21:05
这个就是对应的那三个框里的第二组

360截图16720402313364.png


不对应呀,在你的描述中提到了打印y
这3个框中没有一个print
^_^
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 21:29:11 | 显示全部楼层
360截图16720402313364.png

貌似是这个?
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 22:47:19 | 显示全部楼层
def FunX(x):
        def FunY(y):
                print(y)
        return FunY(3)

FunX(0)


程序从上到下执行

先执行第1行 定义FunX函数
定义FunX函数一直到第5行,定义结束

第6行调用FunX函数
现在转去执行FunX函数
第1步,定义FunY
第2步,返回(FunY函数的结果)
第2步又分成2步
第2步的第1步,调用FunY
                现在执行FunY
                执行print(y),打印出 3
                FunY执行完毕,返回,如果没有写返回值,默认返回None
第2步的第2步,把调用FunY函数得到的结果(None)返回
FunX函数执行结束,返回到main函数(就是调用FunX的那个函数,我想那个函数的名字应该是main)


注意,print(y) 只执行了1次


函数定义和函数调用完全不一样

函数定义就是让这个函数存在,在函数定义之前这个函数是不存在的,定义之后这个函数才存在
函数定义不执行函数,只是让这个函数存在

函数调用就是执行这个函数,在调用之前要确保这个函数存在,也就是确保这个函数定义过了


用另外一个比喻可能更容易理解
我们都知道,计算机要执行程序,这个程序必须在内存中,而一般我们的程序存在硬盘

那么函数调用就相当于把这个函数从硬盘加载到内存,就是把硬盘上这个函数的指令复制到内存,然后就定义完了
函数调用就相当于执行这个已经在内存中的函数

在函数调用之前,必须保证这个函数是存在的,也就是说已经从硬盘加载到内存了,如果内存中没有这个函数,你要如何调用(执行)这个函数?
函数定义就仅仅是把硬盘中这个函数的指令复制到内存,仅此而已,并不执行(调用)这个函数
函数调用就仅仅是执行已经加载到内存的这个函数,仅此而已

再一次说明,定义函数并不执行这个函数,就只是让这个函数存在
函数调用就只是执行这个已经存在的函数


1.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 22:51:43 | 显示全部楼层
  1. def FunX(x):
  2.         print("开始执行FunX")
  3.         print("开始定义FunY")
  4.         def FunY(y):
  5.                 print("开始执行FunY")
  6.                 print(y)
  7.                 print("执行完成FunY")
  8.         print("结束定义FunY")
  9.         print("开始返回FunY函数的结果,这实际上是2步,1执行FunY,2返回执行完FunY得到的结果")
  10.         return FunY(3)
  11.         print("这个和下面的 'return FunY(0)'不会被执行")
  12.         return FunY(0)

  13. FunX(0)

  14. '''
  15. 想知道哪一个先执行哪一个后执行很难吗?
  16. 把你认为所有可能的位置都写print,然后运行这个程序,证明你的猜想
  17. '''
复制代码


2.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

发表于 2018-5-17 22:58:47 | 显示全部楼层
再来一个
  1. print("-------------------开始定义Fun1")
  2. def Fun1(a):
  3.     print("Fun1 开始")
  4.     print(a)
  5.     print("Fun1 结束")

  6. print("-------------------结束定义Fun1")
  7. print("-------------------开始定义Fun2")
  8. def Fun2(b):
  9.     print("Fun2 开始")
  10.     print("开始调用Fun1")
  11.     Fun1(0)
  12.     print("结束调用Fun1")
  13.     print("Fun2 结束")

  14. print("-------------------结束定义Fun2")
  15. print("-------------------开始定义Fun3")
  16. def Fun3(c):
  17.     print("Fun3 开始")
  18.     print("开始调用Fun2")
  19.     Fun2(0)
  20.     print("结束调用Fun2")
  21.     print("Fun3 结束")

  22. print("-------------------结束定义Fun3")
  23. print("调用Fun3之前")
  24. Fun3(0)
  25. print("调用Fun3之后")
复制代码


1.png
小甲鱼最新课程 -> https://ilovefishc.com
回复 支持 反对

使用道具 举报

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

本版积分规则

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

GMT+8, 2025-12-30 05:58

Powered by Discuz! X3.4

© 2001-2023 Discuz! Team.

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