Python 使用decorator跟踪和返回方法

Python 使用decorator跟踪和返回方法,python,python-3.x,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()

我试图定义一个装饰器,它将标记某些方法,然后稍后提供按方法调用所有标记方法的功能

下面这篇文章应该让我知道我希望所有的东西都是如何运作的,但是由于明显的原因,它没有功能

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_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())

以下是。

我建议标记方法本身:

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'}

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

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'}

Person不必存在,您可以从方法引用获取类。请看,这似乎在语义上有所不同,两次调用同一个方法只会在列表中出现一次。如果海报真的想要跟踪调用,这是行不通的。因此,虽然这实现了预期的行为,但实现要求您在调用标记的方法时迭代所有函数,并确定哪些被标记,而不是存储在某种注册表中。我在问题中添加了一个不使用装饰器的示例。