在 class 方法中自动化 "for arg in self.args" 过程的装饰器

Decorator to automate "for arg in self.args" process in class methods

我创建了一个子项 class,它将单个参数从父项扩展到 *args 并产生输出。但是,使用装饰器看起来更干净,而不是在多个方法中编写 for arg in self.args: yield

# Parent class
class _greet:

    def _hello(self,name):
        return "hello " + name

    def _hey(self,name):
        return "hey " + name

# Child class
class Greet(_greet):

    def __init__(self,*names):
        self.names = names

    def hello(self):
        for name in self.names:
            yield super()._hello(name)

    def hey(self):
        for name in self.names:
            yield super()._hey(name)

不过,我的所有尝试都产生了错误,因为装饰器不能 "find" self.args.


编辑:
这背后的想法是得到类似的东西:

class Greet(_greet):

    def __init__(self,*names):
        self.names = names

    @args(names)
    def hello(self, var=name):
        super()._hello(var)

首先,如果您可以扩展您的父级 class 并使其具有所需的行为,那将是完美的。

其次,你应该思考一下行为延伸的意义。函数 _hellohello 根本不同。 _hello 需要 一个额外的 arg 和 returns 创建的输出 hello 没有额外的参数 并且 创建生成器 。那么您可能不需要创建子 class 吗?可能您需要创建绝对独立的 class(或新功能)?此外,您在 _greet 中的所有函数都不使用 self arg - 可能它们应该是静态的(通过 @staticmethod)?

第三,你确定你真的需要装饰器吗?我知道有大约 10 个习语可以模仿相同的行为。其中一些具有更好的生产力,其中一些需要少量代码。有些适用于多重继承 - 有些则不然。您提出的实现(如果我理解正确的话)看起来像 adapter pattern(有错误)。

这是适配器解决方案:

from itertools import repeat

class Greet(object):
    age = 666

    @staticmethod
    def hello(name):
        return f'hello {name}'

    @staticmethod
    def hey(name):
        return f'hey {name}'

    def say_age(self, name):
        return f'{name} is {self.age} years old'

def multiple_greet_adapter(adapter):
    return lambda self: map(adapter, repeat(self), self.names)

class MultipleGreet0(Greet):
    def __init__(self, *names):
        self.names = names

    @multiple_greet_adapter
    def hello_many(self, name):
        return super().hello(name)

    hey_many = multiple_greet_adapter(lambda self, name: super().hey(name))
    say_age_many = multiple_greet_adapter(lambda self, name: super().say_age(name))

这样实现的缺点之一是您仍然必须编写许多类似的代码。这也没有我们想要的那么高效。

方法 1 - 代码较少,但效率不高:

from functools import partial
class MultipleGreet1(Greet):
    def __init__(self, *names):
        self.names = names

    _corresponding_names = dict(
        hello_many = 'hello',
        hey_many = 'hey',
        say_age_many = 'say_age',
    )

    def __getattr__(self, attr_name):
        try:
            single_greet_handler = getattr(super(), self._corresponding_names[attr_name])
        except KeyError:
            raise AttributeError()
        else:
            return partial(map, single_greet_handler, self.names)

方法 2 - 相同,但带有描述符:

class ManyGreets(object):
    def __init__(self, attr_name):
        self._attr_name = attr_name

    def __get__(self, owner_inst, owner_cls):
        if owner_inst is None:
            return self
        else:
            return partial(map, getattr(super(owner_cls, owner_inst), self._attr_name), owner_inst.names)

class MultipleGreet2(Greet):
    def __init__(self, *names):
        self.names = names

    hello_many = ManyGreets('hello')
    hey_many = ManyGreets('hey')
    say_age_many = ManyGreets('say_age')

方法 3 - 如果 MultipleGreet 是独立的 class:

,您可以采用的好方法
def _create_many_greets(single_greet_handler, method=True):
    if method:
        return lambda self: map(single_greet_handler, repeat(self), self.names)
    else:
        return lambda self: map(single_greet_handler, self.names)

class MultipleGreet3(object):
    def __init__(self, *names):
        self.names = names
    age = 123

    hello_many = _create_many_greets(Greet.hello, False)
    hey_many = _create_many_greets(Greet.hey, False)
    say_age_many = _create_many_greets(Greet.say_age)

方法 4 - 我推荐的方法 if MultipleGreet 取决于 Greet:

class ManyGreetsCreator(object):
    def __init__(self, attr_name):
        self._attr_name = attr_name

    def __set_name__(self, owner_cls, set_name):
        attr_name = self._attr_name
        many_greets = lambda s: map(getattr(super(owner_cls, s), attr_name), s.names)
        setattr(owner_cls, set_name, many_greets)

class MultipleGreet4(Greet):
    def __init__(self, *names):
        self.names = names

    hello_many = ManyGreetsCreator('hello')
    hey_many = ManyGreetsCreator('hey')
    say_age_many = ManyGreetsCreator('say_age')

测试:

>>> mg0 = MultipleGreet0('Nick', 'John')
>>> mg1 = MultipleGreet1('Nick', 'John')
>>> mg2 = MultipleGreet2('Nick', 'John')
>>> mg3 = MultipleGreet3('Nick', 'John')
>>> mg4 = MultipleGreet4('Nick', 'John')
>>> list(mg4.hello_many())
['hello Nick', 'hello John']
>>> list(mg0.hello_many()) == list(mg1.hello_many()) == list(mg2.hello_many()) ==\
    list(mg3.hello_many()) == list(mg4.hello_many())
True
>>> list(mg0.say_age_many()) == list(mg1.say_age_many()) == list(mg2.say_age_many()) ==\
    list(mg4.say_age_many())
True
>>> list(mg4.say_age_many())
['Nick is 666 years old', 'John is 666 years old']
>>> list(mg3.say_age_many())
['Nick is 123 years old', 'John is 123 years old']

您可以阅读更多关于 descriptors, about __getattr__, about super-class. There are also approaches based on __init_subclass__