Я пытаюсь перегрузить метод __add__
для экземпляров namedtuple, и у меня возникают проблемы.
Параметры, вводимые в мои namedtuples, генерируются динамически. Четыре параметра всегда одни и те же и в том же порядке, а остальные могут быть любыми и в любом количестве. Поэтому мне нужно иметь возможность динамически определять мою фабрику классов namedtuple. И после того, как я создам несколько экземпляров, я хотел бы иметь возможность добавить их вместе в новый экземпляр namedtuple со всеми уникальными параметрами вместе. Но у меня возникли проблемы с правильной перегрузкой метода __add__
. Кажется, это не работает.
Так, например, если у меня есть 3 экземпляра namedtuple
e = Row(a=1, b=2, c=3, d=4)
m = Row(a=1, b=2, c=3, d=4, param1='a', param2='b')
t = Row(a=1, b=2, c=3, d=4, param3='val', param4=10)
Я хотел бы иметь возможность добавлять их, например e + m + t
, который возвращает
Row(a=1, b=2, c=3, d=4, param1='a', param2='b', param3='val', param4=10)
Вот мой текущий код
class Row(object):
''' Creates a new namedtuple object '''
__slots__ = ()
def __new__(cls, *args, **kwargs):
''' make a new Row instance '''
default = namedtuple('Row', 'a, b, c, d')
newcols = set(args) - set(default._fields)
finalfields = default._fields + tuple(newcols) if newcols else default._fields
return namedtuple('Row', finalfields)
def __add__(self, other):
''' This is the new add '''
self_dict = self._asdict()
other_dict = other._asdict()
self_dict.update(other_dict)
new_fields = tuple(self_dict.keys())
new_row = namedtuple('Row', new_fields)
return new_row(**self_dict)
Благодаря этому я могу правильно динамически генерировать новые namedtuples и создавать их экземпляры.
e = Row()
m = Row(*['a', 'b', 'c', 'd', 'param1', 'param2'])
e._fields
('a', 'b', 'c', 'd')
m._fields
('a', 'b', 'c', 'd', 'param1', 'param2')
e2 = e(1, 2, 3, 4)
m2 = m(1, 2, 3, 4, 'a', 'b')
e2
Row(a=1, b=2, c=3, d=4)
type(e2)
__main__.Row
m2
Row(a=1, b=2, c=3, d=4, param1='a', param2='b')
но когда я добавляю их, мой перегруженный __add__
никогда не вызывается, и я, кажется, просто возвращаю обычный объект кортежа
w = e2 + m2
print(w)
(1, 2, 3, 4, 1, 2, 3, 4, 'a', 'b')
type(w)
tuple
Мой метод __add__
, похоже, не активен для моих объектов экземпляра.
Row.__add__?
Signature: Row.__add__(self, other)
Docstring: This is the new add
File: <ipython-input-535-817d9f528ae7>
Type: instancemethod
e.__add__?
Type: wrapper_descriptor
String form: <slot wrapper '__add__' of 'tuple' objects>
Docstring: x.__add__(y) <==> x+y
e2.__add__?
Type: method-wrapper
String form: <method-wrapper '__add__' of Row object at 0x122614050>
Docstring: x.__add__(y) <==> x+y
Что я делаю не так? Я также попытался создать подкласс namedtuple('Row', ...), как указано в документах https://docs.python.org/2/library/collections.html#collections.namedtuple, но я не смог заставить это работать. Я не мог заставить его динамически изменять именованные параметры.
Вот этот провал
BaseRow = namedtuple('BaseRow', 'a, b, c, d')
class Row(BaseRow):
__slots__ = ()
def __new__(cls, *args, **kwargs):
new_fields = set(kwargs.keys()) - set(cls._fields)
cls._fields += tuple(new_fields)
obj = super(Row, cls).__new__(cls, *args, **kwargs)
return obj
e = Row(a=1, b=2, c=3, d=4, param1='a')
TypeError: __new__() got an unexpected keyword argument 'param1'