235

是否可以在 Python 中前向声明函数?我想cmp在声明之前使用我自己的函数对列表进行排序。

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

我已经组织了我的代码,以便cmp_configs在调用之后放置方法的定义。它失败并出现此错误:

NameError: name 'cmp_configs' is not defined

有没有办法cmp_configs在使用之前“声明”方法?它会让我的代码看起来更干净吗?

我假设有些人会很想告诉我我应该重新组织我的代码,这样我就不会遇到这个问题。但是,在某些情况下,这可能是不可避免的,例如在实现某些形式的递归时。如果你不喜欢这个例子,假设我有一个确实需要转发声明一个函数的情况。

考虑这种情况,在 Python 中需要前向声明一个函数:

def spam():
    if end_condition():
        return end_result()
    else:
        return eggs()

def eggs():
    if end_condition():
        return end_result()
    else:
        return spam()

之前定义的位置end_condition和位置。end_result

是重新组织代码并始终将定义放在调用之前的唯一解决方案吗?

4

16 回答 16

164

您可以做的是将调用包装到它自己的函数中。

以便

foo()

def foo():
    print "Hi!"

会破裂,但是

def bar():
    foo()

def foo():
    print "Hi!"

bar()

将正常工作。

一般规则Python不是函数应该在代码中定义得更高(如 中Pascal,而是应该在使用之前定义它。

希望有帮助。

于 2009-10-19T19:36:32.520 回答
105

如果您通过以下方式启动脚本:

if __name__=="__main__":
   main()

那么您可能不必担心“前向声明”之类的事情。你看,解释器会加载你所有的函数,然后启动你的 main() 函数。当然,请确保您的所有导入也正确;-)

想一想,我从来没有听说过 python 中的“前向声明”之类的东西......但话又说回来,我可能错了 ;-)

于 2009-10-19T19:33:53.557 回答
85

如果你不想在使用之前定义一个函数,并且在之后定义它是不可能的,那么在其他模块中定义它呢?

从技术上讲,您仍然首先定义它,但它很干净。

您可以创建如下递归:

def foo():
    bar()

def bar():
    foo()

Python 的函数是匿名的,就像值是匿名的一样,但它们可以绑定到一个名称。

在上面的代码中,foo()没有调用名称为 foo 的函数,它调用的函数恰好foo在调用点绑定到名称。可以在foo其他地方重新定义,bar然后调用新函数。

您的问题无法解决,因为这就像要求获取尚未声明的变量一样。

于 2009-10-19T19:36:50.490 回答
12

我很抱歉恢复这个线程,但这里没有讨论可能适用的策略。

使用反射可以做一些类似于前向声明的事情。例如,假设您有一段代码如下所示:

# We want to call a function called 'foo', but it hasn't been defined yet.
function_name = 'foo'
# Calling at this point would produce an error

# Here is the definition
def foo():
    bar()

# Note that at this point the function is defined
    # Time for some reflection...
globals()[function_name]()

因此,通过这种方式,我们在实际定义之前确定了要调用的函数,实际上是前向声明。由于上述原因,python 中的语句与ifglobals()[function_name]()相同,因为 python 必须在调用每个函数之前查找它。如果要使用该模块来查看这两个语句的比较,它们的计算成本完全相同。foo()function_name = 'foo'timeit

当然这里的例子没什么用,但是如果有一个复杂的结构需要执行一个函数,但必须在之前声明(或者在结构上之后声明没有意义),可以只存储一个字符串并稍后尝试调用该函数。

于 2014-02-22T04:08:36.543 回答
11

如果对 cmp_configs 的调用在其自己的函数定义中,则应该没问题。我举个例子。

def a():
  b()  # b() hasn't been defined yet, but that's fine because at this point, we're not
       # actually calling it. We're just defining what should happen when a() is called.

a()  # This call fails, because b() hasn't been defined yet, 
     # and thus trying to run a() fails.

def b():
  print "hi"

a()  # This call succeeds because everything has been defined.

一般来说,将代码放在函数中(例如 main())会解决您的问题;只需在文件末尾调用 main() 即可。

于 2009-10-19T19:38:52.690 回答
10

python中没有像前向声明这样的东西。您只需确保在需要之前声明您的函数。请注意,在执行函数之前,不会解释函数的主体。

考虑以下示例:

def a():
   b() # won't be resolved until a is invoked.

def b(): 
   print "hello"

a() # here b is already defined so this line won't fail.

您可以认为函数的主体只是另一个脚本,一旦您调用该函数就会被解释。

于 2009-10-19T19:42:13.770 回答
8

有时,自上而下的算法最容易理解,从整体结构开始,深入细节。

您可以在没有前向声明的情况下这样做:

def main():
  make_omelet()
  eat()

def make_omelet():
  break_eggs()
  whisk()
  fry()

def break_eggs():
  for egg in carton:
    break(egg)

# ...

main()
于 2012-05-29T10:37:36.147 回答
7

不,我不相信有任何方法可以在 Python 中前向声明函数。

假设你是 Python 解释器。当你到达线路时

print "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

要么你知道 cmp_configs 是什么,要么你不知道。为了继续,您必须知道 cmp_configs。是否有递归无关紧要。

于 2009-10-19T19:37:31.693 回答
5
# declare a fake function (prototype) with no body
def foo(): pass

def bar():
    # use the prototype however you see fit
    print(foo(), "world!")

# define the actual function (overwriting the prototype)
def foo():
    return "Hello,"

bar()

输出:

Hello, world!
于 2019-04-29T17:49:48.293 回答
4

