我如何前向声明一个函数,以避免' NameError '以后定义的函数?

是否有可能在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方法之前使用?

有时候,很难重新组织代码来避免这个问题。例如,在实现某些形式的递归时:

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之前已经定义。

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

228683 次浏览

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

您总是可以在引用函数之前提供该函数。

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

我看不出这有什么可能。请举例说明在使用之前不能定义函数的地方。

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

if __name__=="__main__":
main()

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

仔细想想,我从来没有在python中听到过“前向声明”这样的东西……但话说回来,我可能错了;-)

将调用包装到它自己的函数中,以便

foo()


def foo():
print "Hi!"

会破碎,但

def bar():
foo()


def foo():
print "Hi!"


bar()

将正常工作。

Python中的一般规则是,函数应该在使用之前定义,因此不一定意味着它需要在代码中处于更高的位置。

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

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

你可以创建一个像下面这样的递归:

def foo():
bar()


def bar():
foo()

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

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

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

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

假设您是Python解释器。当你走到排队的时候

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

你知道cmp_configs是什么或者你不知道。为了继续,你必须 知道cmp_configs。不管有没有递归

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

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

您可以认为函数体只是另一个脚本,在调用该函数后将对其进行解释。

现在等一下。在你的例子中,当你的模块到达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变量。

[EDIT -接下来的部分被证明是不正确的,因为默认参数值将在函数编译时被赋值,即使您稍后更改cmp_configs的值,该值也将被使用。]

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

cmp_configs = None

这样编译器就高兴了。只是要确保在调用fn之前声明了真正的cmp_configs

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

另外,用生成器表达式替换该列表推导式(即print "\n".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs)))。

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

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

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

def main():
make_omelet()
eat()


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


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


# ...


main()

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

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

处理程序可以接受参数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)

我很抱歉恢复这个话题,但有一个策略没有在这里讨论,但可能适用。

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

# 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()相同,因为python必须在调用每个函数之前查找它。如果使用timeit模块来比较这两个语句,它们具有完全相同的计算代价。

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

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

import test


if __name__=='__main__':
test.func()
else:
def func():
print('Func worked')
# 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!

Python确实需要前向声明。简单地将函数调用放在function 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)def 定义一个函数,它们不调用它们的行;(2)当你调用一个未绑定的函数符号时,你会遇到麻烦。

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

if __name__ == '__main__': main()

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

Python在技术上支持前向声明。

如果你定义了一个函数/类,然后将函数体设置为pass,那么它在全局表中将有一个空条目。

然后你可以“重新定义”;函数/类稍后实现函数/类。

不过,与c/c++的前向声明不同的是,这不能从作用域外(即另一个文件)工作,因为它们有自己的“global”;名称空间

例子:

def foo(): pass


foo()


def foo(): print("FOOOOO")


foo()


foo两次都声明 然而,第一次调用foo时,它没有做任何事情,因为主体只是pass 但是第二次调用foo。它执行print("FOOOOO")

的新体

但再一次。注意,这不会修复循环依赖关系。这是因为文件有自己的名称和自己的函数定义

示例2:


class bar: pass


print(bar)


它会打印<class '__main__.bar'>,但如果它在另一个文件中声明,它将是<class 'otherfile.foo'>

我知道这篇文章很旧了,但我认为这个答案对任何在张贴多年后仍然找到这篇文章的人都是有用的