1

Assuming that I have foo.py like below. This file is not created by me, so I want neither to modify nor copy this. In other words, foo.py is in some extra package I installed.

# foo.py

def bar():
  print('This is bar')

def foo():
  print('something')
  bar()
  print('something')

Then, I want to implement foo_as_baz() behaving as comments.

# baz.py

from foo import foo

def baz():
  print('This is baz')

def foo_as_baz():
"""
This function is expected to behave as below
  print('something')
  baz()
  print('something')
"""  
  pass

I tried below one but it does not work since the namescope differs.

def foo_as_baz():
  bar = baz # I expect this `baz` affects `bar` function in `foo`
  foo()
Hironori
  • 27
  • 4
  • Possible duplicate of [Changes made to variable not reflected in console](https://stackoverflow.com/questions/56615309/changes-made-to-variable-not-reflected-in-console) – LiuXiMin Jun 17 '19 at 03:22
  • look at my another answer a few days ago. look different, but same issue in fact. – LiuXiMin Jun 17 '19 at 03:24
  • The underlying issue is the same as in that question, but I think there's some possible nuance here about mocking and how module attributes work. – Josh Karpel Jun 17 '19 at 03:28
  • @JoshKarpel I think understand the underlying issue is key. After that, you can do whatever you want by any possible trick. – LiuXiMin Jun 17 '19 at 03:37
  • @LiuXiMin Thanks. if `a` shows zeros, this issue is the same as yours. ``` In [5]: a = np.zeros(shape=(4, 2)) In [6]: func() [666. 1.] [1. 1.] [1. 1.] [1. 1.] ``` – Hironori Jun 17 '19 at 03:43
  • @Hironori Yes, it does. `a` is rebound to zeros, but `func` can not see it unless you redefine a new `func`. – LiuXiMin Jun 17 '19 at 03:56

2 Answers2

0

You can pass the function itself as an argument to foo by slightly modifying how foo works

def foo(func=bar):
   print('something')
   func()
   print('something')

def foo_as_baz():
   foo(baz)

By adding bar as the default argument to foo you retain the original behaviour

rdas
  • 20,604
  • 6
  • 33
  • 46
  • Yes, but `foo()` has no argument and I do not want to modify `foo`, if possible. I know this question is much odd. – Hironori Jun 17 '19 at 03:18
  • But `foo` when called without any arguments still exhibits the same behaviour. So what would be the issue? – rdas Jun 17 '19 at 03:32
  • @Hironori rebind an object to bar in your file can not affect the bar in `foo`, it is the scope rule of Python. – LiuXiMin Jun 17 '19 at 03:35
0

For the record: don't do the thing below the horizontal rule in normal code. It's confusing and largely pointless.

If you're doing this kind of thing to test your code, I recommend the standard library unittest.mock package, which provides a systematic way to replace Python objects with fake Python objects for testing purposes.

If you think you have some real non-testing use case, please elaborate...


# foo.py

def bar():
    print("This is bar")

def foo():
    print("something")
    bar()
    print("something")

And then then in another file:

import foo


def baz():
    print("This is baz")


def foo_as_baz():
    original = foo.bar  # save a copy of the original bar()

    foo.bar = baz       # make foo.bar point to baz()

    foo.foo()           

    foo.bar = original  # cleanup by restoring bar()


foo_as_baz()

The key difference from your attempt is that I did import foo, which lets me look into the actual module namespace foo. I swap foo.bar for baz, then call foo.foo(), then switch foo.bar back to the original bar function.

This works because functions are effectively attributes of modules. Therefore, we can reassign those attributes as if they were attributes of any other object (because, in Python, everything, including modules, are objects).

Josh Karpel
  • 2,110
  • 2
  • 10
  • 21
  • As you said, this is a very dangerous code. Do you think which is better to copy all the `foo` function or your implementation shown in the above? @JoshKarpel – Hironori Jun 17 '19 at 03:50
  • @Hironori Could you clarify what you mean by "copy all of the `foo` function"? – Josh Karpel Jun 17 '19 at 04:03
  • When I wrote this, I meant copy all the source of `foo` to another file. But now, I realize that there is another way based on `new_foo = foo.copy()`. I think this better than the others. Thanks. – Hironori Jun 17 '19 at 04:33