有没有办法标记特定的类方法?

时间:2017-11-29 18:32:16

标签: python python-3.x

基本上,我有一个方法课,其中一些我想用他们的类别标注。例如:

class ExampleClass:
    # category: foo
    def method_a(self):
        """does stuff"""

    # category: foo
    def method_b(self):
        """does stuff"""

    def method_c(self):
        """does stuff"""

    # category: bar
    def method_d(self):
        """does stuff"""

    # category: foo
    def method_e(self):
        """does stuff"""

现在我想编写一个单独的函数,它将查看类的命名空间并返回标记为特定类别的所有方法的名称。 (比方说,因为我想用特定标签运行所有方法。)

def find_methods(class, label):
    """Searches through the class namespace
       returns all methods with the correct label"""

例如,这是所需的输出:

find_methods(ExampleClass, 'foo')
# Returns ['method_a', 'method_b', 'method_e']

find_methods(ExampleClass, 'bar')
# Returns ['method_d']

find_methods(ExampleClass, 'baz')
# Returns []

我想知道是否有一种简单的方法来实现它。

要做的手工操作是在类顶部定义的变量中维护一个方法名称和类别列表,但这需要大量的维护,因为每次添加新方法时我都要记住更新我的清单。如果我有一个继承自ExampleClass但也有标签的类,那也会让人感到困惑。

3 个答案:

答案 0 :(得分:3)

编辑:更好的解决方案,请参阅下面的解释。

根据您的需要,在dir次调用中不要搜索find_methods类及其所有属性,这可能是合理的。这是一个案例,我将使标记功能更加智能,并在添加它们并存储到字典中时构建标记函数列表。 Find_methods只会从字典中检索函数列表。

E.g

class Tagger(object):
  def __init__(self):
    self._dict = {}

  def __call__(self, name):
    if name not in self._dict:
      self._dict[name] = []
    def tags_decorator(func):
      self._dict[name].append(func)
      func._tag = name
      return func
    return tags_decorator

tag = Tagger()
class ExampleClass:
  @tag('foo')
  def method_a(self):
    """does stuff"""

  @tag('foo')
  def method_b(self):
    """does stuff"""

  def method_c(self):
    """does stuff"""

  @tag('bar')
  def method_d(self):
    """does stuff"""

  @tag('bar')
  def method_e(self):
    """does stuff"""

print(tag._dict)

ORIGINAL POST:最终解决方案的解释。

我会扩展其他人给出的想法,但被删除了。他建议在每个函数中添加私有属性_tag。你可以这样做,因为Python中的所有东西都是一个对象。

所以:

class ExampleClass:
    # category: foo
    def method_a(self):
        """does stuff"""
    method_a._tag = "foo"

虽然这是脏代码,所以也许我们可以做得更好?例如。让我们用函数包装标记。

def add_foo_tag(func):
    func._tag = "foo"
    return func

class ExampleClass:
    # category: foo
    def method_a(self):
        """does stuff"""
    method_a = add_foo_tag(method_a)

但我们仍然可以做得更好:有装饰器语法@,为我们调用method_a = add_foo_tag(method_a)

class ExampleClass:
    # category: foo
    @add_foo_tag
    def method_a(self):
        """does stuff"""

但是其他标签字符串呢?让我们创建标签装饰工厂!

def tag(tag_name):
    def tags_decorator(func):
        func._tag = tag_name
        return func
    return tags_decorator

语法很好:

class ExampleClass:
    @tag('foo')
    def method_a(self):
        """does stuff"""

    @tag('foo')
    def method_b(self):
        """does stuff"""

    def method_c(self):
        """does stuff"""

    @tag('bar')
    def method_d(self):
        """does stuff"""

    @tag('bar')
    def method_e(self):
        """does stuff"""

现在,在课堂上找到方法是什么?我们可以使用dir操作。有一个明确的代码版本:

def find_methods_explicit(cls, label):
  """Searches through the class namespace
     returns all methods with the correct label"""
     attributes = [getattr(cls, func) for func in dir(cls)]
     tagged = [attr for attr in attributes if '_tag' in dir(atrr)]
     labeled = [attr for attr in tagged if  atrr._tag == label]
     return labeled

并且单行:

def find_methods(cls, label):
    """Searches through the class namespace
        returns all methods with the correct label"""
    return [getattr(cls, func) for func in dir(cls) if '_tag' in dir(getattr(cls, func)) and getattr(cls, func)._tag == label]

最后,用法:

print(ExampleClass.method_a._tag)    
print(find_methods(ExampleClass,'foo'))
print(find_methods(ExampleClass,'bar'))
print(find_methods(ExampleClass,'qwe'))

完整代码:

def tag(tag_name):
    def tags_decorator(func):
        func._tag = tag_name
        return func
    return tags_decorator

class ExampleClass:
    @tag('foo')
    def method_a(self):
        """does stuff"""

    @tag('foo')
    def method_b(self):
        """does stuff"""

    def method_c(self):
        """does stuff"""

    @tag('bar')
    def method_d(self):
        """does stuff"""

    @tag('bar')
    def method_e(self):
        """does stuff"""

def find_methods(cls, label):
    """Searches through the class namespace
        returns all methods with the correct label"""
    return [getattr(cls, func) for func in dir(cls) if '_tag' in dir(getattr(cls, func)) and getattr(cls, func)._tag == label]

print(ExampleClass.method_a._tag)

print(find_methods(ExampleClass,'foo'))
print(find_methods(ExampleClass,'bar'))
print(find_methods(ExampleClass,'qwe'))

在线示例:https://repl.it/repls/FrayedMilkyKangaroo

答案 1 :(得分:0)

for method_name in dir(ExampleClass):
   if callable(getattr(ExampleClass, method_name)):
       print(method_name)

修改

在一个函数中,这将是。

def find_methods(classobj, label):
    for method_name in dir(classobj):
       if method_name == label:
           if callable(getattr(classobj, method_name)):
               print(method_name)        

答案 2 :(得分:0)

你可以(误)使用类型注释:

def f(args) -> "category":
    # code

 f_category = f.__annotations__.get('return') # None if no category defined

或者你可以使用函数属性:

def f(args):
    # code

f.category = "category"

f_category = f.category

对于find_methods

def find_methods(class, label):
    for name in dir(class):
        possible_method = getattr(class, name)
        # test if it has the right label and is a function
        yield name # or append to a list and return