您不能在 Python 中前向声明函数。如果您在定义函数之前执行了逻辑,那么您可能无论如何都会遇到问题。将您的操作放在if __name__ == '__main__'脚本的末尾(通过执行一个您命名为“main”的函数,如果它不重要),您的代码将更加模块化,如果您需要,您可以将其用作模块到。

此外,用生成器表示替换该列表理解(即,print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs))

另外,不要使用cmp已弃用的 。使用key并提供小于函数。

于 2009-10-20T04:53:42.953 回答
4

导入文件本身。假设文件名为 test.py:

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')
于 2018-10-11T09:20:08.493 回答
2

TL;DR:Python不需要前向声明。只需将函数调用放在函数def定义中,就可以了。

def foo(count):
    print("foo "+str(count))
    if(count>0):
        bar(count-1)

def bar(count):
    print("bar "+str(count))
    if(count>0):
        foo(count-1)

foo(3)
print("Finished.")

递归函数定义,完美成功地给出:

foo 3
bar 2
foo 1
bar 0
Finished.

然而,

bug(13)

def bug(count):
    print("bug never runs "+str(count))

print("Does not print this.")

在尚未定义的函数的顶级调用处中断,并给出:

Traceback (most recent call last):
  File "./test1.py", line 1, in <module>
    bug(13)
NameError: name 'bug' is not defined

Python 是一种解释型语言,类似于 Lisp。它没有类型检查,只有运行时函数调用,如果函数名已绑定则成功,如果未绑定则失败。

至关重要的是,函数def定义不执行其行内的任何函数调用,它只是声明函数体将由什么组成。同样,它甚至不进行类型检查。所以我们可以这样做:

def uncalled():
    wild_eyed_undefined_function()
    print("I'm not invoked!")

print("Only run this one line.")

它运行得非常好(!),输出

Only run this one line.

关键是定义调用之间的区别。

解释器执行顶层的所有内容,这意味着它会尝试调用它。如果它不在定义内。
您的代码遇到了麻烦,因为您试图在绑定之前调用一个函数,在这种情况下是顶层。

解决方案是将您的非顶级函数调用放在函数定义中,然后在稍后的某个时间调用该函数。

关于“if __ main __”的业务是基于这个原则的成语,但你必须明白为什么,而不是简单地盲目地遵循它。

当然还有更多关于 lambda 函数和动态重新绑定函数名称的高级主题,但这些不是OP 所要求的。此外,它们可以使用这些相同的原则来解决: (1) defs 定义一个函数,它们不调用它们的行;(2) 当您调用未绑定的函数符号时,您会遇到麻烦。

于 2020-09-28T22:42:24.333 回答
1

Python 不支持前向声明,但常见的解决方法是在脚本/代码末尾使用以下条件:

if __name__ == '__main__': main()

这样,它将首先读取整个文件,然后评估条件并调用 main() 函数,该函数将能够调用任何前向声明的函数,因为它已经首先读取了整个文件。此条件利用特殊变量,每当我们从当前文件运行 Python 代码时该变量__name__返回__main__值(当代码作为模块导入时,然后__name__返回模块名称)。

于 2020-12-05T17:22:36.353 回答
0

“只需重新组织我的代码,这样我就不会遇到这个问题。” 正确的。很容易做到。总是有效。

您始终可以在引用之前提供该功能。

“但是,在某些情况下,这可能是不可避免的,例如在实现某些形式的递归时”

看不出这怎么可能。请提供一个示例,说明您无法在使用之前定义该功能的地方。

于 2009-10-19T19:33:09.783 回答
0

现在等一下。当您的模块到达示例中的 print 语句时,beforecmp_configs已定义,您希望它做什么?

如果您使用 print 发布问题确实试图代表这样的内容:

fn = lambda mylist:"\n".join([str(bla)
                         for bla in sorted(mylist, cmp = cmp_configs)])

那么在执行该语句之前不需要定义cmp_configs,只需在代码中稍后定义它就可以了。

现在,如果您尝试将cmp_configs参数的默认值引用为 lambda,那么情况就不同了:

fn = lambda mylist,cmp_configs=cmp_configs : \
    "\n".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])

现在您需要cmp_configs在到达此行之前定义一个变量。

[编辑 - 下一部分结果不正确,因为在编译函数时将分配默认参数值,即使您稍后更改 cmp_configs 的值也会使用该值。]

幸运的是,Python 如此适应类型,并不关心你定义为什么cmp_configs,所以你可以用这个语句开头:

cmp_configs = None

编译器会很高兴。请务必在cmp_configs调用之前声明真实的fn

于 2009-10-19T19:56:39.753 回答
-1

一种方法是创建一个处理函数。尽早定义处理程序,并将处理程序放在您需要调用的所有方法下方。

然后,当您调用处理程序方法来调用您的函数时,它们将始终可用。

处理程序可以接受一个参数nameOfMethodToCall。然后使用一堆 if 语句来调用正确的方法。

这将解决您的问题。

def foo():
    print("foo")
    #take input
    nextAction=input('What would you like to do next?:')
    return nextAction

def bar():
    print("bar")
    nextAction=input('What would you like to do next?:')
    return nextAction

def handler(action):
    if(action=="foo"):
        nextAction = foo()
    elif(action=="bar"):
        nextAction = bar()
    else:
        print("You entered invalid input, defaulting to bar")
        nextAction = "bar"
    return nextAction

nextAction=input('What would you like to do next?:')

while 1:
    nextAction = handler(nextAction)
于 2013-08-04T04:24:25.880 回答