2012-09-04 103 views
5

最近我一直在做這樣的事情:代碼風格 - 「隱藏」功能之外的功能裏面

import Tkinter 
class C(object): 
    def __init__(self): 
     self.root = Tkinter.Tk() 
     def f(): 
      print 'hello' 
     self.button = Tkinter.Button(master=self.root, command=f, text='say hello') 

,而不是像這樣:

import Tkinter 
class C(object): 
    def __init__(self): 
     self.root = Tkinter.Tk() 
     self.button = Tkinter.Button(master=self.root, command=self.f, text='say hello') 
    def f(self): 
     print 'hello' 

的問題是不特定到Tkinter,但這是一個很好的例子。功能f僅用作該按鈕的回調,因此我選擇在__init__內定義它。這樣,只有__init__內的代碼甚至知道f的存在 - 外部範圍不會開始混淆名稱,並且用戶不需要關心設計爲內部方法的負載。

我的問題是:這是否被視爲好風格?我很擔心,因爲我有一個有很多按鈕的GUI類 - __init__開始看起來很長,有很多本地函數定義。有沒有更合適的選擇,我應該使用?

回答

8

Tkinter的上下文中執行類似操作的典型方法是使用lambda函數。

self.button = Tkinter.Button(master=self.root, 
          command=lambda:sys.stdout.write("Hello!\n"), 
          text='say hello') 

在它的核心,這實在是與您的第一個例子,雖然,所以如果你喜歡第一種方式 - 去用它。我認爲在這種情況下創建一個新方法通常不是慣用的(除非實際需要將實例傳遞給回調 - 在這種情況下,您應該使用作爲第二種方法)。

這裏有兩件事值得擔心。首先是可讀性。如果__init__變得太雜亂無法閱讀,那麼你有一個問題。您可以將這些函數移動到模塊級別(前綴爲_,以防止在from module import *上下文中導入),並使用lambda將局部變量作爲參數綁定到這些函數(如有必要)。

如果__init__沒有變得混亂,那麼隨意放置功能。在那裏有功能確實意味着一個新函數在每次創建新實例時創建(與lambda相同),我認爲就內存而言可能是浪費的,但它不應該是一個大的交易(特別是在gui中)。

第二件要擔心的是名稱空間混亂。但是,如果你的模塊太大以至於將這些本地函數移動到模塊級函數時會產生命名空間問題,那麼你的模塊太大了,無法啓動。只要你在函數前面加下劃線(見上面的建議),你就不應該在其他文件中導入這個模塊的命名空間問題。

+2

當然,你是對的,但在我的例子的實際版本中,'f'不僅僅是'print'hello''。它不適合lambda函數。 –

+0

@poorsod - 如果它沒有使用實例,那麼它不應該是一個方法。你可以使它成爲一個模塊級別的函數,或者你可以像現在一樣繼續這樣做,但是如果你不需要函數中的實例,我不會在這裏使用一個方法(甚至是一個靜態方法)。 – mgilson

+1

@mgilson:我想你會錯過這個問題的重點,即回調是否應該是非本地功能。您在Tkinter回調中經常看到lambda的原因是將本地參數綁定到回調函數。回調通常在其他地方定義。 –

2

在這種情況下,它是無害的。整體而言,雖然,我會避免它的原因有兩個:

1)回調作爲類成員的定義是較爲可讀的(特別是當你使用是pydoc調查)

2)在另一個內部創建函數會引入更多閉包(從調用上下文繼承的變量)。

+1

進一步做這件事並在另一個模塊中定義回調將gui與應用程序邏輯分開並不會有什麼壞處。 –