(newbie) Is there a way to prevent "name redundancy" in OOP ?

Martin Miller ggrp1.20.martineau at dfgh.net
Wed Jan 10 10:33:56 EST 2007


Carl Banks wrote:
> Martin Miller wrote:
> > Carl Banks wrote:
> >
> > > Martin Miller wrote:
> > > > ### non-redundant example ###
> > > > import sys
> > > >
> > > > class Pin:
> > > >     def __init__(self, name, namespace=None):
> > > >         self.name = name
> > > >         if namespace == None:
> > > >             # default to caller's globals
> > > >             namespace = sys._getframe(1).f_globals
> > > >         namespace[name] = self
> > > >
> > > > Pin('aap')      # create a Pin object named 'aap'
> > > > Pin('aap2')     # create a Pin object named 'aap2'
> > > > print aap.name
> > > > print aap2.name
> > >
> > > The problem with this is that it only works for global namespaces,
> > > while failing silently and subtly if used in a local namespace:
> >
> > Oh, contrair. It would work fine with local namespaces simply by
> > overriding the default value of the optional 'namepace' parameter (see
> > below).
>
> Did you try it?

Yes, but I misinterpreted the results which seemed to support my
claim. Therefore I must retract what I wrote and now have to agree
with what you said about it not working in a local namespace --
specifically in the sense that it is unable to bind the instance the
name in the caller's local namespace.

I'm not sure that this is a critical flaw in the sense that it may
not matter for some usages. For example I've seen it used to define
(yet another) Enum class which facilitated the creation of names
bound to a range or sequence of values. The fact that these couldn't
be defined local to code block wasn't apparently a big issue.


> > > def fun():
> > >     Pin('aap')
> > >     aap1 = aap
> > >     fun2()
> > >     aap2 = aap
> > >     print aap1 is aap2
> > >
> > > def fun2():
> > >     Pin('aap')
> > >
> > > If it's your deliberate intention to do it with the global namespace,
> > > you might as well just use globals() and do it explicitly, rather than
> > > mucking around with Python frame internals.  (And it doesn't make the
> > > class unusable for more straightforward uses.)
> >
> > You could be more explicit by just passing 'globals()' as a second
> > parameter to the __init__ constructor (which is unnecessary, since
> > that's effectively the default).
> >
> > It's not clear to me how the example provided shows the technique
> > "failing silently and subtly if used in a local namespace" because what
> > happens is exactly what I would expect if the constructor is called
> > twice with the same string and defaulted namespace -- namely create
> > another object and make the existing name refer to it. If one didn't
> > want the call to Pin in fun2 to do this, just change fun2 to this:
>
> Because the usage deceptively suggests that it defines a name in the
> local namespace.  Failing may be too strong a word, but I've come to
> expect a consistent behavior w.r.t. namespaces, which this violates, so
> I think it qualifies as a failure.

I don't see how the usage deceptively suggests this at all. In this
case -- your sample code for fun() and fun2() -- all were simply
Pin('aap'). Since no additional namespace argument was supplied, the
same name was bound in the defaulted global namespace each time but
to different objects. In other words the 'print aap1 is aap2'
statement produced 'false' because the call to fun2() changed the
(global) object to which 'aap' was previously bound.

>
> > def fun2():
> >     Pin('aap', locals())
>
> Did you actually try this?  ...

As I said, yes, I did, and the addition of the 'locals()' parameter
does make the 'print aap1 is aap2' statement in fun() output 'true'.
This lead me to take for granted that it had bound the name in the
local namespace. However this assumption was incorrect, but that
wasn't obvious since there were no further statements in fun2().

The problem is that there fundamentally doesn't seem to be a way to
create local variables except directly by using an assignment
statement within the block of a function or method. Modifying the
mapping returned from locals() does not accomplish this -- normally
anyway, although interestingly, it will currently if there's an exec
statement anywhere in the function, even a dummy one, but this is
not a documented feature and from what I've read just side-effect of
the way code optimization is be done to support the exec statement.


> ...  Better yet, try this function.  Make sure
> aap isn't defined as a global:
>
> def fun3():
>     Pin('aap',locals())
>     print aap
>
> (I get NameError.)

I do, too, if I run it by itself or first unbind the global left over
from
fun() with a 'del aap' statement.


> > This way the "print aap1 is aap2" statement in fun() would output
> > "true" since the nested call to Pin would now (explicitly) cause the
> > name of the new object to be put into fun2's local namespace leaving
> > the global one created by the call in fun() alone.
>
> Did you try it?

Yes -- explained above.


> > Obviously, this was not an objection I anticipated. An important one I
> > would think is the fact that the current documentation says that
> > f_globals is a special *read-only* attribute of a frame object implying
> > that it shouldn't be changed (even though doing so 'works' as my
> > example illustrates).
>
> That means it can't be rebound.  It doesn't mean you can't modify the
> object.  Nevertheless, frame objects are an internal feature of Python,
> not guaranteed to work for other incarnations of Python (it doesn't
> even exist in IronPython, for example), and subject to change.  And
> what you want to do is easily done in a platform generic way without
> using sys._getframe.

Since '_getframe' starts with an underscore, by convention it's
something private, likely an implementation detail, and therefore
not guaranteed not to change in future. Similarily the documentation
says it "should be used for internal and specialized purposes only".

Guess I should have mentioned that this was also an expected
complaint when I warned the OP that some people would oppose the use
of the technique.

My own philosophy is that it's simply a judgment call that depends
on what you're doing and what are the alternatives. Not all code is
mission-critical and even with that which is, it may perhaps be
worth the risk of possible issues later in future in order to have
something now that works in your present environment.


> > I think other valid arguments against this practice might include
> > whether it's an example of good OOP, or a good programming practice at
> > all, since it involves possibly subtle side-effects, the use of global
> > variables, and/or is 'unpythonic'.
>
> I think programmatically creating variables is fine; I just recommend
> you not use sys._getframe, nor the automagical namespace self-insertion
> class, to do it.

You've explained some of your worries about sys._getframe. It would
be interesting to hear specifically what it is you don't like about
the idea of namespace self-insertion -- mainly because of the local
namespace limitation?


> > Certainly the approach is not without caveats. Despite them, I believe
> > it can be useful in some contexts, as long as what is going on is
> > clearly understood. The point of my reply to the OP was mainly just to
> > illustrate the power and flexibility of Python to the newbie. I guess
> > to that I should have also added that it gives you "enough rope to
> > shoot yourself" as Allen Holub said regarding C++.
> >
> > Cheers,
> > -Martin
>
> Carl Banks

Regards,
-Martin




More information about the Python-list mailing list