inner classes in python as inner classes in Java

Alex Martelli aleax at aleax.it
Thu Oct 16 11:15:00 EDT 2003


Carlo v. Dango wrote:

> On Wed, 15 Oct 2003 22:20:44 GMT, Alex Martelli <aleaxit at yahoo.com> wrote:
> 
>>> __setattr__ will have no changes of knowing when to create the field or
>>> look it up in the outer scope... this suxx man... I never understood the
>>> smart thing about that feature in python.. anyone care to enlighten me?
>>
>> If you can't see that it's simpler to let
>>
>>    target = value
>>
>> have the same semantics whether target was already bound to something
>> or not, it's hard to see what will convince you.
> 
> I can't... mispelling occours and leed to stupid bugs rather than compile
> errors which are simple to check. Even smalltalk has this feature..

So does pychecker, e.g., given the following erroneous a.py:

def func(lead):
    "return the argument if true, else 23"
    if not lead: leed = 23
    return lead

running "pychecker a" will easily warn you:

a.py:4: Local variable (leed) not used

et voila, the "stupid bug" is squashed, without any need to burden
down the language with ``declarations'' for the purpose.  Some of
pychecker's functionality is likely to get incorporated in future
versions of Python interpreter (just like good old tabnanny.py's
functionality was once incorporated via the -t switch).


>> its still hard to say what benefits you'll be getting, of course, but
>> there may be some cunning plan behind it all.
> 
> aaaah come on :) you really cannot see the nice things of having a scope
> for inner class instances?? It's like inner method.. atleast they share
> scope with their outer method... if it didn't it would render inner
> methods almost useless..
> 
> class A(object):
> def foo(self):
> i = 3
> def bar():
> print i
> 
> bar()

Your stubborn refusal to follow Python's recommended style, which
I already repeatedly explained to you (indent with spaces, not tabs)
makes your code look like the above -- flat and meaningless -- in
KDE's KNode (and MS Outlook Express, too, also widely used).

In any case, I cannot see any _usefulness_ whatsoever in the above
snippet, no matter how one inserts whitespace in it -- it appears
to use a def statement for 'bar' in a totally gratuitous way,
without any benefit whatsoever.  There ARE, of course, good use
cases for nested functions (the inner one just isn't a method no
matter how much you waste your breath saying it is); many of them
relate to returning the function object created by the inner def
(the 'closure' idiom):

def make_summator(addend):
    def summator(augend): return augend + addend
    return summator

this is about the simplest case that is of any use.  When Python
did not nest scopes, this was equivalently implemented as:

def make_summator(addend):
    def summator(augend, addend=addend): return augend + addend
    return summator

and you can still code it that way (it's marginally faster at
runtime, but not enough to matter).  It's fractionally handier
to not have to inject names explicitly, but, again, it's hardly
a major issue in terms of programmer's productivity.

A simple but non-toy use case for a class defined within another
class might be an iterable/iterator pair:

class tree(object):
    
    # snipped: most definitions of tree's methods

    def __getitem__(self, tree_index):
        # somehow locate and return the item of this tree
        # corresponding to key 'tree_index', or raise
        # IndexError if tree_index is out of bounds

    class iterator(object):
        def __init__(self, tree):
            self.tree = tree
            self.index = 0    # or whatever means "first item's index"
        def __iter__(self):
            return self
        def next(self):
            try: item = self.tree[self.index]
            except IndexError: raise StopIteration
            self.index += 1   # or whatever means "next item's index"
            return item

    def __iter__(self):
        return self.iterator(self)

Note the amount of 'overhead' you have here to let the tree's
__iter__ method specifically pass 'self' when instantiating the
tree_iterator class: essentially negligible when compared to
the amount of other code in even such a simple example as this
one.  In contrast, a closure might want to bind some number of
names from the outer scope, and the amount of other code
involved can be tiny, so the admittedly-small overhead of
explicitly injecting (binding) names stands out more.  At
the same time, having tree.iterator intrinsically "inherit"
names from the scope of class tree would be potentiallly quite
problematic: e.g. the user might erronously 'index' an iterator
and be in fact indexing the whole tree (if name __getitem__ got
thus transparently injected), quite ignoring the current index
in the iterator.  The incredibly tiny benefits of having a
class automatically get names from the scope of the containing
class would obviously be dwarfed by the evident problems.

Further note that neither in nested functions, neither in
nested classes, is there any need in the above examples to
BIND any name in the containing scope.  Indeed, nested
functions just don't support that: the inner function can
ACCESS outer names, but NOT rebind them.  The interest of
binding outer names WOULD be there -- in examples not quite
as trivial as these, but almost -- but there is absolutely
no interest in having that be 'transparent'.  There is
currently no syntax to rebind names from an inner function
in the outer scope (you need to achieve similar ends by
_mutating_ mutable objects in the outer scope, a very
different concept from that of rebinding names).  Rebinding
names from the inner to the outer class is achieved in just
the same way as accessing them -- i.e. normally by explicit
non-transparent syntax such as self.tree.foobar (be it for
access or for rebinding).

But what is the purported usefulness of having the inner
class act as a somewhat transparent proxy for the outer one?
The iterable/iterator example does nothing of the kind and
has no need for any such transparency (which indeed would
seriously impair it).  What are the use cases for instances
of the nested (inner) class acting as proxies for instances
of the containing (outer) one, intercepting some but not all
of the attribute rebindings (and presumably deletions?  have
not seen you implement __delattr__ for that purpose) yet
handling other rebindings "on its own", AND somehow choosing
which re-bindings to delegate and which to intercept in a
totally automatic/introspective way (e.g. based on what set
of attributes happen to be defined on the outer class's
instance at some given point in time)?

As I said, there may be some cunning plan behind this weird
kind of specs, but you surely have not given any indication
of what need you might have for them (indeed, haven't given
any complete non-contradictory set of specs that I've seen
for the rebinding behavior you appear to desire).  And no,
I "cannot see the nice things" unless you indicate a use case:
I have neither had nor seen such a specific need in 4 years
of Python usage, writing, teaching, mentoring, tutoring, and
consulting.  I _have_ used inner classes in Java, mostly as
workarounds for the lack of multiple inheritance (a lack
which Python doesn't share), but also marginally for other
things -- but none that would require transparent rebinding
in a Python setting.  If you _do_ finally deign to indicate
the use cases you have in mind, as well as the exact, precise
and non-contradictory semantics for attribute access and for
attribute rebinding that you think are best suited to handle
your use cases, it's possible that experienced Pythonistas may
be able to indicate preferable approaches, as well as to
advise on how to implement the approach you think you need.


Alex





More information about the Python-list mailing list