-3

Here is what I'd like to be able to do

x = 1

f(x+3) # returns 'x+3' (a string)

Is this possible?

(Lisp and C can do this, but f would have to be a macro)

MWB
  • 11,740
  • 6
  • 46
  • 91
  • Seems unlikely given that `x + 3` would be evaluated before the function call. The function would simply see the argument with value `4`. – 101 Apr 05 '20 at 23:31
  • @101 I don't mind it if `x+3` gets evaluated. I just want to get the string `'x+3'` from inside the function. – MWB Apr 05 '20 at 23:32
  • 2
    There could be hacky ways to do this, but *why* do you want to do this? This screams [X-Y problem](https://meta.stackexchange.com/questions/66377/what-is-the-xy-problem). I just ask, because your question might be better answered and better received if you provide some background and motivation. For example, do you control `def f`? – juanpa.arrivillaga Apr 05 '20 at 23:33
  • This would be straightforward in something like R, but in Python, there's no good option. (Someone will probably suggest reading the caller's source code with `inspect`, but that's got so many silent failure cases and loud, messy failure cases that it's not worth going for.) – user2357112 Apr 05 '20 at 23:37
  • It's not possible. When a function is called, it only receives the value, it doesn't receive the original expression. Introspection doesn't provide access to this, because the caller has been compiled. – Barmar Apr 05 '20 at 23:40
  • 2
    @Barmar no, you could hack together something with introscection, at least in CPython, you can get the source code and line number using `inspect`. As others have mentioned though, it will be extremely brittle and very hacky. – juanpa.arrivillaga Apr 05 '20 at 23:42
  • @juanpa.arrivillaga wouldn't work in REPL, I'm guessing... – MWB Apr 05 '20 at 23:43
  • Yes, except that you do *not* do this as a function. You're typing too many characters. Simply remove the function call and replace it with quotation marks: `"x+3"`. – Prune Apr 05 '20 at 23:51
  • It could. Again, though, this is going to require heavy, brittle introspection. maybe it will be made easier with the up and coming `ast.unparse` in Python 3.9. Although, it won't provide you *exactly* the calling string. Again, **why** do you want to do this? Python is not LISP. Far from it, really. LISP is a [homoiconic language](https://en.wikipedia.org/wiki/Homoiconicity) python is not. – juanpa.arrivillaga Apr 05 '20 at 23:57
  • @juanpa.arrivillaga I have a very good reason, but if it's hard and brittle in Python, I guess I won't bother. I think it was a valid question and others might find it interesting, especially since this functionality exists and is readily available in other languages. – MWB Apr 05 '20 at 23:58
  • @MaxB well LISP maybe, I can see that. Not really C (a macro being a pretty special). But Python doesn't have the nice feature of homoiconicity, and uses a strict/eager evaluation strategy, so it's really not geared towards this kind of thing. I also think this would be strictly impossible if the caller were a built-in function (a function written as a C-extension). I'm only suggesting you give more background/motivation so your question is better received. Take my advice or leave it. – juanpa.arrivillaga Apr 06 '20 at 00:08
  • @juanpa.arrivillaga Both Lisp and C are eager. Laziness has nothing to do with this. Homoiconicity also has little to do with this (C is not homoiconic) – MWB Apr 06 '20 at 00:12
  • Aren't macros are a special case? Is it possible without macros in C? I'm not saying these things are required, I'm saying they would make it easier. In python, this requires extracting source code from the stack frame (not guaranteed possible) and then either using a string processing hack, or more sanely, the `ast` module and `ast.parse` to get a representation of the abstract syntax tree, and then trying to reconstruct the source code string from the tree (again, this will be easier when 3.9 ads an `ast.unparse`). Of course, the language could always special-case this. But it doesn't. – juanpa.arrivillaga Apr 06 '20 at 00:18

2 Answers2

4

This is possible, despite what others have said. The only way I can think of to do this is to inspect the source code. I cannot recommend doing this in reality. It's a fun little toy, though.

from inspect import currentframe
import linecache
import re

def f(x):

    cf = currentframe()
    calling_line_number = cf.f_back.f_lineno

    calling_line = linecache.getline(__file__, calling_line_number)
    m = re.search(r"^.*?f\((?P<arg>.*?)\).*$", calling_line)
    if m:
        print (m.group("arg"))

x = 1
f(x + 3)

Prints x + 3, which is exactly what was passed to f().

Charles Angus
  • 394
  • 1
  • 7
  • The fragility the comments speak of (aside from other examples) is the case where you define `v = f` and `v(...)` results in the regex not maching. It is possible to traverse the assignments until the `qualname` of appears - but seems hard and still fragile. But I still like your quick and dirty solution. – modesitt Apr 24 '20 at 18:05
0

If the value that you want to add(3) is a constant,

`def f(x):
    return f"{x}+3"

f(x)

If it is not, you can pass two parameters,

def f(x, y):
    return f"{x} + {y}"

f(x, y)


deadshot
  • 8,881
  • 4
  • 20
  • 39
RockNav
  • 79
  • 2
  • 8
  • No, this doesn't answer the questino. The OP wants how the function is being called in the source code as a string. This assumes it's always `f(x + something)` – juanpa.arrivillaga Apr 05 '20 at 23:48