如何检查类方法是否返回自身?

时间:2019-03-31 16:28:31

标签: python python-3.x

我想列出给定类中返回self的所有方法。

我知道在Python中无法在运行之前检查函数的返回类型。

也许可以在函数体(源)中检查return self还是有其他方法?

我需要它才能与Python 3.5及更高版本一起使用

编辑:

此类(不是完整的代码)是gremlinpython库的一部分。

class GraphTraversal(Traversal):
    def __init__(self, graph, traversal_strategies, bytecode):
        super(GraphTraversal, self).__init__(graph, traversal_strategies, bytecode)
    def __getitem__(self, index):
        if isinstance(index, int):
            return self.range(long(index), long(index + 1))
        elif isinstance(index, slice):
            low = long(0) if index.start is None else long(index.start)
            high = long(sys.maxsize) if index.stop is None else long(index.stop)
            if low == long(0):
                return self.limit(high)
            else:
                return self.range(low,high)
        else:
            raise TypeError("Index must be int or slice")
    def __getattr__(self, key):
        return self.values(key)

    def V(self, *args):
        self.bytecode.add_step("V", *args)
        return self

    def addE(self, *args):
        self.bytecode.add_step("addE", *args)
        return self

    def addV(self, *args):
        self.bytecode.add_step("addV", *args)
        return self

它允许使用像这样的流利的API查询图形数据库

g.V().hasLabel('label').has('id','xxx').addE('relation').to(g.V().hasLabel('otherlabel').has('id','yyy')

到目前为止,我能够获得如下方法:

from inspect import getsourcelines, signature

def contains_return_self(f):   
   lines, _ = getsourcelines(f)   
   return any("return self" in line for line in lines)

def check_signature(f):
   sig = signature(f)
   if(len(sig.parameters) == 2 
      and 'self' in sig.parameters.keys()
      and 'args' in sig.parameters.keys()):
      return True
   return False

fluent_methods = [
   method_name for method_name in dir(GraphTraversal) 
      if callable(getattr(GraphTraversal, method_name)) 
         and ('__' not in method_name)
         and contains_return_self(getattr(GraphTraversal, method_name))
         and check_signature(getattr(GraphTraversal, method_name))]

我想返回所有具有以下签名的方法:

def foo(self, *args)
    # some code
    return self 

1 个答案:

答案 0 :(得分:1)

尽管与@RafaelC一样,我强烈怀疑这很可能是XY Problem,但有些东西(部分基于inspect模块)似乎可行(在这种方法的固有局限性内) 。为了进行测试,我添加了Traversal基类的定义以及它和派生的GraphTraversal类的一些不匹配方法。

from collections import namedtuple
import inspect
import re

class Traversal:
    def inherited_method1(self, *args):
        return self

    def inherited_method2(self, foobar):
        return foobar + 13

class GraphTraversal(Traversal):
    def __init__(self, graph, traversal_strategies, bytecode):
        super(GraphTraversal, self).__init__(graph, traversal_strategies, bytecode)
    def __getitem__(self, index):
        if isinstance(index, int):
            return self.range(long(index), long(index + 1))
        elif isinstance(index, slice):
            low = long(0) if index.start is None else long(index.start)
            high = long(sys.maxsize) if index.stop is None else long(index.stop)
            if low == long(0):
                return self.limit(high)
            else:
                return self.range(low,high)
        else:
            raise TypeError("Index must be int or slice")

    def __getattr__(self, key):
        return self.values(key)

    def non_match1(self, *args):
        self.bytecode.add_step("V", *args)
        return 42

    def non_match2(self, arg1, arg2):
        self.bytecode.add_step("V", *args)
        return self

    def V(self, *args):
        self.bytecode.add_step("V", *args)
        return self

    def addE(self, *args):
        self.bytecode.add_step("addE", *args)
        return self

    def addV(self, *args):
        self.bytecode.add_step("addV", *args)
        return self

### Introspect class
DUNDER = re.compile(r"^_{2,}\w*_{2,}\Z", re.UNICODE)
MethInfo = namedtuple('MethInfo', ['name', 'value'])

methods = [MethInfo(*pair) for pair in inspect.getmembers(GraphTraversal, inspect.isfunction)
                if not DUNDER.match(pair[0])]

def contains_return_self(meth_info):
    src = inspect.getsource(meth_info.value)
    for line in src.splitlines():
        if 'return self' in line.strip():
            return True
    else:
        return False

def check_signature(meth_info):
    sig = inspect.signature(meth_info.value)
    return str(sig) == '(self, *args)'

fluent_methods = [meth_info.name for meth_info in methods
                    if contains_return_self(meth_info) and check_signature(meth_info)]

print('fluent_methods:', fluent_methods)

输出:

fluent_methods: ['V', 'addE', 'addV', 'inherited_method1']