python: 字典,類與 "switch"

  python中是沒有switch語法的,我在練習的時候想使用相似switch的功能,搜索相關內容知道了使用字典能夠完成我想要的步驟。因而,開始動手。python

  我使用的是python3,而且在練習使用tkinter模塊寫個小遊戲:乒乓球。測試階段,首先我敲入:canvas

from tkinter import *

從而加載tkinter模塊,並使用 * 使得在以後的代碼輸入中能夠稍打一些代碼。在這以後,我構想在建立一個canvas類變量,並在上面畫一個矩形,經過左右方向鍵控制矩形移動從而模擬球拍。測試的完整代碼爲:函數

 1 from tkinter import *
 2 
 3 
 4 def move2right(dis):
 5     canvas.move(1, dis, 0)
 6 
 7 
 8 def move2left(dis):
 9     canvas.move(1, -dis, 0)
10 
11 movement = {'Right': move2right, 'Left': move2left}
12 
13 
14 def move(event):
15     movement.get(event.keysym)(8)
16 
17 tk = Tk()
18 canvas = Canvas(tk, width=500, height=500)
19 canvas.pack()
20 canvas.create_rectangle(100, 100, 200, 120)
21 canvas.bind_all('<KeyPress-Right>', move)
22 canvas.bind_all('<KeyPress-Left>', move)
23 
24 tk.mainloop()

以上測試代碼的結果很順利:使用字典變量movement將兩個函數的指針存入字典中。21,22行綁定左右建的觸發事件給函數move(),由於bind_all()方法中傳遞的函數要有一個參數(event),在bind_all()方法內部會將其設置爲一個event類,從而存儲觸發的事件內容,由於以上緣由,我定義了move()函數來封裝movement.get()方法。15行中,.get()方法能夠經過索引key獲取相應的value,event.keysym爲bind_all()傳遞給move()函數的event類中的元素,是一個表明相應鍵盤按鍵的字符串(這裏有效的是'Right' 和 'left')。15行最後的 (8) 是函數的參數。oop

  在測試成功後,我將這種方法移到了測試乒乓球和球拍反彈的文件中,這裏面有乒乓球的Ball類和球拍Paddle類。先貼上最後能夠運行的代碼:測試

 1 # !/usr/bin/env python3
 2 # -*-coding=utf-8-*-
 3 
 4 from tkinter import *
 5 import time
 6 
 7 
 8 class Ball:
 9     def __init__(self, canvas, color, paddle):
10         self.canvas = canvas
11         self.canvas_height = self.canvas.winfo_height()
12         self.canvas_width = self.canvas.winfo_width()
13         self.paddle = paddle
14         self.id = canvas.create_oval(10, 10, 25, 25, fill=color)
15         self.canvas.move(self.id, 245, 100)
16         self.hit_bottom = False
17         self.x_pixel_of_one_step = 0  # 小球橫向速度
18         self.y_pixel_of_one_step = -3  # 小球縱向速度
19 
20     def hit_paddle(self, pos):
21         paddle_pos = self.canvas.coords(self.paddle.id)
22         if paddle_pos[0] <= (pos[0] + pos[2])/2.0 <= paddle_pos[2]:
23             if paddle_pos[1] <= pos[3] < paddle_pos[3] and self.y_pixel_of_one_step > 0:
24                 return True
25             if paddle_pos[1] < pos[1] <= paddle_pos[3] and self.y_pixel_of_one_step < 0:
26                 return True
27         return False
28 
29     def draw(self):
30         ball_pos = self.canvas.coords(self.id)  # 提取目前小球的位置
31         # 碰撞到上下邊反彈參數設置
32         if ball_pos[1] <= 0 or ball_pos[3] >= self.canvas_height:
33             self.y_pixel_of_one_step = -self.y_pixel_of_one_step
34         # 碰撞到左右邊反彈參數設置
35         if ball_pos[0] <= 0 or ball_pos[3] >= self.canvas_width:
36             self.x_pixel_of_one_step = -self.x_pixel_of_one_step
37         # 碰撞到球拍反彈參數設置
38         if self.hit_paddle(ball_pos):
39             self.y_pixel_of_one_step = -self.y_pixel_of_one_step
40         # 移動小球
41         self.canvas.move(self.id, self.x_pixel_of_one_step, self.y_pixel_of_one_step)
42 
43 
44 class Paddle:
45     def __init__(self, canvas, color):
46         self.canvas = canvas
47         self.canvas_height = canvas.winfo_height()
48         self.canvas_width = canvas.winfo_width()
49         self.id = canvas.create_rectangle(0, 0, 100, 10, fill=color)
50         self.canvas.move(self.id, 250, 250)
51         self.canvas.bind_all('<KeyPress-Right>', self.move)
52         self.canvas.bind_all('<KeyPress-Left>', self.move)
53         self.x_pixel_of_one_step = 0
54         self.y_pixel_of_one_step = 0
55 
56     def move2left(self):
57         paddle_pos = self.canvas.coords(self.id)
58         if paddle_pos[0] >= 0:
59             self.canvas.move(self.id, -5, 0)
60 
61     def move2right(self):
62         paddle_pos = self.canvas.coords(self.id)
63         if paddle_pos[2] <= self.canvas_width:
64             self.canvas.move(self.id, 5, 0)
65 
66     movement = {'Right': move2right, 'Left': move2left}
67 
68     def move(self, event):
69         self.movement.get(event.keysym)(self)
70         # print(self.movement[event.keysym])
71 
72     def draw(self):
73         pass
74 
75 
76 tk = Tk()
77 tk.title('Fuck The Ping-Pang')
78 tk.resizable(0, 0)  # 限制畫布不能伸縮
79 tk.wm_attributes('-topmost', 1)
80 canvas = Canvas(tk,width=500, height=500, bd=0, highlightthickness=0)
81 # canvas = Canvas(tk, width=500, height=500)
82 canvas.pack()
83 tk.update()
84 paddle = Paddle(canvas, 'black')
85 ball = Ball(canvas, 'black', paddle)
86 
87 while True:
88     ball.draw()
89     tk.update_idletasks()
90     tk.update()
91     time.sleep(0.01)

