From f6452a849436c0a271f1587260f0b2b9e0510b6e Mon Sep 17 00:00:00 2001 From: Mateusz Paprocki Date: Wed, 14 Jul 2010 15:24:53 +0200 Subject: [PATCH] Dropped old-style symbols() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Now symbols('xyz') means one symbol 'xyz', not three separate: In [1]: symbols('xyz') Out[1]: xyz In [2]: symbols('x,y,z') Out[2]: (x, y, z) In [3]: symbols('x y z') Out[3]: (x, y, z) In [4]: symbols('x') Out[4]: x In [5]: symbols('x,') Out[5]: (x,) In [6]: symbols(('x', 'y', 'z')) Out[6]: (x, y, z) In [7]: symbols(['x', 'y', 'z']) Out[7]: [x, y, z] In [8]: symbols(['x', 'y', 'z:5']) Out[8]: [x, y, (z₀, z₁, z₂, z₃, z₄)] In [9]: symbols('x,y,z:5') Out[9]: (x, y, z₀, z₁, z₂, z₃, z₄) In [10]: symbols('x', seq=True) Out[10]: (x,) In [11]: symbols('f', cls=Function) Out[11]: f In [12]: type(_) Out[12]: ok --- sympy/core/symbol.py | 224 ++++++++++++++++++-------------- sympy/core/tests/test_symbol.py | 85 +++++++++--- sympy/core/tests/test_var.py | 2 +- 3 files changed, 196 insertions(+), 115 deletions(-) diff --git a/sympy/core/symbol.py b/sympy/core/symbol.py index baaa01fd0a..0c0bbbe663 100644 --- a/sympy/core/symbol.py +++ b/sympy/core/symbol.py @@ -16,7 +16,7 @@ class Symbol(AtomicExpr, Boolean): You can override the default assumptions in the constructor:: >>> from sympy import symbols - >>> A,B = symbols('AB', commutative = False) + >>> A,B = symbols('A,B', commutative = False) >>> bool(A*B != B*A) True >>> bool(A*B*2 == 2*A*B) == True # multiplication by scalars is commutative @@ -205,51 +205,78 @@ class Pure(Expr): _re_var_range = re.compile(r"^(.*?)(\d*):(\d+)$") _re_var_split = re.compile(r"\s|,") -def symbols(*names, **kwargs): +def symbols(names, **args): """ - Return a list of symbols with names taken from 'names' - argument, which can be a string, then each character - forms a separate symbol, or a sequence of strings. + Transform strings into instances of :class:`Symbol` class. - >>> from sympy import symbols - >>> x, y, z = symbols('xyz') + :func:`symbols` function returns a sequence of symbols with names taken + from ``names`` argument, which can be a comma or whitespace delimited + string, or a sequence of strings:: - Please note that this syntax is deprecated and will be dropped in a - future version of sympy. Use comma or whitespace separated characters - instead. Currently the old behavior is standard, this can be changed - using the 'each_char' keyword: + >>> from sympy import symbols, Function - >>> symbols('xyz', each_char=False) - xyz + >>> x, y, z = symbols('x,y,z') + >>> a, b, c = symbols('a b c') - All newly created symbols have assumptions set accordingly - to 'kwargs'. Main intention behind this function is to - simplify and shorten examples code in doc-strings. + The type of output is dependent on the properties of input arguments:: - >>> a = symbols('a', integer=True) - >>> a.is_integer - True - >>> xx, yy, zz = symbols('xx', 'yy', 'zz', real=True) - >>> xx.is_real and yy.is_real and zz.is_real - True + >>> x = symbols('x') + >>> (x,) = symbols('x,') + + >>> symbols(('a', 'b', 'c')) + (a, b, c) + >>> symbols(['a', 'b', 'c']) + [a, b, c] + >>> symbols(set(['a', 'b', 'c'])) + set([a, b, c]) + + If an iterable container is needed set ``seq`` argument to ``True``:: + + >>> symbols('x', seq=True) + (x,) + + To cut on typing, range syntax is supported co create indexed symbols:: + + >>> symbols('x:10') + (x0, x1, x2, x3, x4, x5, x6, x7, x8, x9) + + >>> symbols('x5:10') + (x5, x6, x7, x8, x9) + + >>> symbols('x5:10,y:5') + (x5, x6, x7, x8, x9, y0, y1, y2, y3, y4) + + >>> symbols(('x5:10', 'y:5')) + ((x5, x6, x7, x8, x9), (y0, y1, y2, y3, y4)) + + All newly created symbols have assumptions set accordingly to ``args``:: + + >>> a = symbols('a', integer=True) + >>> a.is_integer + True + + >>> x, y, z = symbols('x,y,z', real=True) + >>> x.is_real and y.is_real and z.is_real + True + + Despite its name, :func:`symbols` can create symbol--like objects of + other type, for example instances of Function or Wild classes. To + achieve this, set ``cls`` keyword argument to the desired type:: + + >>> symbols('f,g,h', cls=Function) + (f, g, h) + + >>> type(_[0]) + """ - func = Symbol - # when polys12 is in place remove from here... - if 'cls' in kwargs and kwargs.pop('cls') is Dummy: - func = Dummy - # ... to here when polys12 is in place - # use new behavior if space or comma in string - if not 'each_char' in kwargs and len(names) == 1 and \ - isinstance(names[0], str) and (' ' in names[0] or ',' in names[0]): - kwargs['each_char'] = False - if not kwargs.pop("each_char", True): - names = names[0] - - if not isinstance(names, list): - names = _re_var_split.split(names) - - result = [] + result = [] + + if isinstance(names, basestring): + names = _re_var_split.split(names) + + cls = args.pop('cls', Symbol) + seq = args.pop('seq', False) for name in names: if not name: @@ -266,72 +293,79 @@ def symbols(*names, **kwargs): start = int(start) for i in xrange(start, int(end)): - symbol = func("%s%i" % (name, i), **kwargs) + symbol = cls("%s%i" % (name, i), **args) result.append(symbol) + + seq = True else: - symbol = func(name, **kwargs) + symbol = cls(name, **args) result.append(symbol) - result = tuple(result) - - if len(result) <= 1: - if not result: # var('') - result = None - else: # var('x') - result = result[0] + if not seq and len(result) <= 1: + if not result: + return None + elif names[-1]: + return result[0] - return result + return tuple(result) else: - # this is the old, deprecated behavior: - if len(names) == 1: - result = [ func(name, **kwargs) for name in names[0] ] - else: - result = [ func(name, **kwargs) for name in names ] - if len(result) == 1: - return result[0] - else: - return result + for name in names: + syms = symbols(name, **args) + + if syms is not None: + result.append(syms) -def var(*names, **kwargs): + return type(names)(result) + +def var(names, **args): """ - Create symbols and inject them into global namespace. - - This calls symbols() with the same arguments and puts the results into - global namespace. Unlike symbols(), it uses each_char=False by default - for compatibility reasons. - - NOTE: The new variable is both returned and automatically injected into - the parent's *global* namespace. It's recommended not to use "var" in - library code, it is better to use symbols() instead. - - >>> from sympy import var - >>> var('m') - m - >>> var('n xx yy zz') - (n, xx, yy, zz) - >>> n - n - >>> var('x y', real=True) - (x, y) - >>> x.is_real and y.is_real - True + Create symbols and inject them into the global namespace. + + This calls :func:`symbols` with the same arguments and puts the results + into the *global* namespace. It's recommended not to use :func:`var` in + library code, where :func:`symbols` has to be used:: + + >>> from sympy import var + + >>> var('x') + x + >>> x + x + + >>> var('a,ab,abc') + (a, ab, abc) + >>> abc + abc + + >>> var('x,y', real=True) + (x, y) + >>> x.is_real and y.is_real + True + + See :func:`symbol` documentation for more details on what kinds of + arguments can be passed to :func:`var`. """ - import inspect - frame = inspect.currentframe().f_back + def traverse(symbols, frame): + """Recursively inject symbols to the global namespace. """ + for symbol in symbols: + if isinstance(symbol, Basic): + frame.f_globals[symbol.name] = symbol + else: + traverse(symbol, frame) + + from inspect import currentframe + frame = currentframe().f_back + try: - kwargs['each_char'] = False - s = symbols(*names, **kwargs) - if s is None: - return s - if isinstance(s, Symbol): - s_list = [s] - else: - s_list = s - for t in s_list: - frame.f_globals[t.name] = t - return s + syms = symbols(names, **args) + + if syms is not None: + if isinstance(syms, Basic): + frame.f_globals[syms.name] = syms + else: + traverse(syms, frame) finally: - # we should explicitly break cyclic dependencies as stated in inspect - # doc - del frame + del frame # break cyclic dependencies as stated in inspect docs + + return syms diff --git a/sympy/core/tests/test_symbol.py b/sympy/core/tests/test_symbol.py index 34cbafa8bc..0bdba7d376 100644 --- a/sympy/core/tests/test_symbol.py +++ b/sympy/core/tests/test_symbol.py @@ -144,25 +144,72 @@ def test_Pure(): assert (S.Pure != I) == True def test_symbols(): - x, y, z = Symbol('x'), Symbol('y'), Symbol('z') - assert symbols('x') == Symbol('x') - assert symbols('xyz') == [x, y, z] - assert symbols('x y z') == symbols('x,y,z') == (x, y, z) - assert symbols('xyz', each_char=False) == Symbol('xyz') - x, y = symbols('x y', each_char=False, real=True) - assert x.is_real and y.is_real - - assert symbols('x0:0', each_char=False) is None - assert symbols('x0:1', each_char=False) == Symbol('x0') - assert symbols('x0:3', each_char=False) == (Symbol('x0'), Symbol('x1'), Symbol('x2')) - - assert symbols('x:0', each_char=False) is None - assert symbols('x:1', each_char=False) == Symbol('x0') - assert symbols('x:3', each_char=False) == (Symbol('x0'), Symbol('x1'), Symbol('x2')) - - assert symbols('x1:1', each_char=False) is None - assert symbols('x1:2', each_char=False) == Symbol('x1') - assert symbols('x1:3', each_char=False) == (Symbol('x1'), Symbol('x2')) + x = Symbol('x') + y = Symbol('y') + z = Symbol('z') + + assert symbols('') is None + + assert symbols('x') == x + assert symbols('x,') == (x,) + assert symbols('x ') == (x,) + + assert symbols('x,y,z') == (x, y, z) + assert symbols('x y z') == (x, y, z) + + assert symbols('x,y,z,') == (x, y, z) + assert symbols('x y z ') == (x, y, z) + + xyz = Symbol('xyz') + abc = Symbol('abc') + + assert symbols('xyz') == xyz + assert symbols('xyz,') == (xyz,) + assert symbols('xyz,abc') == (xyz, abc) + + assert symbols(('xyz',)) == (xyz,) + assert symbols(('xyz,',)) == ((xyz,),) + assert symbols(('x,y,z,',)) == ((x, y, z),) + assert symbols(('xyz', 'abc')) == (xyz, abc) + assert symbols(('xyz,abc',)) == ((xyz, abc),) + assert symbols(('xyz,abc', 'x,y,z')) == ((xyz, abc), (x, y, z)) + + assert symbols(('x', 'y', 'z')) == (x, y, z) + assert symbols(['x', 'y', 'z']) == [x, y, z] + assert symbols(set(['x', 'y', 'z'])) == set([x, y, z]) + + assert symbols('x,,y,,z') == (x, y, z) + assert symbols(('x', '', 'y', '', 'z')) == (x, y, z) + + a, b = symbols('x,y', real=True) + + assert a.is_real and b.is_real + + x0 = Symbol('x0') + x1 = Symbol('x1') + x2 = Symbol('x2') + + y0 = Symbol('y0') + y1 = Symbol('y1') + + assert symbols('x0:0') == () + assert symbols('x0:1') == (x0,) + assert symbols('x0:2') == (x0, x1) + assert symbols('x0:3') == (x0, x1, x2) + + assert symbols('x:0') == () + assert symbols('x:1') == (x0,) + assert symbols('x:2') == (x0, x1) + assert symbols('x:3') == (x0, x1, x2) + + assert symbols('x1:1') == () + assert symbols('x1:2') == (x1,) + assert symbols('x1:3') == (x1, x2) + + assert symbols('x1:3,x,y,z') == (x1, x2, x, y, z) + + assert symbols('x:3,y:2') == (x0, x1, x2, y0, y1) + assert symbols(('x:3', 'y:2')) == ((x0, x1, x2), (y0, y1)) def test_call(): f = Symbol('f') diff --git a/sympy/core/tests/test_var.py b/sympy/core/tests/test_var.py index 669c19a977..edb75d36ee 100644 --- a/sympy/core/tests/test_var.py +++ b/sympy/core/tests/test_var.py @@ -32,7 +32,7 @@ def test_var(): assert fg == Symbol('fg') # check return value - assert v == (d, e, fg) + assert v == [d, e, fg] # see if var() really injects into global namespace raises(NameError, "z1")