Accessing container's methods

Peter Otten __peter__ at web.de
Mon Dec 7 13:59:54 EST 2015


Tony van der Hoff wrote:

> Hi,
> 
> I have a class A, containing embedded embedded classes, which need to
> access methods from A.
> .
> A highly contrived example, where I'm setting up an outer class in a
> Has-a relationship, containing a number of Actors. The inner class needs
> to access a method of the outer class; here the method get_name.
> 
> I don't really want to make Actor a sub-class (is-a; it isn't) of Monty;
> that would raise all sorts of other problems.
> 
> Can anyone please advise me on how to achieve this magic?
> 
> # define the outer class
> class Monty:
>    def __init__( self, names ):
>      self.actors = []
> 
>      i = 0
>      for n in names:
>        self.actors.append( Actor( n, i ) )
>        i += 1	# here is a case for python supporting post-increment!
> 
>    def count_actors( self ):
>      return len( self.actors )
> 
>    def list_actors( self ):
>      h=[]
>      for n in self.actors:
>        h.append( n.get_name() )
>      return h
> 
> # define the inner class
> class Actor:
>    def __init__ ( self, name, id ):
>      self.name = name
>      self.id = id
> 
>    def get_name( self ):
> 
>      # and here lies the problem;
>      # AttributeError: Actor instance has no attribute 'count_actors'
>      # how do I access the method in the enclosing class
>      txt = "I'm Actor {} Number {} of {}".\
>               format(  self.name, self.id, self.count_actors() )
> 
>      # this works, of course
>      #txt = "I'm Actor \"{}\"; Number {}.  ".\
> format( self.name, self.id )
> 
>      return txt
> 
> if __name__ == '__main__':
>    o = Monty( ["Cleese", "Idle", "Palin" ] )
>    print "number: ",o.count_actors()
>    a = o.list_actors()
>    for l in a:
>      print l

I think I've seen the solution a long time a go in a Borland library -- a 
Collection and a CollectionItem class, the latter with a reference to the 
collection it belongs to. However, you are introducing a reference cycle, 
and a simpler solution like putting a print_actor() method into the Monty 
class is probably the way to go.

Anyway, here's a variant of your code using the back reference (and a few 
cosmetical changes):

__metaclass__ = type # python 2 compatibility

class Monty:
    def __init__(self, names):
        self.actors = []
        for id, name in enumerate(names, 1):
            Actor(name, id, container=self)

    def __len__(self):
        return len(self.actors)

    def __iter__(self):
        for actor in self.actors:
            yield actor

    def remove(self, actor):
        raise NotImplementedError

    def add(self, actor):
        self.actors.append(actor)

class Actor:
    def __init__ (self, name, id, container=None):
        self.name = name
        self.id = id
        self._container = None
        self.container = container

    def get_container(self):
        return self._container

    def set_container(self, container):
        if self._container is not None:
            self._container.remove(self)
        if container is not None:
            container.add(self)
            self._container = container

    container = property(get_container, set_container)

    def __repr__(self):
        return "I'm Actor {} Number {} of {}".format(
            self.name, self.id, len(self.container))


if __name__ == '__main__':
    o = Monty( ["Cleese", "Idle", "Palin" ])
    print("number: {}".format(len(o)))
    for l in o:
        print(l)






More information about the Python-list mailing list