Items inheriting attributes from its container?

K

Kreso

I would like to create a list-like container class so that, additionally
to usual list methods, I could attach attributes to the container instances.
However, I would like it so that the items contained in the particular
instance of container somehow 'inherit' those attributes i.e.

cont = Container()
cont.color = 'blue'
cont.append(item)
print item.color
'blue'

The example appended below does that, but with the restriction that container
attributes must be set in the instantiation phase. This is actually fine
for me at the moment because my objects are "read only", but I would
like to hear about better solutions, with more flexibility, please.


#-----8<----------------------------
class Player:
"""Class for items"""

def __init__(self, playerdata, team):
self.data = playerdata
for key in team.__dict__:
setattr(self, key, team.__dict__[key])
return


class Team(list):
"""Class for containers"""

def __init__(self, teamdata, playerdata):
for key in teamdata:
setattr(self, key, teamdata[key])
for item in playerdata:
self.append(Player(item, self))
return


lakersdata = {'name' : 'Lakers', 'kitcolor' : 'yellow'}
lakersplayers = [['Kobe', 'PG', 12, 123], ['Kareem', 'FW', 23, 345]]

lakers = Team(lakersdata, lakersplayers)

# This is fine:
p1 = lakers[1]
print p1.kitcolor

# However the following doesn't work:
lakers.kitcolor = 'blue'
print p1.kitcolor

#-----8<----------------------------
 
J

Jan Kaliszewski

23-08-2009 Kreso said:
I would like to create a list-like container class so that, additionally
to usual list methods, I could attach attributes to the container
instances.
However, I would like it so that the items contained in the particular
instance of container somehow 'inherit' those attributes i.e.

cont = Container()
cont.color = 'blue'
cont.append(item)
print item.color
'blue'
[snip]

class Player:
"""Class for items"""

def __init__(self, playerdata, team):
self.data = playerdata
for key in team.__dict__:
setattr(self, key, team.__dict__[key])
return


class Team(list):
"""Class for containers"""
def __init__(self, teamdata, playerdata):
for key in teamdata:
setattr(self, key, teamdata[key])
for item in playerdata:
self.append(Player(item, self))
return

lakersdata = {'name' : 'Lakers', 'kitcolor' : 'yellow'}
lakersplayers = [['Kobe', 'PG', 12, 123], ['Kareem', 'FW', 23, 345]]

lakers = Team(lakersdata, lakersplayers)
[snip]

p1 = lakers[1]
print p1.kitcolor
[snip]

lakers.kitcolor = 'blue'
print p1.kitcolor

[Not tested. I believe that the idea is clear]


class RecruitmentError(ValueError):
"""Raised when a player cannot be recruited."""


class Player(object):
"""A potential item of a Team() instance."""

def __init__(self, playerdata, team=None):
self.data = playerdata
self.team = None
if team:
team.append(self)

# (called when the usual attribute lookup didn't succeed)
def __getattr__(self, name):
return getattr(self.team, name)

# ^ the last method may not be necessary -- you can always
# access to team data via 'team' attribute
# (and 'explicit is better than implicit')


class Team(list):
"""A container for Player() instances."""

def __init__(self, teamdata, playerdata=None):
for key in teamdata:
setattr(self, key, teamdata[key])
for data in playerdata:
self.append(Player(data))

def _validate_and_get(self, index=None, player=None):
if player is not None and not isinstance(player, Player):
raise TypeError('A Player instance is required')
if index is not None:
if isinstance(index, slice):
raise TypeError('Slicing is not allowed')
return self[index] # (raise IndexError for bad index)

_validate = _validate_and_get

def _recruit(self, player):
if player.team is None:
player.team = self
else:
raise RecruitmentError('Player %s has already recruited')

def _dismiss(self, player):
player.team = None

def __setitem__(self, index, player):
current = self._validate_and_get(index, player)
if player is not current:
self._recruit(player)
self._dismiss(current)
list.__setitem__(self, index, player)

def __delitem__(self, index):
player = self._validate_and_get(index)
self._dismiss(player)
list.__delitem__(self, index)

def append(self, player):
self._validate(player=player)
self._recruit(player)
list.append(self, player)

# and similarly:
# * def extend...
# * def insert...
# * def pop...
# * def remove...

....if you really need ordered container (reflecting functions/hierarchy
of players in a team?).

Otherwise, as Stephen noted, you should subclass set rather than list.

Cheers,
*j
 
J

Jan Kaliszewski

PS. Erratum:
class Team(list):
"""A container for Player() instances."""

def __init__(self, teamdata, playerdata=None):

*** Here should be added: list.__init__(self) ***
for key in teamdata:
setattr(self, key, teamdata[key])
for data in playerdata:
self.append(Player(data))
 
B

Bruno Desthuilliers

Kreso a écrit :
I would like to create a list-like container class so that, additionally
to usual list methods, I could attach attributes to the container instances.
However, I would like it so that the items contained in the particular
instance of container somehow 'inherit' those attributes i.e.

cont = Container()
cont.color = 'blue'
cont.append(item)
print item.color
'blue'

This is named "environmental aquisition" (or simply "aquisition") and is
one of the base mechanisms in Zope 2. While an interesting idea, from
experience, it can rapidly turn into a total maintainance nightmare if
not used with care :-/
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,772
Messages
2,569,588
Members
45,100
Latest member
MelodeeFaj
Top