Вопрос
Как вы процедурно создаете в Python функцию, которая принимает конкретные именованные аргументы, но позволяет этим именам аргументов управляться данными?
Пример
Скажем, вы хотите создать декоратор класса with_init
, который добавляет метод __init__
с конкретными именованными аргументами, так что следующие два класса эквивалентны.
class C1(object):
def __init__(self, x, y, z):
self.x = x
self.y = y
self.z = z
@with_init('x y z')
class C2(object):
pass
Моя первая попытка обмануть, сделав функцию, которая принимает *args
вместо определенных именованных параметров:
class with_init(object):
def __init__(self, params):
self.params = params.split()
def __call__(self, cls):
def init(cls_self, *args):
for param, value in zip(self.params, args):
setattr(cls_self, param, value)
cls.__init__ = init
return cls
Работает в некоторых ситуациях:
>>> C1(1,2,3)
<__main__.C1 object at 0x100c410>
>>> C2(1,2,3)
<__main__.C2 object at 0x100ca70>
Но не так много в других:
>>> C2(1,2,3,4) # Should fail, but doesn't.
<__main__.C2 object at 0x100cc90>
>>> C2(x=1, y=2, z=3) # Should succeed, but doesn't.
Traceback (most recent call last):
File "<string>", line 1, in <fragment>
TypeError: init() got an unexpected keyword argument 'y'
Конечно, я могу добавить код во вложенную функцию init
, чтобы попытаться проверить каждую возможную ситуацию, но, похоже, должен быть более простой способ.
Я заметил, что collections.namedtuple
позволяет избежать этих проблем, передав строку exec
. Мне это кажется очень круглым, но, возможно, в этом и есть решение.
Какая правильная реализация with_init.__call__
?
Примечание: я бы хотел решение Python 2.x, пожалуйста.