[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.python

if not global -- then what?

egasimus

2/20/2010 7:26:00 PM

Hi, newbie here. I've read on using the 'global' keyword being
discouraged; then what is the preferred way to have something, for
example a class containing program settings, accessible from
everywhere, in a program spanning multiple files?
7 Answers

MRAB

2/20/2010 8:00:00 PM

0

egasimus wrote:
> Hi, newbie here. I've read on using the 'global' keyword being
> discouraged; then what is the preferred way to have something, for
> example a class containing program settings, accessible from
> everywhere, in a program spanning multiple files?

Python's 'global' keyword is global in a file (module), not global
across multiple files, so it would have helped you anyway.

If you want program settings accessible across multiple modules then put
the settings in a module and import that module in any other module that
needs to access them.

Gary Herron

2/20/2010 8:01:00 PM

0

egasimus wrote:
> Hi, newbie here. I've read on using the 'global' keyword being
> discouraged; then what is the preferred way to have something, for
> example a class containing program settings, accessible from
> everywhere, in a program spanning multiple files?
>

Define your "global" in a module (a .py file) and import that wherever
you need it:

mod.py:
gvar = 123 # A Global (like) value


main.py:
import mod
.... mod.gvar ... # To access the value
.... mod.gvar = 456 ... #To change the value


Using the "global" keyword has nothing to do with this. And in fact
Python does not even have global variables in the sense you may be thinking.

If you wish to provide a function in mode.py that sets the value then
this would fail

mod.py:
gvar=123
def SetGvar(v):
gvar = v

because the gvar inside the function is local to that function. This
is the proper place to use the "global" keyword.

mode.py:
gvar = 123
def SetGvar(v):
global gvar # Makes gvar refer to the (global) module level gvar
gvar = v

Gary Herron

Krister Svanlund

2/20/2010 8:03:00 PM

0

On Sat, Feb 20, 2010 at 8:25 PM, egasimus <fallenblood@gmail.com> wrote:
> Hi, newbie here. I've read on using the 'global' keyword being
> discouraged; then what is the preferred way to have something, for
> example a class containing program settings, accessible from
> everywhere, in a program spanning multiple files?
> --
> http://mail.python.org/mailman/listinfo/p...
>

There is probably a smarter way but I would recommend passing a
settings object around.

Steven D'Aprano

2/21/2010

0

On Sat, 20 Feb 2010 11:25:46 -0800, egasimus wrote:

> Hi, newbie here. I've read on using the 'global' keyword being
> discouraged; then what is the preferred way to have something, for
> example a class containing program settings, accessible from everywhere,
> in a program spanning multiple files?

Such a thing is no different from keeping many global settings. It's not
the global KEYWORD that is discouraged, but the over-use of global
variables itself.

See, for example:

http://c2.com/cgi/wiki?GlobalVaria...
http://weblogs.asp.net/wallen/archive/2003/05/08...


In a nutshell: shared variables introduce tight coupling between parts of
your code that should be independent. Whether they are truly global, or
passed around in a Settings object, makes little or no difference. In
both cases, the risks and problems are identical.

Let me give a fanciful example: suppose your refrigerator, oven and
shower all shared a single global temperature setting instead of having
three local temperature settings. This would be a recipe for disaster:
every operation to any of the three would need to carefully save the
current temperature setting, and there's the constant risk of scalding
showers, ruined foods, and undercooked meals.

Having three different global temperature settings helps a bit, but that
just expands the number of variables that everything has access too. Why
do the television and the garage door need access to any temperature
setting, let alone all three?

Python globals aren't as bad as in some other languages, because "global"
means global to a single module, not global to the entire program, but if
you create a Settings object and import it from module to module, you
break the encapsulation, *and* you then have the problem of needing to
make sure it is initialised properly.

http://archive.eiffel.com/doc/manuals/technology/bmarticles/joop/gl...

Global constants also aren't as bad, because the coupling is weaker.
Unfortunately Python doesn't have constants, except by convention, so
there's always the risk that some badly-behaved function might decide to
redefine (say) math.pi to 3.15.

Unfortunately, you probably can't get rid of globals altogether, but the
tricks are (1) use as few of them as possible, and (2) make them local as
quickly as possible.

For example, you might have something like this:

default_header = "My special header" # A global

for option, argument in command_line:
if option == "--header":
default_header = argument

class Printer:
def print_page(self, text, header=None):
if header is None:
header = default_header
print header
print text


I would limit the scope of default_header so that it was local to the
printer class:


for option, argument in command_line:
if option == "--header":
Printer.default_header = argument

class Printer:
default_header = "My special header"

def print_page(self, text, header=None):
if header is None:
header = self.default_header
print header
print text

You might not be able to completely eliminate either globals, or a
Settings object, but you should be able to make it much, much smaller.

In practice, to make testing easier, I'd split the command line
processing into two steps: a function that reads and processes the
command line into a single local settings object, then a separate
function that applies those settings to the various classes. The function
that reads the command line doesn't need to know where the settings
eventually end up, and the function that applies the settings doesn't
need to know where they come from. All the coupling between settings and
classes is in one place, the second function, instead of scattered all
over your code.



--
Steven

Jonathan Gardner

2/21/2010 1:34:00 AM

0

On Sat, Feb 20, 2010 at 11:25 AM, egasimus <fallenblood@gmail.com> wrote:
> Hi, newbie here. I've read on using the 'global' keyword being
> discouraged; then what is the preferred way to have something, for
> example a class containing program settings, accessible from
> everywhere, in a program spanning multiple files?

One of the powerful concepts to come out of Lisp was "dynamic scope".
This is the ideal solution for configuration and logging.

"Lexical scope" is where the value of the variables are found in the
file it was defined in. This is what Python does, and it's what you're
used to in most other languages.

"Dynamic scope" is where the value of the variables are found by
looking up the call stack. This is similar to perl's "local" keyword.
It's also similar to environment variables, if you consider programs
that run programs that run programs as similar to functions. (If
you're familiar with neither, then that's fine.)

This is useful for a program where you know it needs configuration or
logging, but you don't want to have to specify it as part of the
program. Whoever calls the program needs to configure it and setup
their logger before calling it.

Unfortunately, there is no built-in way to deal with dynamic variables
in Python. But that doesn't mean you can't emulate it explicitly and
clearly!

In order to emulate it, you simply pass the configuration and such
down to the functions you call. Each function or class instance needs
to pass it further along. So, rather than:

def foo():
bar()

def bar()
...

you need to have:

def foo(config):
bar(config)

def bar(config):
...

Obviously, only certain functions care for the config and logging
setup. Others don't. And obviously, you can modify the value on the
way down. Just be sure to make a copy or you'll change the entire
stack's value for that configuration.

There are a couple of systems that do something close to this. I don't
know of any, except SQLAlchemy, that does this exactly right. WSGI is
also a system that does this right, although I don't know that many
people realize it.

Usually, what people end up doing is setting the config values to some
global in some module. Then everyone is expected to look in that
module for the config. This ends up being messy, particularly for
testing where you'd like to mess with the config without messing with
THE config. You can tell it's messy because people end up writing code
like this:

old_value = get_config(...)
set_config(... some new value ...)
... do your stuff ...
set_config(...back to old_value...)

This kind of code is hard to get right. (What happens if there is an
exception before you set the old config back? That's right, you need a
try-finally block.)

In terms of "global", you should only really use "global" when you are
need to assign to a lexically scoped variable that is shared among
other functions. For instance:

def foo():
i = 0
def inc(): global i; i+=1
def dec(): global i; i-=1
def get(): return i
return (inc, dec, get)

This really isn't that common, although it is useful. Note that the
above might be better organized into a class instance.

Good luck.

--
Jonathan Gardner
jgardner@jonathangardner.net

Steven D'Aprano

2/21/2010 1:54:00 AM

0

On Sat, 20 Feb 2010 17:34:15 -0800, Jonathan Gardner wrote:

> In terms of "global", you should only really use "global" when you are
> need to assign to a lexically scoped variable that is shared among other
> functions. For instance:
>
> def foo():
> i = 0
> def inc(): global i; i+=1
> def dec(): global i; i-=1
> def get(): return i
> return (inc, dec, get)

That doesn't do what you think it does:


>>> def foo():
.... i = 0
.... def inc(): global i; i+=1
.... def dec(): global i; i-=1
.... def get(): return i
.... return (inc, dec, get)
....
>>> inc = foo()[0]
>>> inc()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in inc
NameError: global name 'i' is not defined


The problem is that i is not global. Inside the inc and dec functions,
you need to declare i nonlocal, not global, and that only works in Python
3 or better.



--
Steven

Jonathan Gardner

2/21/2010 5:42:00 AM

0

On Sat, Feb 20, 2010 at 5:53 PM, Steven D'Aprano
<steve@remove-this-cybersource.com.au> wrote:
> On Sat, 20 Feb 2010 17:34:15 -0800, Jonathan Gardner wrote:
>> In terms of "global", you should only really use "global" when you are
>> need to assign to a lexically scoped variable that is shared among other
>> functions. For instance:
>>
>> def foo():
>>     i = 0
>>     def inc(): global i; i+=1
>>     def dec(): global i; i-=1
>>     def get(): return i
>>     return (inc, dec, get)
>
> That doesn't do what you think it does:
>
>
>>>> def foo():
> ...     i = 0
> ...     def inc(): global i; i+=1
> ...     def dec(): global i; i-=1
> ...     def get(): return i
> ...     return (inc, dec, get)
> ...
>>>> inc = foo()[0]
>>>> inc()
> Traceback (most recent call last):
>  File "<stdin>", line 1, in <module>
>  File "<stdin>", line 3, in inc
> NameError: global name 'i' is not defined
>
>
> The problem is that i is not global. Inside the inc and dec functions,
> you need to declare i nonlocal, not global, and that only works in Python
> 3 or better.
>

Oops. :-(

--
Jonathan Gardner
jgardner@jonathangardner.net