3

I'm building a module where there are a whole lot of diverse objects which need to be controlled by several variables.

If this were C, I would pass the objects a pointer to the variable / object I want them to watch, have the application change the variable as it needs to, and the objects in the module would "see" the new value.

In module foo.py:

class foo(object):
  def __init__(self, varToWatch):
    self.varToWatch = varToWatch
  def DoSomething(self):
    print self.varToWatch

In application, bar.py:

import foo
x = "before"
y = foo.foo(x)
y.DoSomething()
x = "after"
y.DoSomething()

This doesn't work, of course, because the original value of x (or a reference to the original value of x?) is stored in y. You get "before" "before".

So, I try...

  1. Passing the name of a global, and referring to the global by name in foo. No go, because the global is in the application's space, not in the module.
  2. Using a global in the foo namespace, but I don't know how to address that space by variable name, and we're currently doing import * from foo rather than import foo. Changing this would be painful.
  3. Make a dictionary in the module and pass the name of a key in the dictionary to the objects in the module. Manipulate the contents of the dictionary from the application. Basically, I'm defining a new global space and sticking variables in it. I see no reason it wouldn't work, but it feels untidy to me. Is that just me being unfamiliar with Python?

I'm not finding the kind of beautiful solutions I'm used to seeing come easily with Python, so I suspect I'm missing something.

Suggestions?

TIA, - Tim.

1
  • In foo.DoSomething(), print varToWatch should be print self.varToWatch Commented Jan 23, 2010 at 6:19

5 Answers 5

3

How about putting the control variables in a separate module -- say, settings -- and doing a separate import settings? You could then have your objects watch settings.bar and settings.quux while still doing from foo import * to clutter up your main namespace. ;-) You could also use a global object to store your settings, with mostly the same effect.

Of course, with problems such as these, one must wonder whether restructuring things to do a import foo wouldn't help things in the long run... Impossible to judge without knowing the problem, though, so I'll leave this problem to you.

Sign up to request clarification or add additional context in comments.

7 Comments

Ok, but how do I identify the variable to the classes and the application? If I do it by name, how do I get to that variable in the settings namespace? There muyst be some obvious way, but I couldn't find it. settings.global() doesn't do it.
You can simply use getattr(settings, varname) to get a variable from any object's namespace.
@tbroberg: What Max says! For example getattr(foo, "bar") returns the value of foo.bar, where foo may be a module (or any object). You'd have to use import foo rather than from foo import * to access foo in this manner, though; thus a separate module for things which you may want to access in this way may make for the cleanest code. @Max: Thanks for the comment!
@tbroberg: BTW, my second suggestion was to use a global object called, say, foo.settings; you could then use it in very much the same manner as a settings module. I have to say I find it cleaner and in some ways more robust to keep settings / configuration well separated from code. Just being able to edit them without touching the files containing the logic is important enough (helps with version control and general sanity, I find).
(...continuing...) Even for things meant as globals for runtime, it's nice to have them grouped in one place so as to ease debugging etc. Although on this front an object does have some good points, the ability to take advantage of @property comes to mind; but you can still replace a module with an object for debugging, it's basically a different type of namespace -- a nice thing about Python, this! Finally, and this wins me over, a separate module is the way to go if you're planning on documenting things properly -- a separate file for all code, comments etc. related to your globals.
|
3

The trick is to use a mutable type. Strings are immutable, so when you change the string, it creates a new string object in a new memory space. If, however, you referenced a list and appended to the list, the memory space won't change. Adapting your code...

>>> class Foo(object):
...     def __init__(self, var):
...         self.var = var
...     def show(self):
...         print self.var
... 
>>> x = ['before']
>>> y = Foo(x)
>>> y.show()
['before']
>>> x.append('after')
>>> y.show()
['before', 'after']

2 Comments

Yes, that is the best solution I came to as well (#3 in the original post). I was hoping there was a more orderly answer than wrapping them all in lists or dicts, but if there ain't then there ain't. Thanks.
I think it's better with: print self.var -> print self.var[0] and x.append('after') -> x[0] = 'after'
3

To catch all re-bindings of a name, you need to pass both a namespace and a specific name to use as the key within it. Using a dedicated dict as the namespace is fine, but the dict for the whole module (which you can get as globals()) or for any specific object o of which you want to observe an attribute (use vars(o)) are just as fine.

So:

class foo(object):
  def __init__(self, namespace, name):
    self.namespaceToWatch = namespace
    self.nameToWatch = name
  def DoSomething(self):
    print self.namespaceToWatch[self.nameToWatch]

and, for your example use, since you're at global level:

import foo
x = "before"
y = foo.foo(globals(), 'x')
y.DoSomething()
x = "after"
y.DoSomething()

4 Comments

Thanks, good tip, especially about passing in globals(). What if the parties agree to use the namespace of the module? How can the application get the module's namespace?
globals() is the namespace dictionary of the module where the call to globals() lexically resides (a shorthand for vars(sys.modules[__name__]), in a sense). If you're referring to "automagically finding the caller's globals", use of such introspection in production code is a bad idea -- anyway, you can find several SO questions and answers discussing this (both showing how, and explaining why it's not a good idea). Java has even fewer options in this regards (no globals!) so you may want to ask how it's done there in another Q.
Would you please explain or point us toward why using introspection in production code is bad? I've tried googling combinations of things like site: stackoverflow.com martelli inspect python bad but am coming up empty.
@unutbu, the general issue is that Python's introspective features are mostly focused and tuned for debugging, not production work: indeed some crucial parts, such as the ability to get to the caller's frame, are very specifically documented as not being supported in all Python implementations. So by relying on them for production work you're potentially cutting yourself off from future, highly optimized implementations (e.g. I wouldn't surprised if Unladen Swallow was one such, once it's merged into Python 3.2) -- how can this possibly be justified?!
1

Your problem here is that in C variables are data are objects. In Python, variables are always references to objects (which are data). You can easily watch an object, but not a variable. For example, x = "after" actually "unlinks" the object before from x and links a new object ("after"). This means that x and foo.varToWatch no longer refer to the same object.

If, on the other hand, you mutate the object referenced by x, both x and varToWatch still refer to the same object and the change is reflected. An example would be if x was a mutable type, like a dictionary - changing x in this case would reflect in varToWatch unless you actually do an assignment.

2 Comments

Agreed. This is kind of where I was going with the dictionary idea, and a list would work too. It just doesn't feel clean to me, but perhaps I'm still mired in my C ways.
Well, I have to agree that it isn't clean. My post was addressing the lower level issues. On the higher level, I'd suggest going with Michal's answer of having a settings or config module.
1

In a comment to Alex Martelli's solution, tbroberg asks, "What if the parties agree to use the namespace of the module? How can the application get the module's namespace?"

Here is how you could set the namespace (by default) to be the caller's namespace:

import inspect
class foo(object):
    def __init__(self, name, namespace=None):
        self.namespaceToWatch = (namespace if namespace else
                                 inspect.currentframe().f_back.f_globals)
        self.nameToWatch = name
    def DoSomething(self):
        print self.namespaceToWatch[self.nameToWatch]

Note that I changed the order of the arguments so namespace is now an optional argument.

inspect.currentframe() returns the current frame, inside foo.__init__.

inspect.currentframe().f_back returns the previous frame, from which foo('x') is being called.

inspect.currentframe().f_back.f_globals returns the global namespace of this frame. This makes

y = test.foo('x')

equivalent to

y = test.foo('x', globals())

1 Comment

Wow! It's great to see that that can be done. The level of complexity involved is a good cue that it's well off the beaten path. Thanks!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.