4

Is there a way to enter an object's namespace so that I can use its methods as though they were global? I was thinking of something using the with statement.

class Bar():
    
    def methodA(self):
        # do stuff

    def methodB(self):
        # do more stuff

    def __enter__(self):
        # somehow enter object namespace / transfer methods into global namespace

    def __exit__(self, *args):
        # exit object namespace / get rid of globalized methods

foo = Bar()

with foo:
    methodA() # all works fine
    methodB()

methodA() # throws an error

This is just a thought, that might not work at all. Or maybe there's a solution without the with statement.

80KiloMett
  • 55
  • 3
  • Have you read e.g. https://stackoverflow.com/q/24920220/3001761? – jonrsharpe Nov 22 '20 at 20:00
  • Can you clarify why would want to do this? – Maximilian Peters Nov 22 '20 at 20:13
  • I see where the request comes from (that `with` construct is used in other languages). I have used it in the past. I don't think this is an advisable Python pattern to follow. This is somewhat related to having to write `self` inside methods. – progmatico Nov 22 '20 at 20:34
  • It was decided you should have to. – progmatico Nov 22 '20 at 20:35
  • 1
    @MaximilianPeters I've been playing with the Cairo graphics framework a bit lately, which involves calling different methods on the same object a bunch of times in a row. And I had a "Wouldn't it be great if ..." moment. I thought of writing a wrapper class that reduces Cairo boilerplate and manages different image layers to combine them into one in the end. – 80KiloMett Nov 22 '20 at 20:36

3 Answers3

2

This answers the original question but I suggest don't use it.


Similar to the suggested way of wKavey.

But I'm not sure why I would want to do that. I would need to make sure that there is no variable methodA in the global namespace.

class Bar():
    
    def __init__(self, value=5):
        self.value = value
        
    def methodA(self):
        return self.value

    def methodB(self):
        return -self.value

    def __enter__(self):
        global methodA
        global methodB
        methodA = self.methodA
        methodB = self.methodB

    def __exit__(self, *args):
        global methodA
        del methodA
        global methodB
        del methodB
        pass

foo = Bar()

with foo:
    print(methodA()) # all works fine
    print(methodB())

methodA() # throws an error
Maximilian Peters
  • 30,348
  • 12
  • 86
  • 99
  • 2
    Probably worth checking for a pre-existing global `methodA` so you can cache it if exists and restore it on exit. Otherwise this has the potential to clobber a name that existed before entering the context manager. – Mark Nov 22 '20 at 20:10
  • 1
    Problem is if some other place is relying on the original global value to work properly. @80KiloMett, While this looks good, I do not think it should be used, not because of the technique per see, but because of affecting globals. And there is no need to even imagine concurrency. You might even call the affected code (in some function call) inside the `with` block, without noticing. – progmatico Nov 22 '20 at 20:25
  • @progmatico Yeah, it doesn't look great. Especially when you have a few more than two methods you want to make available. Then you start looping through the class dict and find yourself with an object's __init__ method in global space. I don't even know the implications of that. Maybe with some book keeping of the changes one could pull it off half way clean. But that's probably not worth the effort. – 80KiloMett Nov 22 '20 at 20:54
  • @80KiloMett Maybe [PEP 555](https://www.python.org/dev/peps/pep-0555/) is of help too. Adding values to a local context (or to the local scope) instead would be less dangerous. Local contexts are thread-safe too. – progmatico Nov 22 '20 at 21:42
  • Guess I'm going to mark this as solved. With the absence of other ways to do this suggestion not to use this one, I guess it can stand as a solution. – 80KiloMett Nov 22 '20 at 22:00
0

You can probably use the techniques described here: Insert variable into global namespace from within a function?

I'd imagine it will require some bookkeeping in the __enter__ and __exit__ functions in order to clean up after itself. This isn't really something standard, so there my be some other foot-guns that I'm overlooking.

wakey
  • 2,283
  • 4
  • 31
  • 58
0

(oh. Maximilian Peters' answer is doing the same with slightly different syntax and was here first...)

nothing i'd recommend but you could do this (you get in trouble of course if there is already a methodA in the global namespace):

class Bar():

    def methodA(self):
        print("methodA called")

    def methodB(self):
        print("methodB called")

    def __enter__(self):
        g = globals()
        g["methodA"] = self.methodA
        g["methodB"] = self.methodB

    def __exit__(self, *args):
        g = globals()
        del g["methodA"]
        del g["methodB"]

foo = Bar()

with foo:
    methodA()  # all works fine
    methodB()
hiro protagonist
  • 44,693
  • 14
  • 86
  • 111