views:

256

answers:

4

What do I pass as the first parameter "object" to the function setattr(object, name, value), to set variables on the current module?

For example:

setattr(object, "SOME_CONSTANT", 42);

giving the same effect as:

SOME_CONSTANT = 42

within the module containing these lines (with the correct object).

I'm generate several values at the module level dynamically, and as I can't define __getattr__ at the module level, this is my fallback.

+4  A: 
import sys

thismodule = sys.modules[__name__]

setattr(thismodule, name, value)

or, without using setattr (which breaks the letter of the question but satisfies the same practical purposes;-):

globals()[name] = value

Note: at module scope, the latter is equivalent to:

vars()[name] = value

which is a bit more concise, but doesn't work from within a function (vars() gives the variables of the scope it's called at: the module's variables when called at global scope, and then it's OK to use it R/W, but the function's variables when called in a function, and then it must be treated as R/O -- the Python online docs can be a bit confusing about this specific distinction).

Alex Martelli
The docs give a warning about modifying vars(). http://docs.python.org/library/functions.html#vars . When is it okay to do this?
unutbu
@~unutbu, I wouldn't really say that it's quite "okay", but it will work when you call `vars()` at module-level scope rather than inside of a function.
Mike Graham
Thanks for the information, @Mike. Is there an easy way to understand why `vars()` behaves this way (okay at module-level but not in functions)? Was it a restriction by design, or something else?
unutbu
`vars()` is equivalent to `globals()` at module scope (and so returns a true, modifiable dict) but to `locals()` at function scope (and so returns a never-to-be-modified pseudodict). I use `vars()` at module scope as it saves 3 characters, one syllable, vs its synonym-in-that-scope `globals()`;-)
Alex Martelli
Okay, that's helpful, @Alex; thanks. But why should `locals()` at function scope return a never-to-be-modified pseudodict? Would there have been something wrong with exposing a modifiable dict here?
unutbu
Yes, it would have destroyed the single most important optimization the Python compiler does: a function's local variables are **not** kept in a dict, they're in a tight vector of values, and each local variable access uses the index in that vector, not a name lookup. To defeat the optimization, forcing the dict you desire to exist, start the function with `exec ''`: time a function with a couple substantial loops each way, and you'll see the importance of this core optimization to Python's performance.
Alex Martelli
Hm is there any reason I can't just do `globals()[name] = value`, keeping in mind that "This is always the dictionary of the current module", and that `vars()` is equivalent to `globals()` in your answer? Is it safe to do this?
Matt Joiner
@Matt, sure, it's safe (`vars` and `globals` are equivalent at module top level, and if you're in a function it's `globals` you want to use to set a module-level variable anyway). Though you _can_ use `setattr`, as you chose to ask in this question, you can also get the same effect with the indexed assignment in question. And, by the way, you _can_ also have a `__getattr__` on what behaves just about like a module (can be imported etc), but, I guess that's yet another issue.
Alex Martelli
@Alex Martelli: As there is no way for me to message you here, perhaps you might take a look at http://stackoverflow.com/questions/2447353/getattr-on-a-module, which is of great interest to me.
Matt Joiner
@Alex: Thank you so much for your enlightening answers, above.
unutbu
Beautiful is better than ugly. Explicit is better than implicit. Simple is better than complex. Readability counts. Special cases aren't special enough to break the rules. There should be one-- and preferably only one --obvious way to do it. If the implementation is hard to explain, it's a bad idea.
msw
@msw, I think you forgot "practicality beats purity";-).
Alex Martelli
@Matt, ah yes, @Håvard S's solution in that Q is what I had in mind (having originally published it myself in the Cookbook, I upvoted when I saw it given on SO;-) -- that it doesn't work after `import *` I see as one of the zillion problems with `import *`, not with the solution in question; that it doesn't work on barenames ("within the current module" as you say) is true, but then barenames are so entirely different from qualified names that it shouldn't be expected to.
Alex Martelli
Fair nuff, so what's practical about `vars()[name] = value` other than "it saves 3 characters, one syllable" that makes it special enough to break the rules, be implicit, be unreadable, be hard to explain, and depends heavily on the knowledge of the particular implementation. Yeah, it's clever, so is the C construct `i++-++j` and if it happens to work the way you want on your platform, does anything else really matter? Do note how much explanation this so-called practical example made to an intelligent, interested reader.
msw
@msw, oh, I thought you were commenting on my several few comments (about how `locals()` can't practically be a dict), not one from 12 hours ago - hard to keep track when you give no reference. There's nothing implicit or unreadable about `vars`, it's not especially clever, it works uniformly across platforms, and the only thing that makes it confusing is that it's documented wrong in the online docs. Guess I'll edit the answer to point this out since this appears to really make you go off the deep end;-).
Alex Martelli
@msw: What are you on about?
Matt Joiner
@matt: assuming that your question is an honest one - I'm new to Python and was attracted to PEP 20 "Zen of Python" principles that make well written Python code almost self-evidently trivial. I find that overuse of `__setattr__` is apparently very seductive yet has the net effect of making code mysterious. Overuse of meta-syntactic constructs tends to make code reading difficult for exactly the reasons the C preprocessor is presently shunned for past abuses.
msw
@msw: Okay now your comments make sense. I'm just happy that it works, and that Alex's description has removed confusion on my part. I can see however, that having both `vars()` and `globals()`, and even the fact that `globals()` is a misleading name... not ideal, but who's counting?
Matt Joiner
+1  A: 
  1. You wouldn't. You would do globals()["SOME_CONSTANT"] = 42
  2. You wouldn't. You would store dynamically-generated content somewhere other than a module.
Mike Graham
Yep, `SOME_CONSTANT` computed at run-time isn't exactly constant. And if `globals()` isn't available to you then you must be reaching into another module to modify its attributes; that's bound to get people wondering.
msw
Constant and mutable are mutually exclusive. Constant and dynamically generated are not. The values I'm generating are always the same, and determined based on further "constants", to save on arithmetic and typing on my part.
Matt Joiner
A: 

Strange as it might sound, it's perfectly legal to import a module within itself.

# -- foo.py --
import foo
foo.SOME_CONSTANT = 42
setattr(foo, 'OTHER_CONSTANT', 'bar')
kwatford
Circular imports are more than a little precarious.
Mike Graham
@Mike Graham - While I get that impression that it isn't a good idea, I'm not sure off hand *why*. The module object is (I believe) guaranteed to exist in the modules table by the time any of the module code gets run, and the code will only be run once, barring reloads. It seems like it might be a good idea to delete the variable once you're done with it, though, in case anything tries to crawl the module recursively.
kwatford
An interesting idea, but I'm not sure I want to try it :P
Matt Joiner
+2  A: 

If you must set module scoped variables from within the module, what's wrong with global?

# my_module.py

def define_module_scoped_variables():
    global a, b, c
    a, b, c = 'a', ['b'], 3

thus:

>>> import my_module
>>> my_module.define_module_scoped_variables()
>>> a
NameError: name 'a' is not defined
>>> my_module.a
'a'
>>> my_module.b
['b']
msw
I like that you demonstrate the variables' scopes are constrained to the module. Thanks!
Matt Joiner
Yeah, I've always (where "always" is defined as the "last few months I've been learning Python") found that `global but not really` declaration puzzling. I suppose it may be a historical relic that predates module namespaces.
msw