繁体   English   中英

Python:实现一系列函数,每个函数调用下一个函数

[英]Python: Implementing a series of functions with each one calling the next

编程不是我的领域,但我正在努力学习。 我一直在编写一个像这样工作的程序:

from Tkinter import *
root=Tk()

def Secondwindow():
    firstframe.destroy()
    secondframe = Frame(root)
    secondframe.pack()
    secondcontent = Label(secondframe, text = 'second window content').pack()
    def Thirdwindow():
        secondframe.destroy()
        thirdframe = Frame(root)
        thirdframe.pack()
        thirdcontent = Label(thirdframe, text = 'third window content').pack()
        def Fourthwindow():
            thirdframe.destroy()
            fourthframe = Frame(root)
            fourthframe.pack()
            fourthcontent = Label(fourthframe, text = 'fourth window content').pack()
        thirdbutton = Button(thirdframe, text = 'Next ->', command = Fourthwindow).pack()
    secondbutton = Button(secondframe, text = 'Next ->', command = Thirdwindow).pack()
firstframe = Frame(root)
firstframe.pack()
firstcontent = Label(firstframe, text = 'first window content').pack()
firstbutton = Button(firstframe, text = 'Next ->', command = Secondwindow).pack()

root.mainloop()

现在,这可以完美运行,但是随着我的程序变得更大更复杂,我开始发现这既不优雅也不易于维护。 我想简单地按(或多或少)顺序编写每个 function,但是当程序读取对尚未定义的 function 的引用时,这会导致名称错误(似乎程序不应该担心它,直到它必须运行 function,到那时它已经看到了 function 定义,但是哦,好吧)。

拥有此功能(从函数内部调用的函数)而不必将下一个 function 定义粘贴在第一个 function 定义中间的最简单方法是什么? 提前致谢!

我取消嵌套函数以查看错误是什么。 您遇到的问题是函数尝试访问另一个 function 的 scope 中定义的变量。 那是行不通的。 您要么必须嵌套函数以使其范围重叠,就像您所做的那样——这很尴尬——或者你必须使用全局变量——这不那么尴尬,但仍然很尴尬——或者你必须从 function 传递变量名至 function。

但是,因为您在这里使用了回调——这是相当先进的。 -- 执行第三个选项更复杂,如果你真的想让它工作的话。 我会建议一种面向对象的方法。 但坦率地说,对于初学者来说,我建议从比这更简单的东西开始。

最重要的是你习惯了范围规则。 至少,我可以用你的代码来解释。 这是您得到的 NameErrors 的解释。

def Secondwindow():
    firstframe.destroy()
    secondframe = Frame(root)
    secondframe.pack()
    secondcontent = Label(secondframe, text = 'second window content').pack()
    secondbutton = Button(secondframe, text = 'Next ->', command = Thirdwindow).pack()
def Thirdwindow():
    secondframe.destroy()
    thirdframe = Frame(root)
    thirdframe.pack()
    thirdcontent = Label(thirdframe, text = 'third window content').pack()
    thirdbutton = Button(thirdframe, text = 'Next ->', command = Fourthwindow).pack()

这两个函数看起来几乎做同样的事情。 但他们没有:原因如下:

def Secondwindow():
    firstframe.destroy()

