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

Nat*_*man 165 python forward-declaration

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

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

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

NameError: name 'cmp_configs' is not defined
Run Code Online (Sandbox Code Playgroud)

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()
Run Code Online (Sandbox Code Playgroud)

end_conditionend_result先前已经被定义.

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

Van*_*nya 101

你可以做的是将调用包装成自己的函数.

以便

foo()

def foo():
    print "Hi!"
Run Code Online (Sandbox Code Playgroud)

会打破,但是

def bar():
    foo()

def foo():
    print "Hi!"

bar()
Run Code Online (Sandbox Code Playgroud)

将正常工作.

一般的规则Python不是该功能应该被定义的代码(如在更高Pascal),但它应该它的使用之前定义.

希望有所帮助.

  • +1最直接的答案,与keystone概念:Pascal =定义更高,Python =定义更早. (16认同)
  • @krubo 是的,正如所写的那样很好 (3认同)
  • 这是正确的答案,它还解释了为什么 `if __name__=="__main__":` 解决方案有效。 (2认同)
  • 如果我理解正确,这意味着 OP 的 spam() 和 Eggs() 示例写得很好。那是对的吗? (2认同)

jld*_*ont 87

如果您通过以下内容启动脚本:

if __name__=="__main__":
   main()
Run Code Online (Sandbox Code Playgroud)

那么你可能不必担心像"前瞻性宣言"这样的事情.你看,解释器会加载你所有的函数,然后启动你的main()函数.当然,确保你所有的进口都是正确的;-)

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

  • +1最实用的答案:如果您将此代码放在最外层源文件的**底部**,那么您可以按任意顺序自由定义. (13认同)
  • 很棒的提示; 真的帮助我; 因为我更喜欢"自上而下"编程而不是自下而上. (2认同)

Ric*_*chN 73

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

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

您可以创建如下的递归:

def foo():
    bar()

def bar():
    foo()
Run Code Online (Sandbox Code Playgroud)

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

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

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

  • 简而言之,如果你有_if\_ _ _ name\_\_ =='\ _ _ _ main\_\_ _':main()_作为你脚本中的最后一行,一切都会好的! (43认同)
  • @SanjayManohar:避免在`import your_module`上执行它 (9认同)
  • @FilipePina我还没有理解你的评论 - 为什么你不能把代码的最后一行简单地放在`main()`中? (3认同)
  • 我想补充一下 - 有时可以使用lambdas来规避这些问题,因为它们稍后会被评估. (2认同)
  • WRT是“匿名的”,您的意思是“一流的对象”。 (2认同)

BJ *_*mer 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.
Run Code Online (Sandbox Code Playgroud)

通常,将代码放在函数内(例如main())将解决您的问题; 只需在文件末尾调用main()即可.


KGa*_*oir 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]()
Run Code Online (Sandbox Code Playgroud)

因此,通过这种方式,我们确定了在实际定义之前要调用的函数,实际上是前向声明.在蟒蛇的说法globals()[function_name]()是一样的foo(),如果function_name = 'foo'由于上述原因的讨论,因为蟒蛇必须在调用它之前查找各项功能.如果要使用该timeit模块来查看这两个语句的比较,它们具有完全相同的计算成本.

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


unu*_*tbu 7

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

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

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

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

  • 好吧,如果你只做一次通过代码.一些编译器(我在解释中实现了python)做了两次传递,因此这些事情可以解决.前向声明,或至少某种范围的发现,将是非常好的. (8认同)

Pio*_*pla 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.
Run Code Online (Sandbox Code Playgroud)

您可以认为函数的主体只是在调用函数后将被解释的另一个脚本.


小智 7

# 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()
Run Code Online (Sandbox Code Playgroud)

输出:

# 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()
Run Code Online (Sandbox Code Playgroud)


fun*_*oll 6

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

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

def main():
  make_omelet()
  eat()

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

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

# ...

main()
Run Code Online (Sandbox Code Playgroud)


小智 5

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

import test

if __name__=='__main__':
    test.func()
else:
    def func():
        print('Func worked')
Run Code Online (Sandbox Code Playgroud)