Class scoping problem...

Kragen Sitaker kragen at dnaco.net
Wed Sep 27 00:40:26 EDT 2000


In article <mailman.969985661.20283.python-list at python.org>,
Simon Brunning  <SBrunning at trisystems.co.uk> wrote:
>I'm having a bit of a problem with scoping within a class. Something similar
>is mentioned in Andrew Kuchling's 'Python Warts' page, but I can't find a
>workaround.
>
>Take the following:
>
>class Foo:
>
>    def __init__(self):
>        self.bar = 1
>
>    def baz(self):
>
>        def quix():
>            return self.bar
>
>        return quix()

I think you mean 'return quix', so you're returning something you can
call later.  quix() here will return 1, so baz will return 1, as you
have written it.  (Except that it doesn't work, which I'll get to in a
minute.)

>Running this, I get this:
>
>  File "H:\SBrunning\Python\ClassScope.py", line 9, in quix
>    return self.bar
>NameError: There is no variable named 'self'
>
>Which is fair enough, 'cos self.bar isn't in the global namespace, and nor
>is it in baz's namespace. But what do I *do*?

It *is* in baz's namespace; self is a formal parameter of baz.  It's
listed in the parameter list.  Where it isn't is in quix's namespace,
which is the problem.

There are several possibilities.  Python has two things that are
callable as functions: user-defined functions, methods, and class
instances --- THREE things that are callable as functions: user-defined
functions, methods, class instances, and classes ---- FOUR things that
are callable as functions --- AMONGST the things that are callable in
Python are such things as user-defined functions, methods . . . I'll
come in again.

The simple thing is just to return a bound method:

class Ximenez:
    def viking(self):
        return self.spam
    def getviking(self):
        return self.viking

Depending on the context, you might be able to replace x.getviking()
with x.viking in the calling code and save yourself a helper.

So that's a method.  How about a user-defined function?  You can stick
stuff in argument defaults:

class Biggles:
    def __init__(self):
        self.count = 0
    def torture(self):
        print "I have poked you with %d pillows" % self.count
        self.count = self.count + 1
    def get_torture(self):
        return lambda s=self: s.torture()

b = Biggles()
x = b.get_torture()
x()
x()
x()

Which you can also write, as you did, with a named inner function:

    def get_torture(self):
        def function_that_needs_no_name(s=self):
            s.torture()
        return function_that_needs_no_name

This doesn't work well in every case, for example, when the function
you're returning is going to be called with variable numbers of
arguments or keyword arguments.  For the heavy-duty work --- like the
XML/XHTML stuff I posted earlier today --- you need to make class
instances that can be called.  It's probably overkill here, but here's
how to do it:

class TortureMaster:
    def __init__(self, torturer):
        self.torturer = torturer
    def __call__(self):
        self.torturer.torture()

class Fang:
    def __init__(self, racktype):
        self.racktype = racktype
        self.count = 0
    def torture(self):
        print ("I have tied you to the %s rack %d times" % 
            (self.racktype, self.count))
        self.count = self.count + 1
    def get_torture(self):
        return TortureMaster(self)

b = Fang('dish')
x = b.get_torture()
x()
x()
x()

There may be other ways to solve the problem, but I haven't figured out
how to do it by returning a class or a built-in function from
get_torture.  I guess you can tell I'm from Perl land.  <wink>

I'd recommend using lambdas when you don't need fancy parameter
handling or statements in the body, inner functions when you don't need
fancy parameter handling, and class instances when you need fancy
parameter handling.

>I-used-foo-so-Tim-won't-help-ly-yrs,

reading-too-many-Python-scripts-ly y'rs  - kragen
-- 
<kragen at pobox.com>       Kragen Sitaker     <http://www.pobox.com/~kragen/>
Perilous to all of us are the devices of an art deeper than we ourselves
possess.
                -- Gandalf the Grey [J.R.R. Tolkien, "Lord of the Rings"]



More information about the Python-list mailing list