这一行指的是firstframe ,它在全局 scope 中定义(即在程序的“最低级别”。这意味着它可以从任何地方访问。所以你在这里没问题。

    secondframe = Frame(root)
    secondframe.pack()
    secondcontent = Label(secondframe, text = 'second window content').pack()
    secondbutton = Button(secondframe, text = 'Next ->', command = Thirdwindow).pack()

这些变量都在 Secondwindow 的Secondwindow中定义。 这意味着它们仅存在于Secondwindow中。 一旦你离开Secondwindow ,它们就不复存在了。 这是有充分理由的!

def Thirdwindow():
    secondframe.destroy()

现在你遇到了你的问题。 这试图访问secondframe ,但secondframe仅在Secondwindow中定义。 所以你得到一个NameError

    thirdframe = Frame(root)
    thirdframe.pack()
    thirdcontent = Label(thirdframe, text = 'third window content').pack()
    thirdbutton = Button(thirdframe, text = 'Next ->', command = Fourthwindow).pack()

同样,这些都仅在 ThirdWindow 的ThirdWindow中定义。

现在,我无法解释完成这项工作所需的所有知识,但这里有一个基本提示。 您可以通过说在函数的命名空间中创建全局变量

global secondframe
secondframe = Frame(root)

通常 python 假定 function 中定义的变量是局部变量,因此您必须另外说明。 这就是global secondframe所做的。 现在你真的不应该经常这样做,因为随着全局 scope 充满越来越多的变量,使用它们变得越来越难。 函数创建更小的范围(或在某些上下文中称为“命名空间”),因此您不必跟踪所有名称(以确保您不会在两个地方使用相同的名称,或者其他更灾难性的错误)。

通常,为了避免创建全局变量,您将让每个 function 通过调用return secondframe返回它定义的帧。 然后,您可以在包含前一帧的每个 function 中添加一个 function 参数,如def Thirdwindow(secondframe) 但是因为您使用回调来调用Secondwindow等,所以这个方法变得棘手。 下面是一些使用lambda语句解决问题的代码。

from Tkinter import *
root=Tk()

def Secondwindow(firstframe):
    firstframe.destroy()
    secondframe = Frame(root)
    secondframe.pack()
    secondcontent = Label(secondframe, text = 'second window content').pack()
    secondbutton = Button(secondframe, text = 'Next ->', command = lambda: Thirdwindow(secondframe)).pack()
def Thirdwindow(secondframe):
    secondframe.destroy()
    thirdframe = Frame(root)
    thirdframe.pack()
    thirdcontent = Label(thirdframe, text = 'third window content').pack()
    thirdbutton = Button(thirdframe, text = 'Next ->', command = lambda: Fourthwindow(thirdframe)).pack()
def Fourthwindow(thirdframe):
    thirdframe.destroy()
    fourthframe = Frame(root)
    fourthframe.pack()
    fourthcontent = Label(fourthframe, text = 'fourth window content').pack()

firstframe = Frame(root)
firstframe.pack()
firstcontent = Label(firstframe, text = 'first window content').pack()
firstbutton = Button(firstframe, text = 'Next ->', command = lambda: Secondwindow(firstframe)).pack()

root.mainloop()

但是解决这个问题的最好方法是使用面向对象的代码。 不幸的是,这个话题太复杂了。 它只会在已经很长的帖子中添加更多的措辞。 老实说,我认为您应该先花一些时间来习惯函数和范围。


也就是说,我找到了一个时间来摆弄面向对象的变体。 这里是:

from Tkinter import *
root=Tk()

class FrameRepeater(object):
    def __init__(self, start=0, end=4):
        self.frame = None
        self.number = start
        self.end = end

    def new_frame(self):
        if self.frame:
            self.frame.destroy()
        self.frame = Frame(root)
        self.frame.pack()
        self.content = Label(self.frame, text = 'window ' + str(self.number) + ' content')
        self.content.pack()
        self.button = Button(self.frame, text = 'Next ->', command = self.replace)
        self.button.pack()
        self.number += 1

    def replace(self):
        if self.number < self.end:
            self.new_frame()
        elif self.number >= self.end:
            self.content.config(text='Press button again to quit')
            self.button.config(command=self.quit)

    def quit(self):
        self.frame.destroy()
        root.destroy()
        exit()

FrameRepeater().new_frame()
root.mainloop()

有几点需要注意。 首先,在像这样读的那些行中,有一个微妙的错误:

thirdcontent = Label(thirdframe, text = 'third window content').pack()

您将None存储在thirdcontent中,因为pack()方法没有返回值。 如果你想保留对Label的引用,你必须先保存引用,然后单独pack()它,就像我在上面的new_frame中所做的那样。

其次,从我的replace方法中可以看出,实际上不必破坏框架来更改 label 的文本按钮命令。 上面仍然破坏了前三帧,只是为了展示它是如何工作的。

希望这能让你开始。 祝你好运。

您可以为每个 function 添加一个parent变量,因为这或多或少是递归的唯一动态部分:

def RecursiveWindow(parent):
    parent.destroy()
    frame = Frame(root)
    frame.pack()
    framContent = Label(frame, text = 'second window content').pack()

    if foo:   # This won't go on forever, will it?
      RecursiveWindow(self)

看起来您正在编写带有框架和前进按钮的应用程序,例如 Windows 安装程序或幻灯片。

与其拥有许多框架,每个框架的区别仅在于它们包含的文本,为什么不只有一个主框架 object 和文本分开? 我不将 Tk 用于我的 GUI,但这就是我的意思(可能有效):

from Tkinter import *

slides = ['Text one', 'Text two', 'Text three', 'cow']
number = 0

root = Tk()
frame = Frame(root).pack()
button = Button(frame, text = 'Next ->', command = NextFrame).pack()


def NextFrame(number):
  frameContent = Label(frame, text = slides[number]).pack()
  number += 1

如果您可以复制和粘贴代码,则可以将其分解:

from Tkinter import *
root=Tk()

messages = ['first window content', 'second window content', 'third window content', 'fourth window content' ]

def nextframe(current, messages):
    # what happens when you click the button
    def command():
        current.destroy()
        makeframe(messages)
    return command

def makeframe(messages):        
    frame = Frame(root)
    frame.pack()
    # take the first message
    next_content = Label(frame, text=messages.pop(0)).pack()

    if messages: # if there are more make the button
        next_button = Button(frame, text = 'Next ->', command = nextframe(frame, messages)).pack()

makeframe(messages)
root.mainloop()

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM