This patch implements something quite different from what we had discussed:

* We never discussed changing the implementation of subs.  I think
that is a separate discussion and it outside of the scope of changing
__call__.  Personally, I think the syntax for subs is fine.

* I thought we decided that __call__(*args, **kwargs) should not
simply be subs(*args, **kwargs).  The reason for this is that calling
a function is a different thing conceptually from calling a function.

The following will be very confusing to users that are used to
python's call syntax:

>>> f = x*y
>>> f(x,2)

* I thought we had also decided to implement an entirely different
approach to handling the positional part of args.

Do we need to have further discussion about what we all want to be
implemented, or do you just want to have a go at implementing the
original proposal.

Just for reference, here is how I picture the positional syntax looking:

>>> f = x*y
>>> f.bindings = (x,y)  # This tells Basic how to order the *args part of 
>>> __call__
>>> f(2,3)
6

>>> f.bindings = (y,x)
>>> f(2, z)
z*2

>>> f.bindings
(y, x)

>>> f.bindings = None
>>> f(2, 3)
this should raise an exception saying that symbols have to first be
bound to the positional arguments.

The name "bindings" could be something else, I just made that up.  But
bindings would just be a property that the implementation of __call__
uses when it gets a positional argument.

I think this syntax is non-magic and explicit and also give people
what they expect.

Cheers,

Brian

