Function Mangling - Utilities

Skip to content
Back to top
View this page
Edit this page

# Function Mangling

This module provides utilities for extracting information about python functions.

AUTHORS:

- Tom Boothby (2009): Original version in Python

- Simon King (2011): Use Cython. Speedup of `fix_to_pos`, cleaning documentation.

class sage.misc.function_mangling.ArgumentFixer

  Bases: `object`

  This class provides functionality to normalize the arguments passed into a function. While the various ways of calling a function are perfectly equivalent from the perspective of the callee, they don’t always look the same for an object watching the caller. For example,

  Sage

      sage: def f(x=10):
      ....:     return min(1,x)

  Python

      >>> from sage.all import *
      >>> def f(x=Integer(10)):
      ...     return min(Integer(1),x)

  the following calls are equivalent,

  Sage

      sage: f()
      1
      sage: f(10)
      1
      sage: f(x=10)
      1

  [Python]

      >>> from sage.all import *
      >>> f()
      1
      >>> f(Integer(10))
      1
      >>> f(x=Integer(10))
      1

  but from the perspective of a wrapper, they are different:

  Sage

      sage: def wrap(g):
      ....:     def _g(*args, **kwargs):
      ....:         print("{} {}".format(args, kwargs))
      ....:         return g(*args, **kwargs)
      ....:     return _g
      sage: h = wrap(f)
      sage: t = h()
      () {}
      sage: t = h(10)
      (10,) {}
      sage: t = h(x=10)
      () {'x': 10}

  Python

      >>> from sage.all import *
      >>> def wrap(g):
      ...     def _g(*args, **kwargs):
      ...         print("{} {}".format(args, kwargs))
      ...         return g(*args, **kwargs)
      ...     return _g
      >>> h = wrap(f)
      >>> t = h()
      () {}
      >>> t = h(Integer(10))
      (10,) {}
      >>> t = h(x=Integer(10))
      () {'x': 10}

  For the purpose of cached functions, it is important not to distinguish between these uses.

  INPUT:

  - `f` – a function

  - `classmethod` – boolean (default: `False`); `True` if the function is a classmethod and therefore the first argument is expected to be the class instance. In that case, we ignore the first argument.

  EXAMPLES:

  Sage

      sage: from sage.misc.function_mangling import ArgumentFixer
      sage: def wrap2(g):
      ....:     af = ArgumentFixer(g)
      ....:     def _g(*args, **kwargs):
      ....:         print(af.fix_to_pos())
      ....:         return g(*args, **kwargs)
      ....:     return _g
      sage: h2 = wrap2(f)
      sage: t = h2()
      ((10,), ())
      sage: t = h2(10)
      ((10,), ())
      sage: t = h2(x=10)
      ((10,), ())

  [Python]

      >>> from sage.all import *
      >>> from sage.misc.function_mangling import ArgumentFixer
      >>> def wrap2(g):
      ...     af = ArgumentFixer(g)
      ...     def _g(*args, **kwargs):
      ...         print(af.fix_to_pos())
      ...         return g(*args, **kwargs)
      ...     return _g
      >>> h2 = wrap2(f)
      >>> t = h2()
      ((10,), ())
      >>> t = h2(Integer(10))
      ((10,), ())
      >>> t = h2(x=Integer(10))
      ((10,), ())

  Sage

      sage: class one:
      ....:    def __init__(self, x=1):
      ....:       self.x = x
      sage: af = ArgumentFixer(one.__init__, classmethod=True)
      sage: af.fix_to_pos(1,2,3,a=31,b=2,n=3)
      ((1, 2, 3), (('a', 31), ('b', 2), ('n', 3)))

  Python

      >>> from sage.all import *
      >>> class one:
      ...    def __init__(self, x=Integer(1)):
      ...       self.x = x
      >>> af = ArgumentFixer(one.__init__, classmethod=True)
      >>> af.fix_to_pos(Integer(1),Integer(2),Integer(3),a=Integer(31),b=Integer(2),n=Integer(3))
      ((1, 2, 3), (('a', 31), ('b', 2), ('n', 3)))

  f
  fix_to_named(*args, **kwargs)

    Normalize the arguments with a preference for named arguments.

    INPUT:

    - any positional and named arguments.

    OUTPUT: we return a tuple

    > \((e_1, e_2, ..., e_k), ((n_1, v_1), ... , (n_m, v_m))\)

    where \(n_1, ... , n_m\) are the names of the arguments and \(v_1, ..., v_m\) are the values passed in; and \(e_1, ..., e_k\) are the unnamed arguments. We minimize \(k\).

    The defaults are extracted from the function and filled into the list `K` of named arguments. The names \(n_1, ..., n_t\) are in order of the function definition, where \(t\) is the number of named arguments. The remaining names, \(n_{t+1}, ..., n_m\) are given in alphabetical order. This is useful to extract the names of arguments, but does not maintain equivalence of

        A,K = self.fix_to_pos(...)
        self.f(*A, **dict(K))`

    and

        self.f(...)

    in all cases.

    EXAMPLES:

    Sage

        sage: from sage.misc.function_mangling import ArgumentFixer
        sage: def sum3(a, b, c=3, *args, **kwargs):
        ....:     return a + b + c
        sage: AF = ArgumentFixer(sum3)
        sage: AF.fix_to_named(1, 2, 3, 4, 5, 6, f=14, e=16)
        ((4, 5, 6), (('a', 1), ('b', 2), ('c', 3), ('e', 16), ('f', 14)))
        sage: AF.fix_to_named(1,2,f=14)
        ((), (('a', 1), ('b', 2), ('c', 3), ('f', 14)))

    Python

        >>> from sage.all import *
        >>> from sage.misc.function_mangling import ArgumentFixer
        >>> def sum3(a, b, c=Integer(3), *args, **kwargs):
        ...     return a + b + c
        >>> AF = ArgumentFixer(sum3)
        >>> AF.fix_to_named(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6), f=Integer(14), e=Integer(16))
        ((4, 5, 6), (('a', 1), ('b', 2), ('c', 3), ('e', 16), ('f', 14)))
        >>> AF.fix_to_named(Integer(1),Integer(2),f=Integer(14))
        ((), (('a', 1), ('b', 2), ('c', 3), ('f', 14)))

  fix_to_pos(*args, **kwds)

    Normalize the arguments with a preference for positional arguments.

    INPUT:

    - `*args`, `**kwds` – any positional or named arguments

    OUTPUT: we return a tuple

    > \((e_1, e_2, ..., e_k), ((n_1, v_1), ... , (n_m, v_m))\)

    where \(n_1, ... , n_m\) are the names of the arguments and \(v_1, ..., v_m\) are the values passed in; and \(e_1, ..., e_k\) are the unnamed arguments. We minimize \(m\).

    The commands

        A,K = self.fix_to_pos(...)
        self.f(*A, **dict(K))

    are equivalent to

        self.f(...)

    though defaults are extracted from the function and appended to the tuple `A` of positional arguments. The names \(n_1, ..., n_m\) are given in alphabetical order.

    EXAMPLES:

    Sage

        sage: from sage.misc.function_mangling import ArgumentFixer
        sage: def do_something(a, b, c=3, *args, **kwargs):
        ....:     print("{} {} {} {} {}".format(a, b, c, args,
        ....:                                   sorted(kwargs.items())))
        sage: AF = ArgumentFixer(do_something)
        sage: A, K = AF.fix_to_pos(1, 2, 3, 4, 5, 6, f=14, e=16)
        sage: print("{} {}".format(A, K))
        (1, 2, 3, 4, 5, 6) (('e', 16), ('f', 14))
        sage: do_something(*A, **dict(K))
        1 2 3 (4, 5, 6) [('e', 16), ('f', 14)]
        sage: do_something(1, 2, 3, 4, 5, 6, f=14, e=16)
        1 2 3 (4, 5, 6) [('e', 16), ('f', 14)]

    Python

        >>> from sage.all import *
        >>> from sage.misc.function_mangling import ArgumentFixer
        >>> def do_something(a, b, c=Integer(3), *args, **kwargs):
        ...     print("{} {} {} {} {}".format(a, b, c, args,
        ...                                   sorted(kwargs.items())))
        >>> AF = ArgumentFixer(do_something)
        >>> A, K = AF.fix_to_pos(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6), f=Integer(14), e=Integer(16))
        >>> print("{} {}".format(A, K))
        (1, 2, 3, 4, 5, 6) (('e', 16), ('f', 14))
        >>> do_something(*A, **dict(K))
        1 2 3 (4, 5, 6) [('e', 16), ('f', 14)]
        >>> do_something(Integer(1), Integer(2), Integer(3), Integer(4), Integer(5), Integer(6), f=Integer(14), e=Integer(16))
        1 2 3 (4, 5, 6) [('e', 16), ('f', 14)]