2016-10-10 11 views
-1

Чтобы понять разницу в использовании вызовов методов, я написал MVC в python 2.7. Вот код:Python - разница между self.method, lambda: self.method() и self.method() при запуске

import Tkinter as tk 

class Model(object): 
    def __init__(self, *args, **kwargs): 
     # dict 
     self.data = {} 
     # -- >values 
     self.data["Value_One"] = tk.IntVar() 
     self.data["Value_Two"] = tk.IntVar() 
     self.data["Value_Three"] = tk.IntVar() 
     # --> texts 
     self.data["Text_Label_val_One"] = tk.StringVar() 
     self.data["Text_Label_val_Two"] = tk.StringVar() 
     self.data["Text_Label_val_Three"] = tk.StringVar() 

class Control(tk.Tk): 
    def __init__(self, *args, **kwargs): 
     tk.Tk.__init__(self, *args, **kwargs) # init 
     tk.Tk.wm_title(self, "Testing Grounds") # title 

     self.model = Model() 
     self.view = View(parent = self, controller = self) 
     self.view.pack(fill = 'both', expand = True) 

    def set_labels_text(self): 
     self.model.data["Text_Label_val_One"].set("Value_One is set to: {0}".format(self.model.data["Value_One"].get())) 
     self.model.data["Text_Label_val_Two"].set("Value_Two is set to: {0}".format(self.model.data["Value_Two"].get())) 
     self.model.data["Text_Label_val_Three"].set("Value_Three is set to: {0}".format(self.model.data["Value_Three"].get())) 

    def set_value_one(self): 
     self.model.data["Value_One"].set(1)  

    def set_value_two(self): 
     self.model.data["Value_Two"].set(2) 

    def set_value_three(self): 
     self.model.data["Value_Three"].set(3) 

class View(tk.Frame): 
    def __init__(self, parent, controller): 
     tk.Frame.__init__(self, parent) 
     self.controller = controller 

     self.buttons() 
     self.labels() 

    def buttons(self): 
     set_v_one = tk.Button(self, text = "Set Value One To 1", command = lambda: self.controller.set_value_one()) 
     set_v_one.pack(fill = 'x', expand = True) 

     set_v_two = tk.Button(self, text = "Set Value Two To 2", command = self.controller.set_value_two()) 
     set_v_two.pack(fill = 'x', expand = True) 

     set_v_three = tk.Button(self, text = "Set Value Three To 3", command = self.controller.set_value_three) 
     set_v_three.pack(fill = 'x', expand = True) 

     update_lbl_two = tk.Button(self, text = "Update Labels", command = self.controller.set_labels_text) 
     update_lbl_two.pack(fill = 'x') 

    def labels(self): 
     label_one = tk.Label(self, textvariable = self.controller.model.data["Value_One"]) 
     label_one.pack(fill = 'x', expand = True) 

     label_two = tk.Label(self, textvariable = self.controller.model.data["Value_Two"]) 
     label_two.pack(fill = 'x', expand = True) 

     label_three = tk.Label(self, textvariable = self.controller.model.data["Value_Three"]) 
     label_three.pack(fill = 'x', expand = True)   

     label_val_one = tk.Label(self, textvariable = self.controller.model.data["Text_Label_val_One"]) 
     label_val_one.pack(fill = 'x', expand = True) 

     label_val_two = tk.Label(self, textvariable = self.controller.model.data["Text_Label_val_Two"]) 
     label_val_two.pack(fill = 'x', expand = True) 

     label_val_three = tk.Label(self, textvariable = self.controller.model.data["Text_Label_val_Three"]) 
     label_val_three.pack(fill = 'x', expand = True) 

if __name__ == "__main__": 
    app = Control() 
    app.mainloop() 

Если один выполняет его и нажимает кнопку, чтобы обновить метки, результат заключается в следующем:

enter image description here

Как можно видеть, model.self.data["Value_One"] не установлен на из-за использования lambda, который я считал безымянной функцией, которая может только вернуть одно значение и не более того. Здесь он, кажется, препятствует первоначальному вызову метода в командной строке кнопки set_v_one.

В случае model.self.data["Value_Two"] значение обновляется при запуске. Я думаю, это потому, что функция вызывается, когда командная строка кнопки считывается и создается кнопка из-за активного вызова или инициализации метода с помощью команд (), потому что это происходит, даже если никто не делает этого упакуйте кнопку.

Для model.self.data["Value_Three"] значение также не обновляется при запуске. Это, как я думаю, вызвано методом set_value_three(self) контроллера, связанным с командной строкой, но не инициализированным из-за отсутствия вызова для него с помощью команд ().

После нажатия кнопки set_v_one и set_v_three значения получают правильно обновляется, как указано соответствующими этикетками label_one и label_three.

Несмотря на то, что я использую этот метод много, я еще не мог полностью понять, как они работают в деталях. Если кто-то может прояснить это или указать мне на хороший источник, которого я просто еще не нашел, это было бы очень признательно.

+2

'self.method <- ссылка на метод',' self.method() <- вызов метода'. Вы также можете использовать 'self.method', если вы используете' lambda: self.method() ', поскольку лямбда просто делает его вызываемым. –

ответ

2

В общем, здесь нет никакой тайны.

Аргумент команды конструктора Button принимает обратный вызов, то есть функцию, которая будет выполняться при нажатии кнопки. Вы делаете это для кнопок 1 и 3. Поэтому, когда нажимаются соответствующие кнопки, вызывается функция (будь то метод лямбда или связанный метод), и метки обновляются. С помощью кнопки два вы фактически выполняете метод, который устанавливает значение второй метки и назначает аргумент команды результат вызова этого метода (который будет None). Насколько я понимаю, это является нарушением API, и, вероятно, это может привести к ошибке.

Чтобы подвести итог, ваше замешательство, похоже, связано с смешиванием функции (объекта, который может быть выполнен) и его вызова (действия выполнения функции).

+0

Итак, в этом случае 'lambda' работает как обертка, которая указывает на метод, который вызывается, если в методе есть не более одного возвращаемого значения, называемого' lambda'? – MrPadlog

+0

Лямбда - это просто ярлык для определения функции. В вашем случае это эквивалентно 'def myfun(): self.controller.set_value_one()', а затем 'command = myfun' – MrMobster