0

What I am trying to do, is creating a module, with a class; and a function, which is an interface of that class; and a variable name on-the-fly in this function, which is pointing to an instance of that class. This function and the class itself should be in a separate module, and their usage should be in a different python file.

I think, it's much easier to understand what I am trying to do, when you are looking at my code:

This is the first.py:

class FirstClass:
    def setID(self, _id):
        self.id = _id
    def func(self):
        pass

# An 'interface' for FirstClass
def fst(ID):
    globals()['%s' % ID] = FirstClass(ID)
    return globals()['%s' % ID]

Now, if I'm calling fst('some_text') right in first.py, the result is pretty much what I dreamed of, because later on, any time I write some_text.func(), it will call the func(), because some_text is pointing to an instance of FirstClass.

But, when the second.py is something like this:

from first import fst

fst('sample_name')
sample_name.func()

Then the answer from python is going to be like this:

NameError: name 'sample_name' is not defined.

Which is somewhat reasonable.. So my question is: is there a "prettier" method or a completely different one to do this? Or do I have to change something small in my code to get this done?

Thank you!

Peter Varo
  • 10,078
  • 6
  • 42
  • 63
  • 2
    Why do you think you need this? – Burhan Khalid Mar 25 '13 at 10:09
  • 1
    global is evil. never use global. understand that if a external reader read your program, he will never understand how this variable is set hier. never use magic, or well document it. `explicit is better than implicit` : The Zen of Python – ornoone Mar 25 '13 at 10:12
  • @BurhanKhalid: Actually I am working on a small GUI framework for designers, and I thought it would be pretty nice, to call only one function (e.g. `rectangle()`) not a whole object creation like `something = Rectangle('something')`. – Peter Varo Mar 25 '13 at 10:14
  • @omoone: Yeah, I know Zen of Python, I use it everyday, but in my case that is the point: to hide where it is came from, since what I am doing is a small and limited framework for designers. – Peter Varo Mar 25 '13 at 10:16

1 Answers1

2

Don't set it as a global in the function. Instead, just return the new instance from the function and set the global to that return value:

def fst(ID):
    return FirstClass(ID)

then in second.py:

sample_name = fst('sample_name')

where, if inside a function, you declare sample_name a global.

The globals() method only ever returns the globals of the module in which you call it. It'll never return the globals of whatever is calling the function. If you feel you need to have access to those globals, rethink your code, you rarely, if ever, need to alter the globals of whatever is calling your function.

If you are absolutely certain you need access to the caller globals, you need to start hacking with stack frames:

# retrieve caller globals
import sys
caller_globals = sys._getframe(1).f_globals

But, as the documentation of sys._getframe() states:

CPython implementation detail: This function should be used for internal and specialized purposes only. It is not guaranteed to exist in all implementations of Python.

Martijn Pieters
  • 889,049
  • 245
  • 3,507
  • 2,997
  • Yes, I know that, (btw: thanks for the quick reply) but, that's actually what I am trying to avoid: to write the "name" twice. Because it also could be a solution to do this: `sample_name = FirstClass('sample_name')` – Peter Varo Mar 25 '13 at 10:11
  • 1
    @PeterVaro: I added more details as to how to do this anyway. This is akin to giving you a gun that is perpetually aimed the region containing your foot, so if you pull the trigger and hit said extremity, you have been warned! :-) – Martijn Pieters Mar 25 '13 at 10:12
  • You mean if I copy my "interface function" to the second.py? Because I want to avoid that also.. That's what I'm looking for: separate the class and its interface to a different module, and the user, who uses this function should never use `sys` and so on. These users need to `import mymodule` and that's all. – Peter Varo Mar 25 '13 at 10:25
  • No, I don't mean that at all. The `sys._getframe(1)` code would live in `first.py`, `caller_globals` then would point to the `second.py` globals if that is where you call `fst()` from. – Martijn Pieters Mar 25 '13 at 10:26
  • And I understand what you are doing, but I think you need to step away from that idea. You can *always* give store objects in different names already, so it is *perfectly fine* to do `spam = fst('ham')` (so store the `ham` interface in a local name `spam`) in Python, for example. The idea of forcing that the interface object is added to your globals just by calling a function is implicit and unexpected behaviour. This goes *against* the Python philosophy (*Explicit is better than implicit*). – Martijn Pieters Mar 25 '13 at 10:28
  • Well, now I understand that — it is also a dirty solution :) And you are absolutely right in the spam-ham dilemma. So, now, you said you understand what I need, and you think there is no other solution to this, just this _getframe? Because if that is the way, I don't think I ever going to use that.. `var = Class('id')` is going to be the prettiest solution, and there is no way to make that simpler. – Peter Varo Mar 25 '13 at 10:32
  • @PeterVaro: That is the *only* way to set values directly in the globals of a caller, yes. – Martijn Pieters Mar 25 '13 at 10:33
  • OK, well thanks for these great replies, I guess that's all for now — **No solution is better than a dirty one** :) Thank you very much! – Peter Varo Mar 25 '13 at 10:37
  • I actually a found a really, really dirty way, for doing this :/ I created a file opening/reading function in the `__init__.py` of my module directory, which reads the `iface.py` file in the lib. I copied the interface-function into the iface.py, and called the function from the init with an exec statement.. I know, I know, it's awful, but it's working :) (the sad part is: I realized, it doesn't solve my original problem:)) – Peter Varo Mar 25 '13 at 12:37
  • @PeterVaro: Using `exec` is uglier still; I'd use the `sys._getframe()` trick before I'd reach for that. But you need to rethink your architecture anyway. – Martijn Pieters Mar 25 '13 at 12:50