On Mon, Nov 17, 2008 at 11:04 AM, Lance Larsen <[EMAIL PROTECTED]> wrote:
>
> Here is the patch for the implicit subs call syntax (i.e. f(x=1, y=2)
> vs. f.subs({x:1,y:2}) ). I changed the behavior of the __call__ method
> in the Basic class, but this did not seem to affect any test cases. If
> this causes a problem, the __call__ can be modified to behave as
> before if a *arg value is passed in, but use subs if **kwargs are
> passed in. I think it is more straight forward to just make __call__
> an alias for subs so that is what I did. However, there may be use
> cases that require the old __call__ code that I am not aware of (there
> didn't appear to be in the test cases). I had trouble setting up the
> smtp server for patchbomb on my machine, so I am sending this manually
> via gmail using the patchbomb output. Hope it works. :)
>
> -Lance Larsen
>
>
> # HG changeset patch
> # User Lance Larsen
> # Date 1226946818 25200
> # Node ID 35c17486e4205095a59d2bb8cd421f48925472a5
> # Parent  e8ef8227fecd5271cf2680af328525f5510734b7
> [mq]: implicit_subs_call_syntax.patch
>
> diff -r e8ef8227fecd -r 35c17486e420 sympy/core/basic.py
> --- a/sympy/core/basic.py       Fri Nov 14 17:01:51 2008 +0100
> +++ b/sympy/core/basic.py       Mon Nov 17 11:33:38 2008 -0700
> @@ -87,10 +87,13 @@
>     classnamespace = {}
>     singleton = {}
>
> -    def __init__(cls, *args, **kws):
> +    def __init__(cls,*args,**kws):
>         n = cls.__name__
>         c = BasicMeta.classnamespace.get(n)
> -        BasicMeta.classnamespace[n] = cls
> +        if c is None:
> +            BasicMeta.classnamespace[n] = cls
> +        else:
> +            print 'Ignoring redefinition of %s: %s defined earlier than %s' 
> % (
> n, c, cls)
>         super(BasicMeta, cls).__init__(cls)
>
>         # --- assumptions ---
> @@ -896,26 +899,41 @@
>         """
>         return self
>
> -    def subs(self, *args):
> -        """
> -        Substitutes an expression.
> +    def subs(self, *args, **kwargs):
> +        """Substitutes an expression.
>
>         Calls either _subs_old_new, _subs_dict or _subs_list depending
> -        if you give it two arguments (old, new), a dictionary or a list.
> +        if you give it two arguments (old, new), a dictionary, a list or
> +        pass in named parameters. When named parameters are passed in,
> +        these are converted to a dictionary and _subs_dict is called.
>
>         Examples:
>
>         >>> from sympy import *
>         >>> x,y = symbols('xy')
> -        >>> (1+x*y).subs(x, pi)
> -        1 + pi*y
> +        >>> (1+x*y).subs(x=pi, y=2)
> +        1 + 2*pi
>         >>> (1+x*y).subs({x:pi, y:2})
>         1 + 2*pi
>         >>> (1+x*y).subs([(x,pi), (y,2)])
>         1 + 2*pi
> +        >>> (1+x*y).subs(x, pi)
> +        1 + pi*y
>
> +        Subs can be called implicitly as well as shown in the following
> +        examples:
> +
> +        >>> f = 1+x*y
> +        >>> f(x=pi, y=2)
> +        1 + 2*pi
> +        >>> f({x:pi, y:2})
> +        1 + 2*pi
> +        >>> f([(x,pi), (y,2)])
> +        1 + 2*pi
> +        >>> f(x, pi)
> +        1 + pi*y
>         """
> -        if len(args) == 1:
> +        if len(args) == 1 and len(kwargs) == 0:
>             sequence = args[0]
>             if isinstance(sequence, dict):
>                 return self._subs_dict(sequence)
> @@ -923,9 +941,11 @@
>                 return self._subs_list(sequence)
>             else:
>                 raise TypeError("Not an iterable container")
> -        elif len(args) == 2:
> +        elif len(args) == 2 and len(kwargs) == 0:
>             old, new = args
>             return self._subs_old_new(old, new)
> +        elif len(args) == 0 and len(kwargs) > 0:
> +            return self._subs_dict(kwargs)
>         else:
>             raise Exception("subs accept either 1 or 2 arguments")
>
> @@ -1932,9 +1952,24 @@
>         from sympy.integrals import integrate
>         return integrate(self, *args, **kwargs)
>
> -    #XXX fix the removeme
> -    def __call__(self, *args, **removeme):
> -        return Function(self[0])(*args)
> +    def __call__(self, *args, **kwargs):
> +        """Alias for calling the subs function.
> +
> +        Examples:
> +
> +        >>> from sympy import *
> +        >>> x,y = symbols('xy')
> +        >>> f = 1+x*y
> +        >>> f(x=pi,y=2)
> +        1 + 2*pi
> +        >>> f({x:pi, y:2})
> +        1 + 2*pi
> +        >>> f([(x,pi), (y,2)])
> +        1 + 2*pi
> +        >>> f(x, pi)
> +        1 + pi*y
> +        """
> +        return self.subs(*args, **kwargs)
>
>     def __float__(self):
>         result = self.evalf()
> diff -r e8ef8227fecd -r 35c17486e420 sympy/core/tests/test_subs.py
> --- a/sympy/core/tests/test_subs.py     Fri Nov 14 17:01:51 2008 +0100
> +++ b/sympy/core/tests/test_subs.py     Mon Nov 17 11:33:38 2008 -0700
> @@ -169,3 +169,16 @@
>     assert (f(x,y)).subs(f,sin) == f(x,y)
>     assert (sin(x)+atan2(x,y)).subs([[atan2,f],[sin,g]]) == f(x,y) + g(x)
>     assert (g(f(x+y, x))).subs([[f, l], [g, exp]]) == exp(x + sin(x + y))
> +
> +def test_subs_call_syntax():
> +    a, b, x, y, p, q = map(Symbol, "abxypq")
> +    f = 2*a*x+3*b*y
> +    assert f(x=1, y=2) == 2*a + 6*b
> +    assert f({a*x:p,b*y:q}) == 2*p + 3*q
> +def test_implicit_subs_call_syntax():
> +    a, b, x, y, p, q = map(Symbol, 'abxypq')
> +    f = 2*a*x + 3*b*y
> +    assert f(x=1, y=2) == 2*a + 6*b
> +    assert f({a*x:p,b*y:q}) == 2*p + 3*q
> +    assert f([(a*x,p),(b*y,q)]) == 2*p + 3*q
> +    assert f(a*x, p) == 2*p + 3*b*y
> \ No newline at end of file
>
> >
>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"sympy-patches" group.
To post to this group, send email to sympy-patches@googlegroups.com
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/sympy-patches?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to