2015-11-23 48 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