__module__ (was Re: Deprecate self)

Alex Martelli aleaxit at yahoo.com
Fri Apr 20 08:27:37 EDT 2001


"Rainer Deyke" <root at rainerdeyke.com> wrote in message
news:7aPD6.58066$J%5.19288805 at news2.rdc2.tx.home.com...
> "Alex Martelli" <aleaxit at yahoo.com> wrote in message
> news:9bn4gu0ugt at news1.newsguy.com...
> > "Rainer Deyke" <root at rainerdeyke.com> wrote in message
> > news:vQpD6.47756$J%5.15806884 at news2.rdc2.tx.home.com...
> >     [snip]
> > > def f():
> > >   class C:
> > >     pass
> > >   C()
> >
> > Ok, it's now clearer what you mean, thanks.  It doesn't
> > STOP somebody sufficiently determined from subclassing
> > it, of course:
> >
> > class OhYeah(f().__class__):
> >     pass
>
> Actually I'm not even returning an instance in my example, so even that
> isn't possible.

True -- you're instancing but not returning it; I had made
a "think-o" and read a "return" that wasn't there.  If no
instances of the class (as well as the class object itself,
method objects, etc) survive the function-execution nor
escape its confines, you _can_ indeed fully control the
use of the class.


> > but it does provide a rather good hint that "being
> > subclassed" is not the class's design intention:-).
>
> > > This is, incidentially, the idiom I had to use extensively to deal
with
> > the
> > > lack of nested scoping in Python.  More generally, you write a class
for
> >
> > How does a local class help you there in ways nested
> > scoping will make obsolete?  Sounds interesting.
>
> def render(draw_fn):
>   gc = get_gc()
>   draw_fn(gc)
>
> def render_text(text):
>   class Text:
>     def __init__(self, text):
>       self.text = text
>     def draw(gc):
>       gc.draw_text(self.text)

I think you mean "def draw(self, gc):" here, right?  (Given
that this is meant to be current-Python code).

>   t = Text(text)
>   render(t.draw)
>
> In this case I could have used the default argument hack, but that isn't
> generally applicable (think *args, **kwargs).

True, if you can't 'reserve' any name (need to accept all as **kwargs)
you can't use default argument values to bind state, so a local class
is a way to get complete generality while a local function will indeed
suffice when scopes nest.  Thanks for the example.  (I'll even avoid
pointing out that, since a bound-method DOES "escape the confines" of
function render_text, inheritance from the local Text class does
become _possible_ again -- the render function could be changed to
one that defines a class inheriting from draw_fn.im_class -- as this
is so obviously outside of design intent that only a fully paranoid
system could expend effort to enforce things here:-).


> > Nice -- a specifically Pythonic benefit.  Of course, it's no
> > problem if VariableAccess gets multiply instantiated, so I
> > would consider this an example of the pattern "Featherweight"
> > (a class whose instances share all state) rather than one of
> > the pattern "Singleton" (a class that is protected against
> > being multiply instantiated), and I like FW as much as I
> > dislike Singleton, so there's still no benefit to the singleton
> > design pattern, but that's another issue.
>
> There is no reason for multiply instantiating 'VariableAccess' because the

No special reason to do so, no special reason to avoid doing
so.  That is fine with me and doesn't match the description
of the "Singleton" design pattern.

> defining module provides one instance and every instance other instance is
> identical in state and behavior.  Whether you call this a "Singleton" or a
> "Featherweight" or even "Gorak, destroyer of worlds" is all the same to
me.

Not to me, nor to most readers, since design patterns' _names_
are an important characteristic -- the "primary key" used to
retrieve (from memory, a book, etc etc) the various things that
make up the pattern -- what forces it resolves, how, etc.  The
Singleton DP goes to some effort to avoid multiple instantiation
and relies on that; I've long thought that expending effort that
is not greater, and in fact is often lesser, to ensure that
multiple instantiations can occur innocuously (by sharing state
among potentially-multiple instances), makes for better design
patterns (albeit the catchy "Singleton" name makes it perhaps
the most popular and well-known Gof4 DP).  So I'm always on the
lookout for situations where the actual forces DO make Singleton
preferable to FeatherWeight -- haven't found any so far.

That there exist design situations where a single copy of a
certain "state" is desirable, is hardly in question -- that IS
a force that comes up again and again.  The issue is how to
best resolve it, and the other forces that accompany it in
various use cases.


