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



我创建了一个子类,它将单个参数从父级扩展到*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)

虽然,我所有的尝试都产生了错误,因为装饰器无法"找到"self.args.


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

class Greet(_greet):
def __init__(self,*names):
self.names = names
@args(names)
def hello(self, var=name):
super()._hello(var)

首先,如果您可以使用所需的行为扩展父类,那就完美了。

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

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

这是适配器解决方案:

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是独立类,你可以做的一个好方法:

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 - 如果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']

您可以阅读有关描述符,有关__getattr__,有关super类的更多信息。还有一些基于__init_subclass__的方法

最新更新