在 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 并使其具有所需的行为,那将是完美的。
其次,你应该思考一下行为延伸的意义。函数 _hello
和 hello
根本不同。 _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__
我创建了一个子项 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 并使其具有所需的行为,那将是完美的。
其次,你应该思考一下行为延伸的意义。函数 _hello
和 hello
根本不同。 _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__