使用装饰器跟踪和返回方法

时间:2017-09-14 19:46:08

标签: python python-3.x

我正在尝试定义一个标记某些方法的装饰器,然后提供通过方法调用所有标记方法的功能。

以下内容应该让我知道我喜欢一切行为,但由于显而易见的原因而无法正常运作

def mark(method):
    # mark_methods should belong to the class wherein methods are being marked
    mark_methods.append(method)
    return method

class BaseClass():
    # This should belong to each subclasses independently
    marked_methods = []

    def bundle(self):
        data = {}
        for marked_method in Person.marked_methods:
            data[marked_method.__name___] = marked_method()
        return data

class Person(BaseClass):
    @mark
    def name(self):
        return 'Fred'

    def say_hi(self):
        return 'Hi'

class Dog(BaseClass):
    @mark
    def name(self):
        return 'Fido'

    @mark
    def fur_color(self):
        return 'Black'

    def bark(self):
        return 'Woof'

Person.marked_methods # => [name]
Person().bundle() # => {'name': 'Fred'}

Dog.marked_methods # => [name, fur_color]
Dog().bundle() # => {'name': 'Fido', 'fur_color': 'Black'}

理想情况下,此行为将包含在可由其他类继承的类中。

这是我正在寻找的一个版本,它表现出类似的行为而不使用装饰器。相反,它依赖于操纵以特定前缀mark_whatever开头的函数:

MARK_PREFIX = 'mark_'

class TrackingWithoutDecoratorClass():
    @classmethod
    def __init_subclass__(cls, **kwargs):
        """Tracks all methods added on init
        """

        # Mapping of transformer names to transformer functions
        cls.marked = {}

        for name, func in vars(cls).items():
            # Add all functions starting with the `MARK_PREFIX` to the
            # marked registry
            if name.startswith(MARK_PREFIX):
                registry_name = name.replace(MARK_PREFIX, '')
                cls.marked[registry_name] = func


class Example(TrackingWithoutDecoratorClass):
  def mark_one(self):
    return 1
  def mark_two(self):
    return 2
  def not_marked_three(self):
    return 3

print(Example.marked.keys())

这是a working REPL

2 个答案:

答案 0 :(得分:1)

我建议自己标记方法:

import inspect

def mark(func):
    func.marked = True
    return func

class Base():
    @classmethod
    def marked_methods(cls):
        return [n for n, f in inspect.getmembers(cls) if hasattr(f, 'marked')]

    def bundle(self):
        return {m: getattr(self, m)() for m in self.marked_methods()}

class Person(Base):
    @mark
    def name(self):
        return 'Fred'

    def say(self):
        return 'Hi'

class Dog(Base):
    @mark
    def name(self):
        return 'Fido'

    def bark(self):
        return 'Woof'

    @mark
    def color(self):
        return 'Black'

print(Person.marked_methods()) #=> ['name']
print(Person().bundle())       #=> {'name': 'Fred'}
print(Dog.marked_methods())    #=> ['color', 'name']
print(Dog().bundle())          #=> {'color': 'Black', 'name': 'Fido'}

答案 1 :(得分:0)

诀窍是,一旦目标类从基类继承,就使用元类来存储修饰的方法:

def mark(method):
    method.marked = True
    return method

class MarkTracking(type):
    def __new__(cls, name, bases, attr):
        marked = []
        for obj in attr.values():
            if hasattr(obj, 'marked'):
                marked.append(obj)
        attr['marked_methods'] = marked
        return type.__new__(cls, name, bases, attr)

class BaseClass(metaclass=MarkTracking):
    def bundle(self):
        data = {}
        for marked_method in self.__class__.marked_methods:
            data[marked_method.__name__] = marked_method(self)
        return data

class Person(BaseClass):
    @mark
    def name(self):
        return 'Fred'

    def say_hi(self):
        return 'Hi'

class Dog(BaseClass):
    @mark
    def name(self):
        return 'Fido'

    @mark
    def fur_color(self):
        return 'Black'

    def bark(self):
        return 'Woof'

print(Person.marked_methods) # => [name]
print(Person().bundle()) # => {'name': 'Fred'}

print(Dog.marked_methods) # => [name, fur_color]
print(Dog().bundle()) # => {'name': 'Fido', 'fur_color': 'Black'}