> > > Consider the following hypothetical Python-like language:
> > >
> > > def f():
> > >   print 'spam'
> > >
> > > class C:
> > >   def f(): # Note lack of 'self'
> > >     print 'eggs'
> > >   def g():
> > >     __instance__.f()
> > >     __module__.f()
> > >
> > > I'm not sure I like this myself (the __magic__ names are too long, the
> > > result of a 'def' statement depends on context, there's no clear way
to
> > > refer to the outer class from a method in an inner class, magically
> > > appearing variables have the potential to cause trouble, and the
> > > 'C.f(instance_of_C)' conventions seems to be broken), but at least
it's
> > > better than the strawman against which you seem to have been arguing.
> >
> > Actually, I think _this_ is "a strawman proposal" -- one with some
> > admitted defects but that at some level realizes design intentions.
> > Earlier, I saw complaints but no pointer to resolving them.
>
> My goal wasn't to change Python, but to discuss a perceived flaw in Python
> which later generations of language designers would then be able to avoid
> (or alternately to be convinced that the inconsistency is a good idea for
> pragmatic reasons).

Your "strawman proposal" doesn't seem to show up any inconsistency in
Python as it stands.  It _does_, to me at least, suggest a possible
lack (specifically that of a __module__ magicnamed attribute in each
module object, referring [maybe weakly, or whatever] to the module
object itself, to avoid having to import, check sys.modules, etc --
assuming for the sake of argument that the need for code in a module
to refer to the module object IS a frequent enough one to warrant it).


> > Thanks for posting it at last.  I fully agree with all of your
> > criticisms, and furthermore don't see how this will support the
> > useful idiom of assigning an existing function to serve as a method,
>
> Given this:
>
> def f(x):
>   do_something_width(x)
>
> Instead of writing this:
>
> class C:
>   do_f = f
>
> Write this:
>
> class C:
>   def do_f():
>     __module__.f(__instance__) # Where '__module__.' might be optional.

Right -- it doesn't support the existing idiom, so you have to
code around it with explicit delegation.  Enriching an existing
class-object from outside the class's body seems even more of
a problem -- current "C.do_f=f" becomes...?  The "def depends
on context" defect that you noticed seems to hurt here.


> This is assuming that you want the instance as first argument.  There are
> times when you don't; see 'VariableAccessor' above.

Yes, some people do keep clamoring for "class methods", and
this strawman might support them as it enables methods to
be (e.g.) without arguments.  Some would no doubt prefer to
have a simpler "class method" idiom even at the cost of
totally losing the "add a function as a method" one -- and
of even deeper black magic than the current type mutation
(function to unbound method).


> > A somewhat related issue is somehow getting easy access to the
> > function object from that function's code -- particularly likely
> > to be interesting now that function objects have arbitrary
> > attributes.  Presumably harder as the __function__ (or
> > whatever) would have to be stuck in (the locals...?).  Just
> > an aside, anyway.
>
> '__function__' could be treated similarily to default arguments.  I don't
> see the problem.

Placing '__function__' in the local namespace of every single
function is a higher cost than placing '__module__' in the
namespace of every module for the very simple reason that
there are relatively few modules, created pretty rarely, and
a LOT of functions, some of which are created more often (in
a style using a lot of local functions).  The reference circle
that gets created may not be as simple and thus easily handled
as " anymodule.__dict__['__module__'] is anymodule " -- it
would presumably be " anyfunc.func_defaults[0] is anyfunc "
(but would 0 be appropriate here?) _plus_ something like
" anyfunc.func_code.co_varnames[ ??what?? ] == '__function__' "
where I guess ??what?? is one more than the actual number
of non-default function parameters.  The implementation does
seem more complex to me than the crystal-simplicity of __module__.
Maybe the newer mechanisms put in place to support nested
scopes may happen to make this easier -- haven't looked much
at them yet; or maybe there _are_ better places for this than
func_defaults anyway.  "Presumable harder" (than __module__)
seems a fair assessment to me.


> > > I'm saying specifically that C++ is right in providing a way to
qualify
> > > names as global - no more.
> >
> > OK, I accept that's what you intended to say right from the
> > start.  Now, "global" in C++ means "global across all sources"
> > rather than "specific to this source".  Object specific to
> > this module would be in the unnamed namespace (since the
> > use of 'static' for that is [informally speaking only, I
> > believe] deprecated in C++) and thus non-qualifiable; other
> > (non-unnamed) namespaces are shared across sources.  Are
> > you expressing admiration for THIS trait of C++?
>
> No.  I see a certain advantage to C++'s decoupling of file and namespace,
> but I am neither talking about it here, nor do I wish it upon Python.

OK.  Having worked extensively with both languages which
couple sourcefiles and namespaces/modules (Python, Java,
etc) and others that decouple them (C, C++, Fortran) I
strongly prefer the former approach, but if this aspect
of the discussion doesn't touch Python then we'd better
drop it, I guess, as there are so many other subthreads
here that ARE Python-relevant anyway.


> >  Or do you
> > actually wish to keep things Pythonically simple and is
> > the C++ reference some sort of red herring?  It's starting
> > to look that way to me.
>
> Python is more than complex enough for me.

So how would __module__ and __istance__ and __function__
and all other such additions REDUCE its complexity that
you perceive?


Alex






More information about the Python-list mailing list