是否可以在Python中转发声明一个函数?

时间:2009-10-19 19:29:22

标签: python forward-declaration

是否可以在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_conditionend_result

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

17 个答案:

答案 0 :(得分:92)

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

那样

foo()

def foo():
    print "Hi!"

会破裂,但

def bar():
    foo()

def foo():
    print "Hi!"

bar()

将正常运作。

Python中的一般规则 not 该函数应在代码中定义得更高(如Pascal中所述),但应在使用之前定义。< / p>

希望有所帮助。

答案 1 :(得分:78)

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

if __name__=="__main__":
   main()
那么你可能不必担心像“前瞻性宣言”这样的事情。你看,解释器会加载你所有的函数,然后启动你的main()函数。当然,请确保所有导入也正确; - )

来想一想,我从来没有在python中听过“向前声明”这样的事情......但话又说回来,我可能错了; - )

答案 2 :(得分:66)

如果您不想在之前定义一个函数,并且之后定义是不可能的,那么在其他模块中定义它会怎样?

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

您可以创建如下的递归:

def foo():
    bar()

def bar():
    foo()

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

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

你的问题无法解决,因为它就像要求获得一个尚未声明的变量。

答案 3 :(得分:10)

如果对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()。

答案 4 :(得分:10)

我为恢复这个帖子道歉,但是这里没有讨论可能适用的策略。

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

# 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中,语句globals()[function_name]()foo()的{​​{1}}相同,原因如上所述,因为python必须在调用之前查找每个函数。如果要使用function_name = 'foo'模块来查看这两个语句的比较方式,则它们具有完全相同的计算成本。

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

答案 5 :(得分:7)

不,我不相信有任何方法可以在Python中转发声明一个函数。

想象一下,你是Python解释器。当你到达

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

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

答案 6 :(得分:6)

在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.

你可以认为函数体是另一个在调用函数后将被解释的脚本。

答案 7 :(得分:5)

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

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

def main():
  make_omelet()
  eat()

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

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

# ...

main()

答案 8 :(得分:2)

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

此外,用明确的生成器(即print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs))

替换该列表理解

另外,请勿使用已弃用的cmp。使用key并提供小于功能。

答案 9 :(得分:2)

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

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')

答案 10 :(得分:0)

“只是重新组织我的代码,以便我没有这个问题。”正确。容易做到。永远有效。

您可以在参考之前始终提供该功能。

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

看不出那是多么遥远的可能。请提供一个示例,说明在使用之前无法定义功能的地方。

答案 11 :(得分:0)

现在等一下。当您的模块到达示例中的print语句时,在定义cmp_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

答案 12 :(得分:0)

# 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!

答案 13 :(得分:0)

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 的定义不会在其行内执行任何funcall,它只是声明函数主体将由什么组成。同样,它甚至不进行类型检查。因此,我们可以这样做:

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

print("Only run this one line.")

它运行得很好(!),并且有输出

Only run this one line.

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

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

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

有关“如果__主要__”的说法是基于此原理的成语,但是您必须了解原因,而不是盲目地遵循它。

当然有关于Lambda函数和动态重新绑定函数名称的更高级的话题,但这不是OP所要求的。另外,可以使用以下相同原理来解决它们:(1) defs 定义一个函数,它们不调用其行; (2)调用未绑定的功能符号会遇到麻烦。

答案 14 :(得分:0)

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

if __name__ == '__main__': main()

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

答案 15 :(得分:-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)

答案 16 :(得分:-3)

是的,我们可以检查一下。

输入

print_lyrics() 
def print_lyrics():

    print("I'm a lumberjack, and I'm okay.")
    print("I sleep all night and I work all day.")

def repeat_lyrics():
    print_lyrics()
    print_lyrics()
repeat_lyrics()

输出

I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.
I'm a lumberjack, and I'm okay.
I sleep all night and I work all day.

正如BJ Homer在上面的评论中所提到的,Python中的一般规则并不是代码中的函数应该更高(如在Pascal中),而是应该在使用之前定义它。

希望有所帮助。