如何前向声明一个函数以避免稍后定义的函数出现“NameError”?
- 2025-01-15 08:45:00
- admin 原创
- 117
问题描述:
cmp
是否可以在 Python 中前向声明函数?我想在声明之前使用自己的函数对列表进行排序。
print "
".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_condition
和end_result
已经预先定义。
唯一的解决方案是重新组织代码并始终将定义放在调用之前吗?
解决方案 1:
将调用包装到其自己的函数中,以便
foo()
def foo():
print "Hi!"
会破裂,但是
def bar():
foo()
def foo():
print "Hi!"
bar()
将正常工作。
Python 中的一般规则是,函数应该在使用之前定义,但这并不一定意味着它需要位于代码的更高位置。
解决方案 2:
如果您通过以下步骤启动脚本:
if __name__=="__main__":
main()
那么您可能不必担心“前向声明”之类的事情。您会看到,解释器将加载所有函数,然后启动您的 main() 函数。当然,请确保所有导入也正确无误 ;-)
仔细想想,我从来没有听说过 Python 中有“前向声明”这样的东西……但是话又说回来,我可能是错的 ;-)
解决方案 3:
如果您不想在使用函数之前定义它,而在之后定义它又是不可能的,那么在其他模块中定义它怎么样?
从技术上讲,您仍然需要先定义它,但它很干净。
您可以创建如下递归:
def foo():
bar()
def bar():
foo()
Python 的函数是匿名的,就像值是匿名的一样,但它们可以绑定到名称。
在上面的代码中,foo()
并没有调用名为 foo 的函数,而是调用了foo
在调用时恰好绑定到该名称的函数。可以foo
在其他地方重新定义,bar
然后调用新函数。
您的问题无法解决,因为这就像要求获取一个尚未声明的变量。
解决方案 4:
有时,自上而下地理解算法是最容易的,从整体结构开始,然后深入到细节。
无需前向声明即可执行此操作:
def main():
make_omelet()
eat()
def make_omelet():
break_eggs()
whisk()
fry()
def break_eggs():
for egg in carton:
break(egg)
# ...
main()
解决方案 5:
我很抱歉重新提起这个话题,但是有一种这里未讨论的策略可能适用。
使用反射可以做类似于前向声明的事情。例如,假设你有一段代码如下:
# 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
当然,这里的例子非常没用,但是如果有一个需要执行函数的复杂结构,但必须在之前声明(或者从结构上讲,之后声明它是没有意义的),那么就可以存储一个字符串并尝试稍后调用该函数。
解决方案 6:
如果对 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() 即可。
解决方案 7:
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.
您可以认为函数主体只是调用函数后将被解释的另一个脚本。
解决方案 8:
不,我不相信有任何方法可以在 Python 中前向声明函数。
想象一下你是 Python 解释器。当你看到下面这行代码时
print "
".join([str(bla) for bla in sorted(mylist, cmp = cmp_configs)])
您要么知道 cmp_configs 是什么,要么不知道。要继续,您必须知道 cmp_configs。是否有递归并不重要。
解决方案 9:
# 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!
解决方案 10:
导入文件本身。假设文件名为 test.py:
import test
if __name__=='__main__':
test.func()
else:
def func():
print('Func worked')
解决方案 11:
您无法在 Python 中前向声明函数。如果您在定义函数之前执行逻辑,那么您可能还是遇到了问题。将您的操作放在if __name__ == '__main__'
脚本末尾(通过执行名为“main”的函数(如果它不简单)),您的代码将更加模块化,并且您可以在需要时将其用作模块。
另外,用生成器表达式替换该列表推导(即`print "
".join(str(bla) for bla in sorted(mylist, cmp=cmp_configs))`)
另外,不要使用cmp
,因为它已被弃用。请使用key
并提供小于函数。
解决方案 12:
TL;DR: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定义不会执行其行内的任何函数调用,它只是声明函数体将由什么组成。同样,它甚至不进行类型检查。所以我们可以这样做:
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) def 定义一个函数,它们不会调用它们的行;(2) 当您调用未绑定的函数符号时,您会遇到麻烦。
解决方案 13:
Python 不支持前向声明,但常见的解决方法是在脚本/代码末尾使用以下条件:
if __name__ == '__main__': main()
这样,它将首先读取整个文件,然后评估条件并调用 main() 函数,该函数将能够调用任何前向声明的函数,因为它已经首先读取了整个文件。此条件利用特殊变量,每当我们从当前文件运行 Python 代码时,__name__
该变量都会返回__main__
值(当代码作为模块导入时,然后__name__
返回模块名称)。
解决方案 14:
现在等一下。当您的模块到达示例中的打印语句时,之前cmp_configs
已定义过,您到底希望它做什么?
如果您使用打印发布的问题实际上试图表达如下内容:
fn = lambda mylist:"
".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
在调用之前声明 real 即可fn
。
解决方案 15:
Python 在技术上支持前向声明。
如果您定义一个函数/类,然后将主体设置为传递,它将在全局表中有一个空条目。
然后您可以稍后“重新定义”该函数/类来实现该函数/类。
与 c/c++ 前向声明不同,这在范围之外(即另一个文件)不起作用,因为它们有自己的“全局”命名空间
例子:
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'>
我知道这篇文章已经很老了,但我认为这个答案对那些在这篇文章发布多年后仍然能找到它的人很有用
解决方案 16:
一种方法是创建一个处理程序函数。尽早定义处理程序,并将处理程序放在您需要调用的所有方法下方。
然后,当您调用处理程序方法来调用您的函数时,它们将始终可用。
处理程序可以接受一个参数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)
解决方案 17:
“只要重新组织我的代码,就不会出现这个问题了。”正确。很容易做到。总是有效。
您始终可以在引用之前提供该函数。
“然而,有些情况下这可能是不可避免的,例如在实现某些形式的递归时”
看不出这怎么可能。请提供一个在使用该函数之前无法定义该函数的地方的示例。
扫码咨询,免费领取项目管理大礼包!