there is a long discussion in the following two threads:
http://groups.google.com/group/sympy-patches/browse_thread/thread/83c33c0aa8560475/
http://groups.google.com/group/sympy-patches/browse_thread/thread/628ebc799ee57ec1
(don't forget to click next at the bottom of the page to get to the next pages).
First note, that our current .subs() is verstile enough to support
almost all kinds of syntax. For example:
In [1]: e = x**y+3*x+sin(y)
In [2]: e
Out[2]:
y
3⋅x + sin(y) + x
In [3]: e.subs(x, z)
Out[3]:
y
3⋅z + sin(y) + z
In [4]: e.subs({x: z})
Out[4]:
y
3⋅z + sin(y) + z
In [5]: e.subs(dict(x=z))
Out[5]:
y
3⋅z + sin(y) + z
In [6]: e.subs({x: 1, y: 2})
Out[6]: 4 + sin(2)
In [7]: e.subs(dict(x=1, y=2))
Out[7]: 4 + sin(2)
However, it may be useful to allow f(x=1, y=2) directly instead of the
equivalent e.subs(dict(x=1, y=2)).
There are pros and cons of each approach, so let me summarize it:
1) do not allow the e(x=1) syntax, i.e. raise an exception in e.__call__
this doesn't cause any problems
2) implement e.__call__ along these lines:
def __call__(self, *args, **kwargs):
return self.subs(kwargs)
so now e(x=1, y=2) will be possible. A huge disadvantage is that the
following code will fail:
x = Symbol("a")
f = x*y
f(x=3)
This is because .subs() will try to substitute for Symbol("x"), but
there is no Symbol("x") in the expression, only Symbol("a").
Another problem is that what if your Symbol contains characters that
cannot be in a Python variable, like "\", "-", ..., then it will not
work.
3) implement e.__call__ along these lines (nice trick by Riccardo):
def __call__(self, *args, **kwargs):
import inspect
frame = inspect.currentframe().f_back.f_globals
nkwargs = {}
try:
for k, v in kwargs.iteritems():
nkwargs[eval(k, frame)] = v
finally:
del frame
return self._subs_dict(nkwargs)
In [1]: t = Symbol("theta")
In [2]: f = x+t**2
In [3]: f(x=10,t=3)
Out[3]: 19
Now it correctly finds the right variable to substitute for. However,
I and Brian don't like this, because playing with frames is a messy
stuff.
Read also the threads for more opinions. For comparison, here is what Sage does:
sage: t = var("theta")
sage: t
theta
sage: f = x+t**2
sage: f(x=10,t=3)
theta^2 + 10
So Sage chose the option 2).
So which option should we choose?
Ondrej
I think we should avoid option 2.
I prefer something like:
f({x:2, y:3})
it's 2 character longer but is more pythonic, and it works.
Using this we can call _subs_dict from __call__ without modifying Basic.subs.
Cheers,
Riccardo
> Ondrej
Oh, I like the look of this (no idea about implementation).
Just to make it clear --- do you like the look of this
e.subs(dict(x=1, y=2)) or this e.subs(x=1, y=2)?
Ondrej
I like the look of the dictionary syntax, although I think e.subs({x:1, y:2})
looks better than e.subs(dict(x=1, y=2)).
I also prefer the syntax:
f({x:2,y:3}) or the implied syntax f(dict(x=2,y=2))
as opposed to
f(x=2,y=3)
for exactly the reasons all of you stated above. Brian has expressed
his opinion here:
http://groups.google.com/group/sympy-patches/msg/fcedf3feed505e54
http://groups.google.com/group/sympy-patches/msg/6e23272b38c6249b
So let's implement it. I am sorry Lance it has taken so long to
discuss this, I think noone has realized all the possible problems
that arised.
Thanks,
Ondrej
Thanks a lot Lance!
Riccardo