简体   繁体   English

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

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

programming isn't my field, but I'm trying to learn.编程不是我的领域,但我正在努力学习。 I've been writing a program that works something like this:我一直在编写一个像这样工作的程序:

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()

Now, this works perfectly, but as my program gets larger and more complicated I am starting to see that this is neither elegant nor easy to maintain.现在,这可以完美运行,但是随着我的程序变得更大更复杂,我开始发现这既不优雅也不易于维护。 I would like to simply write each function in (more or less) sequence, but that causes namerrors when the program reads a reference to a function that hasn't been defined yet (it seems like the program shouldn't worry about it until it has to run the function, by which time it would have already seen the function definition, but oh well).我想简单地按(或多或少)顺序编写每个 function,但是当程序读取对尚未定义的 function 的引用时,这会导致名称错误(似乎程序不应该担心它,直到它必须运行 function,到那时它已经看到了 function 定义,但是哦,好吧)。

What is the simplest way to have this functionality (functions called from within functions) without having to stick the next function definition in the middle of the first function definition?拥有此功能(从函数内部调用的函数)而不必将下一个 function 定义粘贴在第一个 function 定义中间的最简单方法是什么? Thanks in advance!提前致谢!

I un-nested the functions to see what the error was.我取消嵌套函数以查看错误是什么。 The problem you have is that the functions try to access variables defined in the scope of another function.您遇到的问题是函数尝试访问另一个 function 的 scope 中定义的变量。 That won't work.那是行不通的。 You either have to nest functions so that their scopes overlap, as you did -- which is awkward -- or you have to use global variables -- which is less awkward, but still awkward -- or you have to pass variable names from function to function.您要么必须嵌套函数以使其范围重叠,就像您所做的那样——这很尴尬——或者你必须使用全局变量——这不那么尴尬,但仍然很尴尬——或者你必须从 function 传递变量名至 function。

However, because you're using callbacks here -- which are quite advanced.但是,因为您在这里使用了回调——这是相当先进的。 -- executing the third option is more complicated, If you really want to get this working. -- 执行第三个选项更复杂,如果你真的想让它工作的话。 I would suggest an object-oriented approach.我会建议一种面向对象的方法。 But frankly I would suggest starting with something simpler than this for a beginning programmer.但坦率地说,对于初学者来说,我建议从比这更简单的东西开始。

The most important thing is that you get used to scoping rules.最重要的是你习惯了范围规则。 That, at least, I can explain with your code.至少,我可以用你的代码来解释。 Here's an explanation of the NameErrors you were getting.这是您得到的 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()

These two functions look like they do almost the same thing.这两个函数看起来几乎做同样的事情。 But they don't: Here's why:但他们没有:原因如下:

def Secondwindow():
    firstframe.destroy()

