Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I create a tuple with an attribute using Python?

I have a class WeightedArc defined as follows:

class Arc(tuple):

  @property
  def tail(self):
    return self[0]

  @property
  def head(self):
    return self[1]

  @property
  def inverted(self):
    return Arc((self.head, self.tail))

  def __eq__(self, other):
    return self.head == other.head and self.tail == other.tail

class WeightedArc(Arc):
  def __new__(cls, arc, weight):
    self.weight = weight
    return super(Arc, cls).__new__(arc)

This code clearly doesn't work, because self isn't defined for WeightArc.__new__. How do I assign the attribute weight to the WeightArc class?

like image 840
Ceasar Bautista Avatar asked Oct 24 '11 06:10

Ceasar Bautista


2 Answers

The fixed-up version of your original code is:

class WeightedArc(Arc):
    def __new__(cls, arc, weight):
        self = tuple.__new__(cls, arc)
        self.weight = weight
        return self

Another approach to look at the verbose option for collections.namedtuple to see an example of how to subclass tuple:

>>> from collections import namedtuple, OrderedDict
>>> _property = property
>>> from operator import itemgetter as _itemgetter
>>> Arc = namedtuple('Arc', ['head', 'tail'], verbose=True)
class Arc(tuple):
    'Arc(head, tail)' 

    __slots__ = () 

    _fields = ('head', 'tail') 

    def __new__(_cls, head, tail):
        'Create new instance of Arc(head, tail)'
        return _tuple.__new__(_cls, (head, tail)) 

    @classmethod
    def _make(cls, iterable, new=tuple.__new__, len=len):
        'Make a new Arc object from a sequence or iterable'
        result = new(cls, iterable)
        if len(result) != 2:
            raise TypeError('Expected 2 arguments, got %d' % len(result))
        return result 

    def __repr__(self):
        'Return a nicely formatted representation string'
        return 'Arc(head=%r, tail=%r)' % self 

    def _asdict(self):
        'Return a new OrderedDict which maps field names to their values'
        return OrderedDict(zip(self._fields, self)) 

    def _replace(_self, **kwds):
        'Return a new Arc object replacing specified fields with new values'
        result = _self._make(map(kwds.pop, ('head', 'tail'), _self))
        if kwds:
            raise ValueError('Got unexpected field names: %r' % kwds.keys())
        return result 

    def __getnewargs__(self):
        'Return self as a plain tuple.  Used by copy and pickle.'
        return tuple(self) 

    head = _property(_itemgetter(0), doc='Alias for field number 0')
    tail = _property(_itemgetter(1), doc='Alias for field number 1')

You can cut, paste, and modify this code, or just subclass from it as shown in the namedtuple docs.

To extend this class, build off of the fields in Arc:

WeightedArc = namedtuple('WeightedArc', Arc._fields + ('weight',))
like image 132
Raymond Hettinger Avatar answered Nov 15 '22 08:11

Raymond Hettinger


Another approach to look at the verbose option for collections.namedtuple to see an example of how to subclass tuple

Better yet, why not use namedtuple ourselves? :)

class Arc(object):
    def inverted(self):
        d = self._asdict()
        d['head'], d['tail'] = d['tail'], d['head']
        return self.__class__(**d)

class SimpleArc(Arc, namedtuple("SimpleArc", "head tail")): pass

class WeightedArc(Arc, namedtuple("WeightedArc", "head tail weight")): pass
like image 24
Karl Knechtel Avatar answered Nov 15 '22 08:11

Karl Knechtel