Need help with Python scoping rules

Hendrik van Rooyen hendrik at microcorp.co.za
Thu Aug 27 15:27:59 CEST 2009


On Thursday 27 August 2009 11:14:41 Steven D'Aprano wrote:
> On Thu, 27 Aug 2009 08:38:29 +0200, Hendrik van Rooyen wrote:
> > On Wednesday 26 August 2009 17:14:27 kj wrote:
> >> As I described at length in another reply, the function in question is
> >> not intended to be "callable outside the class".  And yes,
> >
> > I think this might go to nub of your problem - It might help you to
> > think as follows:
> >
> > A Python class, even after it has been executed, does not really exist
> > except as a kind of template or pattern - it is mostly useless until an
> > instance of the class is made by calling it with whatever it needs to
> > set up the instance.  And once you have an instance, you can do stuff
> > with that particular instance.  Before that time, the class is mostly
> > just a promise of things to come.
>
> Oh my! I couldn't disagree more strongly! I think the above is totally
> incorrect.

It was intended to help the OP out of the mental hole he finds himself in.

Most of the classes I use fall directly into such a classification - they are 
useless until an instance is created.  And I would be so bold as to say that 
_all_  gui classes are like that. 

This is the pattern I am talking about:

class thing(object):
    def __init__(self,foo,bar):
        stuff to do things with foo and bar,
        creating or modifying attributes of
        the instance.

    def somemethod(self,baz,bling):
        instance method to do further operations on
        the attributes of the instance

Now kindly explain to me how a class like that is usefull before an instance 
of it is created, and I might agree with you that what I said is "totally 
incorrect"

8< --trivial examples showing that there is something there ------------

> Classes are themselves instances of their metaclass. By default, classes
> have a metaclass of type, but you can easily change that. Metaclass
> programming is advanced but very powerful.
>
> Because classes are themselves objects, you can (with a bit of metaclass
> jiggery-pokery) make them do all sorts of interesting things. For
> instance, huge amounts of effort are often put into creating a Singleton
> class, a class that has a single instance. Well, okay... but why not just
> use the class object itself, instead of an instance? There's already one
> of those, and you can't (easily) make a copy of it, and even if you did,
> it would be an independent class. Instead of this:
>
> singleton = SingletonClass(args)
> do_something_with(singleton)
>
> just do this:
>
> do_something_with(SingletonClass)
>
> Of course SingletonClass needs to be designed to work that way, which
> will probably need some metaclass magic. It would be interesting to see
> which requires less effort.

*nods* yes this would make sense - but it is not quite what either the OP or I 
was on about.

>
> When it comes to built-in classes (types), I often use the class object
> itself as an object. E.g. I might do something like this:
>
> def convert(seq):
>     t = type(seq)  # remember the original type
>     result = process(seq)  # always produces a list
>     if t is list:
>         return result  # don't bother making a copy of the result
>     elif t is str or t is unicode:
>         empty = t()
>         return empty.join(result)
>     else:
>         return t(result)  # return the original type

nice.  now try doing it with object:

thing = object()

After that, thing will exist, but it is a bit like write only memory - 
completely useless, until you have done some more work with it.

>
> >> recursive functions in Python *are* restricted in ways that
> >> non-recursive functions aren't.  The examples I've posted prove this
> >> point unambiguously.
> >
> > Yes and no - mostly no -  your examples just illustrate the point I
> > tried to make above.
>
> Completely no. You may have missed the examples I've given, but the
> problems the Original Poster were having had nothing to do with recursion.

The yes was in there to make the man feel a bit better, and because from where 
he stood, it _was_ the recursion that did him in.   : - )

>
> > Pythons object model, and its classes, are different from what you are
> > used to.  A bare class is mostly useless without an instance, which is
> > ultimately why accessing a function in a class from itself like you are
> > doing, without reference to an instance, does not work - the function
> > does not exist yet to a degree that it can be referenced.
>
> That is incorrect. What's going on is more subtle.
>
Right -  I can see that you are reading that to mean that there must be an 
instance.  That is not what I intended to bring across.  I was talking about 
the lack of a reference that is his original problem, which he only 
encountered with recursion.

> >>> class Demo:
>
> ...     def function(x):
> ...         print "Calling function with argument %s" % x
> ...     function(None)
> ...     function(1)
> ...     function(function)
> ...
> Calling function with argument None
> Calling function with argument 1
> Calling function with argument <function function at 0xb7d495dc>
>
> >>> Demo
>
> <class __main__.Demo at 0xb7d4e0ec>

two points:

Of what earthly use is a bare class like that - except possibly as a container 
for all the good stuff those functions produced out of nothing?   - You might 
as well use a list, or better a dict, to keep the results of such functions, 
unless the functions are procedures, doing things by side effect - and if 
that is the case, why bother putting them in a class?

Secondly:

Now what you have done is carefully avoided the OP's original problem, which 
arises when the function in the class is called, and tries to call itself at 
the time of execution of the class suite, and fails, for the reasons so 
nicely explained by a lot of people.  So the OP had a point when he 
complained that python does not like recursion, because, until he understands 
what is happening, the simplest theory that fits his observed facts is that 
the failure is caused by the recursion. - a completely rational conclusion 
based on Occam's razor.   It just happens to be wrong in this case.  This was 
the source for my "Yes" above.

This should also work for the OP - I cannot recall if it has been shown:

class Demo(object):
    def fact(x,f):
        if x < 2:
            return 1
        else:
            return x*f(x-1,f)

    thing = fact(42,fact)

I still think it is stupid, though - thing should simply be set to
1405006117752879898543142606244511569936384000000000L
and that would be the end of it for this use case, without the bother of a 
function and the concomitant waste of time.

- Hendrik




More information about the Python-list mailing list