2015-11-23 51 views
3

我想弄清楚如何區分python模塊級函數和尚未綁定到對象實例的方法。當函數是一個未綁定的實例方法時,我需要一個定義該方法的類的句柄。 inspect.ismethod()不起作用,因爲該方法尚未綁定到對象實例。 type()爲模塊級函數和未綁定方法返回「函數」。區分python函數和類函數

用例是一個使用方法和函數裝飾器的插件框架。我有一個通用的'模型'類,可以分類添加功能。使用@register_action(name)裝飾器在子類中定義插件「操作」。在導入時調用裝飾器函數來註冊動作。 gui然後爲字典中的每個ACTION添加菜單項 - 但是gui應該只添加一個動作,如果gui的Model類是註冊了函數/方法的類的實例。

一些示例代碼如下。現在我的解決方案是解析fcn描述符的文本,然後檢查是否class_str == str(model.__class__).split('.')[-1].strip("'>")。我也嘗試將SpecialModel傳遞給裝飾器,但是當裝飾器函數運行時SpecialModel沒有定義,所以不起作用。註冊類方法操作必須有更好的解決方案。

注:因爲它是不可能通過SpecialMethodregister_action裝飾,因爲SpecialMethod沒有定義,我可能需要調用get_class_from_function當談到時間來註冊與GUI的操作。

ACTIONS = dict() 

### HACK -- best I could do, but want actual Class, not class_name ### 
# FIXME : want to return None, Model, SpecialModel, etc. 
def get_class_from_function(fcn): 
    qname = fcn.__qualname__.split('.') 
    if len(qname) > 1:  # this is (likely) a class function 
     cls_name = qname[0] 
     return cls_name 
    else: 
     return None 

def register_action(name): 
    def register_decorator(fcn): 
     global ACTION 
     cls = get_class_from_function(fcn) 
     ACTION[ name ] = (fcn, cls) 
     return fcn 
    return register_decorator 

@register_action("Help/About") 
def help_about_function(model): 
    pass 

class Model(object): 
    def __init__(self): 
     pass 

    @register_action("File/Load") 
    def load(self): 
     pass 

class SpecialModel(Model): 
    @register_action("Special/Custom Action") 
    def specialact(self): 
     pass 
+1

你有沒有考慮檢查是否第一參數是'self'(例如'args = inspect.getargspec(thing).args; args和args [0] =='self'')?這不是保證,但是一個很好的指示。 – jonrsharpe

+1

我相信在Python 3中,自由函數和類定義中定義的函數沒有區別。事實上,你可以在模塊級定義一個函數,然後將它「粘貼」到類中,它就像它被定義爲一個方法一樣工作。也許這不是最好的方法......也許應該由班級完成註冊方法的操作。也就是說,無論何時定義一個'Model',即它的方法應該被註冊。這可以使用類裝飾器或者元類來完成。 – jme

+0

* class method *是一門藝術術語。據我所知,你對這個術語的使用是不正確的,只是混淆了這個問題。 – 7stud

回答

3

我想你可以實現你的最終像這樣的裝飾者的目標:

def register_action(name): 
    def register_decorator(fcn): 
     fcn._action_name = name 
     return fcn 
    return register_decorator 

再後來就在你的GUI代碼時,你實際上有對象實例,迭代的實例方法和尋找任何與_action_name,併爲它創建特定的GUI代碼:

for method_name, method in inspect.getmembers(my_specific_instance, predicate=inspect.ismethod): 
    if hasattr(method, '_action_name'): 
     add_to_gui_menu(method._action_name, method) 
+0

時,該對象還沒有添加到模塊中,謝謝,這可以工作(在小的語法修復之後)。 'inspect.getmembers()'返回'(mname,method)'元組。另外,將'__action_name__'更改爲'_action_name',因爲尾部'__'是針對python特殊的,並且領先的'__'將變量名稱 – swinman

2

這將有助於區分在類中定義的功能模塊中定義的函數:

import sys 
import types 

def is_module_func(obj): 
    mod = sys.modules[obj.__module__] 
    return isinstance(obj, types.FunctionType) and hasattr(mod, obj.__name__) 

現在:

class A: 
    def meth(self): 
     pass 

def func(): 
    pass 

>>> is_module_func(func) 
True 
>>> is_module_func(A.meth) 
False 
+0

謝謝 - 不幸的是,這不能在@register修飾器中工作,因爲hasattr(mod,obj .__ name__)返回false,當模塊和函數具有相同的名稱 – swinman

+0

以澄清:hasattr返回false當裝飾器運行 – swinman