Sharing package data across files

Steven D'Aprano steve at pearwood.info
Tue Jun 28 20:50:29 EDT 2016


On Wed, 29 Jun 2016 09:51 am, scottpakin1 at gmail.com wrote:

> On Tuesday, June 28, 2016 at 4:37:45 PM UTC-6, Michael Selik wrote:
>> Why do you want to?
> 
> I have a standalone script that grew and grew until reaching an
> unmaintainable size.  I was hoping to refactor it into a relatively small
> top-level script plus a package containing a bunch of relatively small
> files.
> 
>> Isn't easier to have the ``funcs`` module import the ``vars`` module?
> 
> Easier, yes.  Correct, no:

No, you are misinterpreting what you are seeing.

The correct answer (for some definition of correct) is to import vars and
operate on the attributes of it. Another solution would be to collect
related bits of functionality and their variables into classes.

Aside: you should reconsider the name "vars", as that shadows the builtin
function vars().

This code doesn't work:

>     from vars import foo
> 
>     def bar():
>         global foo
>         foo += 1
>         return foo


but not for the reason you think. However this will work:

    import vars
    def bar():
        vars.foo += 1
        return vars.foo

and that's probably the easiest way to refactor what you have now.

But honestly, if your standalone script contains *so many global variables*
that you need to do this, that's a code smell. You should rethink your use
of global variables, and consider refactoring your code into functions and
classes. Just a suggestion.

 
> which surprisingly (to me, anyway) changes a _copy_ of foo, not the foo
> I'd think of as belonging to the example package:

No, this explanation is incorrect. The object foo is not copied. Python
doesn't copy objects unless you explicitly tell it to, usually with the
copy module.

When you import something from another module, it creates a name in the
importer's local namespace:

# module vars.py
foo = 999


# module spam.py
from vars import foo


Now there are TWO names "foo". Remember that names are string keys in a
dict. We call that a namespace, and there are two of them:

the "vars" namespace is the dict vars.__dict__

the "spam" namespace is the dict spam.__dict__


and they BOTH have a key "foo". Obviously they must be different, as
otherwise any module that happens to use the same variable names as your
module would stomp all over your variables and change their values.

So at this point, you have

    vars.foo = 999
    spam.foo = 999  # the SAME object, not a copy


Inside the "vars" module, you don't give the fully-qualified
name "vars.foo", you just say "foo". And likewise in the "spam" module, you
don't give the fully-qualified name "spam.foo" but just say "foo". But
don't be fooled, they are *different* foos that just happen to both refer
to the same value.

When you say

    spam.foo = 100  # equivalent to spam.foo += 1

obviously spam's foo will change, but vars' foo doesn't. It's in a different
namespace, and assignment doesn't reach into different namespaces unless
you give the fully-qualified name. (That would be bad.)

Inside spam, you don't need to say "spam.foo" (in fact, you can't, unless
you first import spam from inside itself, which is weird thing to do). You
just say "foo = 100". But conceptually, it is the same.





-- 
Steven
“Cheer up,” they said, “things could be worse.” So I cheered up, and sure
enough, things got worse.




More information about the Python-list mailing list