簡體   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