开发者

What are some Pythonic ways to share variables with objects defined in a module?

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开发者_JAVA百科 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.


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.


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']


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()


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.


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())
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