2014-11-06 62 views
0

也許標題有點誤導,但是我想創建一個簡單的裝飾器來在RPC機制中將某些類方法裝飾爲「允許」,但是我被卡住了一個奇怪的錯誤當試圖訪問類變量(Python 2.7.5)時。檢查下面的代碼:使用裝飾器時,Python類不是全局變量

class myclass(): 

    rpcallowedmethods = [] 

    def __init__(self):    
     pass 

    def rpcenabled(fn): 
     print fn 
     print globals() 
     print myclass 

    @rpcenabled 
    def somefunc(self,param): 
     pass 

c = myclass() 

例外:NameError: global name 'myclass' is not defined

任何人都可以解釋這背後的原因嗎?

編輯: 我在問的是更多的事實,即python執行在類中定義的裝飾器,並運行在裝飾類方法之前甚至在全局類中,所以我相信它更多的是一個邏輯「錯誤「在python實現方面比看似明顯NameError

+2

'rpcenabled'裝飾者沒有任何意義。首先,它不應該被定義在你的類中,其次,'NameError'來自'rpcenabled'調用中的print語句,因爲當裝飾器被執行時(myclass)還沒有定義類對象的_creation_ - 你的類正在被編譯並且不存在)。你的'print globals()'應該向你展示了這一點。你真的想讓裝飾者做什麼? – l4mpi 2014-11-06 13:58:45

+0

我不知道python有這樣一個選擇性解析器,因爲它已經在類的定義中了,我認爲它在執行任何裝飾方法之前必須解析它... – 2014-11-06 14:02:18

+0

那麼,解析器在創建類時基本上就像這樣工作:它創建一個空的類對象,獲取屬於類定義的所有代碼,通過執行所有類級別的語句(創建函數,執行函數裝飾器,分配類變量)填充類對象,將填充的類對象傳遞給任何類裝飾者和_then_將結果分配給surronding範圍中的類的名稱。這項任務是這個過程中的最後一步。在你的具體情況下,只需將你的'rpcallowedmethods'列表傳遞給裝飾器,而不是使用類。 – l4mpi 2014-11-06 14:15:19

回答

1

實際的類對象僅在之後分配給它的名稱其定義完成。因此,在定義過程中不能使用類名稱。您可以創建一個類外的裝飾給你明確地傳遞要填充的列表,或使用以下命令:

class myclass(): 
    rpcmethods = [] 

    def _rpcallowed(fct, l=rpcmethods): 
     l.append(fct) 
     return fct 

    @_rpcallowed 
    def myfct(): pass 

注意,默認參數(l=rpcmethods)是一個解決方法,因爲你不能訪問一個函數內部沒有對類或實例的引用的類變量。

與裝飾外的類的變體很可能將有資格爲「乾淨」比這個,因爲它是明確的和可重複使用,但它會更多的代碼並不太具體。

+0

這很好,經過一些測試後,幾個班完成實施,這將變成最乾淨和最簡單的解決方案,謝謝。 – 2014-11-08 20:29:34

0

奇怪的錯誤?

print myclass 

引起的錯誤。您的定義中不能使用名稱myclass ...

+0

添加了打印以演示該問題,實際上代碼更像是'myclass.rpcallowedmethods.append(fn .__ name __)' – 2014-11-06 14:03:54

+0

@MartinoDino如果要使用該類,則定義一個'@ classmethod'及其第一個參數是'cls',這是班級。 – laike9m 2014-11-06 14:07:15

1

你濫用裝飾。裝飾器是爲了給事物添加東西。以某種方式「裝飾」它。

更常見的做這種事情的方法是裝飾方法和類。元類是解決這個問題的另一種方法。他們更強大,但是對於你當前的問題來說是過度的。但是,直接裝飾功能可能是你需要做的。在保存代理時保存整理rpc函數。

from types import FunctionType 

def enable_rpc(func): 
    func.rpc_enabled = True 
    return func 

def rpc_enabled_class(cls): 
    functions = [attr for attr in vars(cls).values() 
     if isinstance(attr, FunctionType)] 
    cls._rpc_enabled_methods = [ 
     func for func in functions 
      if getattr(func, "rpc_enabled", False) 
    ] 
    return cls 

@rpc_enabled_class 
class SampleClass(object): 

    @enable_rpc 
    def my_func(self): 
     pass 

print(SampleClass._rpc_enabled_methods)