一切正常,除了56~70行。這幾行正是我加入的剛剛所示的方法。我糾結的地方在第69行。這一行的內容我百思不得其解。spa

  對於類方法的定義,全部類方法的第一個參數必須是一個self(名稱可變)參量,這個參量默認指向方法說在的類,便於方法內部相關內容的編寫。而在使用方法時,這個self參量是隱藏的,也就是說這個參量並不會對外可見。例如Paddle類中的move2left()函數(56行),在類方法定義中使用move2left()函數時,只須要寫self.move2left(),內部的self參數不須要寫。可是在69行上,我必需要在參數中加入self才行。重溫一下69行:指針

 

self.movement.get(event.keysym)(self)

 

這裏的self參數是move()方法中的默認參數self,指向所在的Paddle類,是一個函數指針。爲何要顯示地寫入這個參數呢?code

  在字典movement中保存了兩個內容,分別是 move2left 函數和 move2right 函數的指針。那麼在使用 movement.get('Right') 時,至關於返回了 move2right 。那麼,movement.get('Right')() 這個代碼就至關於move2right() 這個函數的調用,最後的括號表示前面的內容是一個函數,不加的話運行確定是過不去的(不能用編譯兒,由於python是解釋性語言)。對於非類方法的函數,在函數定義時形參中沒有設定默認值的參數,在調用這個函數時,必需要在相應的形參位置上傳入實參,若是沒有傳入足夠多的實參,那麼Python解釋器就沒法正確運行這個函數從而報錯。對於類方法中的函數,由於Python解釋器對類的 」特別對待「,因此在調用類方法的時候,解釋器會自動 「跨過」 類方法中第一個默認指向自身類的指針的變量。blog

  可是糾結的地方來了。在使用字典方法get()時,get 方法僅僅返回字典中對應鍵(key)的值(value)。在這裏返回的是move2left或者move2right函數的地址。解釋器運行到這裏並從get()方法中出來後,獲得了一個函數指針。然而此時解釋器已經不知道這個指針是不是類中的方法,它只知道這個函數指針所指向的函數須要一個參數self,因此在與後面的括號()結合並解釋成一個函數時,它須要相應個數的實參,因此此時不能忽略那個self參數的輸入。索引

  問題又來了,python中是隱藏數據類型的。而且在這個例子中,move2left 和 move2right 的self參量都沒有使用,這時我可不能夠不傳入self 而傳入別的參數,例如: self.movement.get(event.keysym)(20)。雖然這樣是無心義的,可是與上一段python解釋器的行爲並不相沖突。恩,結果卻是沒有懸念:運行出錯。運行出錯,那麼緣由基本只有一個:在解釋器將get()方法與後面的括號結合起來並解釋爲函數時,解釋器將這個函數解釋爲非類方法,將其做爲普通函數,檢測它的參數傳遞個數與定義個數是否一致。在檢測參數一致後,程序將由函數指針進入這個函數,而後發現這個函數居然是一個類方法!那麼它就再檢查一次向函數傳遞的參數,這時候發現傳遞給函數的參數有一個—— 20,而這個類方法的定義的參數只有一個默認的self。解釋器接受這個事實,並把20賦值給self。由於腳本運行,程序繼續跑(例如以前按的是左鍵,那麼跳入的是move2left()方法)。在move2left 方法中使用了self.canvas.move()方法,解釋器這時候發現,self等於20!它不是一個類!好吧好吧,報錯吧那就!

  結案。

相關文章
相關標籤/搜索