【问题标题】:Difference in declaring a function in Python and C在 Python 和 C 中声明函数的区别
【发布时间】:2016-01-03 11:13:54
【问题描述】:

我现在正在同时阅读和学习 Python 和 C。 (不要问我为什么,这很有趣!:-)) 我使用 Mark Lutz 的“Learning Python”。以下是他写的关于 Python 函数的内容:

与 C 等编译语言不同,Python 函数不需要 在程序运行之前完全定义。更一般地说,defs 是 直到它们到达并运行后才被评估,并且 defs 中的代码 在以后调用函数之前不会计算。

我不太明白,因为在我的第二本书中,K.N.King 说您可以先声明一个函数,然后再创建一个定义。

英语不是我的母语,所以我在这里缺少什么?

我只能猜测,它与程序运行时有某种关系。在 C 中,编译器运行程序并找到函数声明。即使没有定义,编译器也会继续查找函数定义。 C 中的函数声明有助于避免函数返回类型的问题(因为它默认为 int)。另一方面,在 Python 函数中,直到在运行时到达它才会被评估。并且当它到达时,它不会评估函数的主体,直到有函数调用。但是这个猜测并不能解释上面的引用。

那么卢茨先生在说什么?我有点糊涂了……

【问题讨论】:

  • python也编译好了...
  • “defs 在到达并运行之前不会被评估”对我来说意味着(也在学习 Python :-),你甚至可以在函数体中出错,但直到执行没有达到调用该函数,错误未被注意到
  • @rolika,取决于错误。语法错误会在程序开始执行之前导致崩溃。其他错误将等到您到达问题行。
  • @ShadowRanger 在开发新程序时,没有人应该接受垃圾编译器。尤其是初学者学习当前的 C 标准而不是它的某个古老版本是非常重要的。例如,我不明白为什么人们接受 gcc 默认情况下“将您的程序编译为某种 GNU 垃圾”,而不是默认设置为“根据标准 C 编译您的程序”。
  • @Lundin:嘿,你正在向合唱团宣讲编写现代代码。可悲的是,在处理遗留代码时,你不会成为一个纯粹主义者,所以你需要知道这些怪癖。如果你的百万行应用程序是在假设非严格编译的情况下编写的,它可能会编译失败,并有 10,000 个警告(诚然,其中几千个可能指出真正的问题,其余的可能使它不可移植)如果你打开-std=c11 -pedantic -Wall -Wextra -Werrors,并坚持停止工作直到代码库干净是不可能的。

标签: python c


【解决方案1】:

当源代码出现时,Python 函数被“执行”:

def foo():
    return bar

def bar():
    return 'spam'

这里foo 依赖于bar,但是直到foo 声明之后才执行bar 函数定义并不重要。只有当您调用foo() 时,名称bar 才需要存在。

但是,在 C 中,如果没有额外的原型声明,则无法执行上述操作:

char * bar(void);

char * foo(void) {
    return bar();
}

char * bar(void) {
    return "spam";
}

所以是的,您可以推迟bar 的完整声明,但编译器仍要求您仍提前声明函数签名。

所以 Python 在运行时才关心是否定义了 bar,而 C 要求在编译时声明函数。

【讨论】:

  • 不,C 允许隐式原型(尽管我不建议您使用该功能)。
  • @skyking:对,未知函数被认为返回int,对吧?除非您使用 C99 或 C11,否则我认为这至少会给您一个编译器警告。
  • @MartijnPieters 是的。正确。
  • 谢谢,您说得简单明了。我想,我明白了。
【解决方案2】:

在以下示例中,text_func 在我们第一次开始执行时未定义。在python中,函数是在运行时定义的,当我们执行def行时。

print ('Entry point. No custom functions/methods are defined at this point')

print ('Defining test_func')
# We defined a function here.
# The function is defined at runtime since python when we run this line
def test_func():
    # Obviously, the body of the function is executed only when we call the function.
    # Not when we define it
    print ('evaluating test_func now')


print ('test_func is now defined.')

test_func()

【讨论】:

    【解决方案3】:

    在 C 语言中,您可以在不声明的情况下进行编译,编译器假定函数 是 int,如果是这种情况,您的程序可以编译并运行。 如果您的函数或函数是另一种类型,您将遇到问题并且必须声明。不声明并将声明从代码生成到 h 文件并在需要的地方包含通常会更快。您可以将其留给程序来编写程序的这一部分。就像你可以离开缩进,写一大堆,让缩进为你做。

    【讨论】:

      【解决方案4】:

      在 python 中,你没有前向声明这样的东西。在调用函数之前,不会查找函数(代码)中引用的符号。这就是为什么您不需要按顺序定义函数的原因。例如

      def foo():
          return bar()
      
      def bar():
          return 42
      
      foo()
      

      首先,所有这些都已编译,但我将跳过这些细节。执行时的代码将首先定义一个名为foo 的函数,其主体/代码显示“找到一个名为bar 的东西并调用它,然后返回返回值”。 bar 也有类似的情况。最后在foo(),解释器运行代码说“找到一个名为foo 的东西并调用它”。它此时 foo 已定义,因此它被调用并执行它的主体,但它说“找到一个名为 bar 的东西并调用它”,但此时我们已经通过了 bar 的定义点。

      现在让我们看看C:

      int foo() {
          return bar();
      }
      
      int bar() {
          return 42;
      }
      

      现在我们这里有一些小问题,因为名称查找必须在构建时完成,这不是一个大问题,但请记住,C 编译器按顺序处理源代码,因此它不会向前查找。因此,在编译 foo 时,它会看到您正在尝试调用名为 bar 的东西,编译器不知道它是什么 - 所以它假设您已经正确使用了 bar 并且发现它是一个不带参数并返回int 的函数(没有传递参数,默认情况下它假定它返回int)。然后,当您达到bar 的定义时,幸运的是假设是正确的(因为编译器已经下定决心并在foo 中生成代码假设 - 否则您会得到奇怪的行为)。最后(链接时)它将解析所有符号(无论您放置声明的顺序),并且名称的占位符将只是对名称所指内容的引用。此时,实现可能会删除名称,因为它们不再对程序有任何用途(但可以选择保留它们,因为如果您需要调试程序可能会有所帮助)。

      现在,如果您想更安全一点,您可以转发声明 bar,以便编译器可以检查您是否使用正确类型的参数调用它(并将返回值解释为正确的类型)。然后你会有:

      int bar(void);
      
      int foo(void) {
          return bar();
      }
      
      int bar(void) {
          return 42;
      } 
      

      如果您在 C++ 中进行编程,那么以后的这种方式将是强制性的,因为 C++ 不允许猜测参数类型,但也建议您在 C 中这样做(有编译器警告警告在编译器必须猜测bar 是什么的第一个 C 示例)。

      【讨论】:

        【解决方案5】:

        示例蟒蛇:

        foo()
        def foo():
            #some code
        

        python解释器在调用它之前没有找到任何函数的错误。但是:

        def foo():
            bar() #just declared and not called yet
        
        def bar():
            print("42")
        foo() #now it is called and bar() is also got interpreted 
        

        这将打印42而没有任何错误,因为解释器已经解释并执行了前面的行,直到最后调用了 foo()。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-08-14
          • 1970-01-01
          • 2012-11-23
          • 1970-01-01
          • 2010-10-03
          • 1970-01-01
          • 1970-01-01
          • 2011-01-10
          相关资源
          最近更新 更多