Yarin
Yarin

Reputation: 183909

Reference an owner class from inside one of its callable attributes

I want to assign a callable class as a function on another class, and then reference the owner class from the owned function:

class DoIt(object):

    def __call__(self):
        print "%s did it" % self.name

class Doer(object):

    def __init__(self, name):
        self.name = name

    doSomething = DoIt()


doer = Doer("Bob")
doer.doSomething()

In the code above, I want the doSomething() function to print "Bob did it", but with this code I'll get a

AttributeError: 'DoIt' object has no attribute 'name'

Because self references the DoIt instance, not the Doer instance. Is it possible to reference the Doer instance?

Upvotes: 0

Views: 683

Answers (3)

Ignacio Vazquez-Abrams
Ignacio Vazquez-Abrams

Reputation: 799230

class DoIt(object):

    def __get__(self, instance, owner):
        def done():
            print "%s did it" % instance.name
        return done

class Doer(object):

    def __init__(self, name):
        self.name = name

    doSomething = DoIt()


doer = Doer("Bob")
doer.doSomething()

Explanation: (This is the asker explaining this answer as best he can)

Python offers you descriptor classes, which are classes that act as attributes (in contrast to attributes as instance variables or properties). The __get__ method of a descriptor class dictates what's returned when that class is assigned and accessed as an attribute on another class. And __get__ lets you access both the owning instance and owning class (See method signature).

By having __get__ return a function, you essentially create a callable that has access to its owner.

For more on descriptors, see Attributes, Properties and Descriptors- Building Skills in Python

Upvotes: 4

Yarin
Yarin

Reputation: 183909

This is purely another example for Ignacio's answer- I wanted to show the same concept when a parameter is required for the "callable".

class DoIt(object):

    def __get__(self, instance, owner):

        def done(what):
            print "%s did something %s" % (instance.name, what)
        return done

class Doer(object):

    def __init__(self, name):
        self.name = name
    doSomething = DoIt()

doer = Doer("Bob")
doer.doSomething("cool")

prints:

Bob did something cool

Upvotes: 0

Andrew Clark
Andrew Clark

Reputation: 208635

Here is one way to accomplish this:

class DoIt(object):
    def __init__(self, obj):
        self.obj = obj

    def __call__(self):
        print "%s did it" % self.obj.name

class Doer(object):
    def __init__(self, name):
        self.name = name
        self.doSomething = DoIt(self)

doer = Doer("Bob")
doer.doSomething()    # prints 'Bob did it'

Upvotes: 2

Related Questions