This line refers to firstframe , which was defined in the global scope (ie at the 'lowest level' of the program. That means it can be accessed from anywhere. So you're ok here.这一行指的是firstframe ,它在全局 scope 中定义(即在程序的“最低级别”。这意味着它可以从任何地方访问。所以你在这里没问题。

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

These variables are all defined within the scope of Secondwindow .这些变量都在 Secondwindow 的Secondwindow中定义。 That means they only exist within Secondwindow .这意味着它们仅存在于Secondwindow中。 Once you leave Secondwindow , they cease to exist.一旦你离开Secondwindow ,它们就不复存在了。 There are good reasons for this!这是有充分理由的!

def Thirdwindow():
    secondframe.destroy()

Now you run into your problem.现在你遇到了你的问题。 This tries to access secondframe , but secondframe is only defined within Secondwindow .这试图访问secondframe ,但secondframe仅在Secondwindow中定义。 So you get a NameError .所以你得到一个NameError

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

Again, these are all defined only within the scope of ThirdWindow .同样,这些都仅在 ThirdWindow 的ThirdWindow中定义。

Now, I can't explain everything you need to know to make this work, but here's a basic hint.现在,我无法解释完成这项工作所需的所有知识,但这里有一个基本提示。 You can create a global variable within a function's namespace by saying您可以通过说在函数的命名空间中创建全局变量

global secondframe
secondframe = Frame(root)

Normally python assumes that variables defined in a function are local variables, so you have to tell it otherwise.通常 python 假定 function 中定义的变量是局部变量,因此您必须另外说明。 That's what global secondframe does.这就是global secondframe所做的。 Now you really shouldn't do this very often, because as the global scope fills up with more and more variables, it becomes harder and harder to work with them.现在你真的不应该经常这样做,因为随着全局 scope 充满越来越多的变量,使用它们变得越来越难。 Functions create smaller scopes (or 'namespaces' as they're called in some contexts) so that you don't have to keep track of all the names (to make sure you don't use the same name in two places, or make other even more disastrous mistakes).函数创建更小的范围(或在某些上下文中称为“命名空间”),因此您不必跟踪所有名称(以确保您不会在两个地方使用相同的名称,或者其他更灾难性的错误)。

Normally, to avoid creating a global variable, you would have each function return the frame it defines by calling return secondframe .通常,为了避免创建全局变量,您将让每个 function 通过调用return secondframe返回它定义的帧。 Then you could add a function argument to each function containing the previous frame, as in def Thirdwindow(secondframe) .然后,您可以在包含前一帧的每个 function 中添加一个 function 参数,如def Thirdwindow(secondframe) But because you're using callbacks to call Secondwindow , etc., this method gets knotty.但是因为您使用回调来调用Secondwindow等,所以这个方法变得棘手。 Here's some code that works around the problem by using lambda statements.下面是一些使用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()

But the best way to fix this is to use object-oriented code.但是解决这个问题的最好方法是使用面向对象的代码。 Unfortunately that's just too complex a topic to get into;不幸的是,这个话题太复杂了。 it would just add more verbiage to an already long post.它只会在已经很长的帖子中添加更多的措辞。 I honestly think you should spend some time getting used to functions and scoping first.老实说,我认为您应该先花一些时间来习惯函数和范围。


That said, I found a moment to fiddle with an object-oriented variation.也就是说,我找到了一个时间来摆弄面向对象的变体。 Here it is:这里是:

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()

A couple of things to note.有几点需要注意。 First, in those lines that read like this, there's a subtle error:首先,在像这样读的那些行中,有一个微妙的错误:

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

You were storing None in thirdcontent , because the pack() method has no return value.您将None存储在thirdcontent中,因为pack()方法没有返回值。 If you want to preserve a reference to the Label , you have to save the reference first, then pack() it separately, as I did in new_frame above.如果你想保留对Label的引用,你必须先保存引用,然后单独pack()它,就像我在上面的new_frame中所做的那样。

Second, as you can see from my replace method, you don't actually have to destroy the frame to change the text of the label or the button command.其次,从我的replace方法中可以看出,实际上不必破坏框架来更改 label 的文本按钮命令。 The above still destroys the first three frames just to show how it would work.上面仍然破坏了前三帧,只是为了展示它是如何工作的。

Hope this gets you started.希望这能让你开始。 Good luck.祝你好运。

You can add a parent variable to each function, as that is more or less the only dynamic part of your recursion:您可以为每个 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)

It looks like you're coding an application with frames and a forward button, like a Windows installer or a slideshow.看起来您正在编写带有框架和前进按钮的应用程序,例如 Windows 安装程序或幻灯片。

Instead of having many frames, each differing only by the text they contain, why not just have one master frame object and the text separate?与其拥有许多框架,每个框架的区别仅在于它们包含的文本,为什么不只有一个主框架 object 和文本分开? I don't use Tk for my GUIs, but here's what I mean (might work):我不将 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

If you can copy&paste code you can factor it out:如果您可以复制和粘贴代码,则可以将其分解:

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