[Help] [Newbie] Require help migrating from Perl to Python 2.7 (namespaces)

Dave Angel d at davea.name
Mon Dec 24 11:47:30 EST 2012


Python is a flexible language, but manages to let one write readable
code even while using that flexibility.  It does, however, require that
one gets a grasp of some concepts that may differ greatly, either in
implementation or in name, from other languages.  Every language has its
quirks and constraints, but Python is usually about self-discipline,
rather than the language forcing it down our throat.

So you can write object-oriented code, or you can (for the most part)
forget that objects exist, and use them without realizing.  You can
write functional code (mostly), or you can ignore that concept.  The
main thing I can think of that you can't do is "goto."   Unlike C, 
BASIC, and assembler.

Python 2.7, if not stated otherwise.  3.x has some new rules.  I'm going
to assume you're familiar with lots of concepts, and have played with
the language.  So I'm not going to define print, or the plus sign.

So what are the building blocks?  Everything in Python is an object, or
becomes one soon after you look at it.  Open a file, you get an object. 
Do some arithmetic, you get an object.

So how do you create one?  As soon as you launch the compiler on your
source script, you're creating a module object.  inside that module
object are attributes that we usually call global variables, and other
attributes we call functions.  And maybe attributes we call classes. 
These are creating while compiling that script.  How do we reference
those objects?  By using their names.  That's what's meant by global,
they're universally accessible.  Oops, that's until we start making more
modules, which we'll get to later.

Python also has a bunch of modules it already loaded for us, including
the one called builtins.  Those objects appear magically in every
module, so they appear global as well.  What about objects in some
library function?  First we have to import the module, then we reference
the object by both the module name and the name in THAT global space. 
(That's why the word global was probably unfortunate).  So to get at the
function object called "sin" in the module called "math", we do the
following two statements:

import math
print  math.sin(1)

What did that import do?  It simply created a new global called math,
and bound it to the module by that name, compiling it and loading it if
necessary.  Once we have imported it, we can use ALL of its globals
(functions, variables, and classes, mostly) simply by the magic of the
dot.   math.sin   means look up the global math, then within that object
look up the attribute sin.  The syntax doesn't care that math is a
module, the same syntax would work if math was a file you just opened,
and sin was a File method.  So to read data from a file, we might do

myfile = open("filename")
print myfile.read(10)

Now, we glibly used 1,10, and "filename" in the above.  What are they? 
They're literal objects.  In the source code you give enough information
to create an object of a particular builtin type (like int and str), and
by the time the code is running, these objects are created in some
magical space we might call "the literal pool."  How that works doesn't
matter to anybody except to the guys building the compiler/interpreter.

So some names are global, and some are attributes.  I think that's it
for names.  There are constraints on what characters make up a name
(though in 3.x, that restricts one to a few hundred thousand symbols or
so), but that's about it.

Do all objects have names?  Nope.  We also have the notion of
containers, and we have special syntax to access them.  So a list can
reference dozens, or millions of objects, and none of them have an
independent name.  And the list might not either, if it came from a
literal      print [1,2,3].

What about a dictionary.  Funny thing, a dictionary works much like
attributes, only with a special syntax, and no restrictions on what the
keys can be.  In fact, under the covers, attributes are just items in an
invisible dictionary of an ordinary object.  (Note that invisible means
you don't usually care, in Python it's nearly always possible to peek
through the keyhole to see these things, but it can be very confusing,
and even harder to discuss)

Now we've been glibly referring to global items "having values" and
attributes "having values"  and in fact much of the literature refers to
variables.  But in a real sense, there are no variables at all.  There
is what's called binding.  A name is bound to an object.  Any object,
created any way, and located any way.  A list entry is bound to an
object.  So the object itself has no name.  it has type, and it probably
has attributes (actually it always does, but they may be "invisible"). 
But a name might be bound to that object at some time, or ten names, or
none at all.  And the object (probably) ceases to exist once there's no
way to find it.  If it's been bound to three names, and you rebind all
three names, then the object goes away after the third name is rebound. 
If its bound to a slot in a list, and also in a dictionary, and also in
a name, then it won't go away till all of those go away, or get rebound.

An important concept is mutation.  Many object, such as ints and strs,
are immutable, meaning they cannot change.  You can create a new object
very similar to the original, and rebind names from first to the second:
      a = "Peter"
      a = a + " Punkin Eater"

So a is rebound to a new object, and the Peter object will go away.

But if we have a mutable object, like a list, then we can modify the
single object without having to rebind it.
     a = [1,2,3]
     a.append(42)

This "changed a" but not really.  It changed the object that a is bound
to.  This distinction is a primary reason why the term variable is
misleading.

Now I get to modules, which are really most interesting when you start
writing the yourself.  Then, instead of following examples in the module
description, you have to decide how the module's "public" interfaces are
going to work, and why.

Back to the example
    import math
    print math.sin(1)

Suppose that we use that function a lot.  Then we might wish to avoid
prepending it with the module name every time.  The direct way to do
that is simply
    sin = math.sin

This creates a new global name, and binds it to the object which was
already accessible as math.sin .  Notice that there are now two ways to
get at that function, and also notice that we have no called the
function yet (no parentheses).

The shorthand for this is
    from math import sin

It does the same thing, but without actually defining a global called math.

Please avoid the funny mechanism:    from math import *   It gives the
reader no clue as to what symbols are now visible as globals.  And some
of those symbols might happen to have names that conflict with others we
do want.  The only time to do this is when a module is specifically
designed that way.  it can be coded in such a way as to restrict the
names that come in.  I still don't like it, but the docs are probably
written that way.

So if you write your own, please use a lowercase leading letter in the
filename.

Now, to reference stuff in that other module, you add an import, just
like you did with math.
    import mymodule
    result = mymodule.myfunction(arg1, arg2)

Let's assume you use
    from mymodule import myfunction

You now have an extra name bound to the same object as myfunction was in
the other module.

For constants and for functions, this is pretty straightforward.  But
what happens if somebody changes one of those globals?  that depends if
they rebind it, or they mutate it?  If they rebind it, you'll still be
referring to the original version.  While if they mutate it, you'll see
the changes, because you have an extra name bound to the same object.

I haven't mentioned writing classes yet, and describing them will take
lots more words.


-- 

DaveA




More information about the Python-list mailing list