I am writing code for a math calculator, and am running into a problem. When I ask the user to input a function to derive, and they input a number and then x, like 3x, instead of 3*x. The code crashes because it cannot parse it. To get around this I have a function to sanitize it, but it won't even run for some reason.
class Derivatives:
def __init__(self):
x=sympy.Symbol('x')
self.func=self.clean_func((input("Enter f(x): ")))
print(self.func)
print(sympy.diff(self.func, x))
def clean_func(self, func):
clean_func=""
print("c")
for i in range(len(func)):
if func[i].isalpha or isinstance(func[i], int) or func[i] in self.math_symbols :
if func[i]== "^" :
clean_func+="**"
else:
clean_func+=func[i]
for c in range(48, 57) :
if func[i:i+1] == (chr(c) + 'x') :
clean_func+="*" #helps with parsing
return clean_func
and I get this error in a=Derivatives()
Enter f(x): 3x
Traceback (most recent call last):
File "<ipython-input-48-f4cd19afbde9>", line 1, in <module>
a=Derivatives()
File "/Volumes/PROJECTS/M_series.py", line 71, in __init__
x=sympy.Symbol('x')
File "/Users/thomastierney/anaconda/lib/python3.5/site-packages/sympy/core/function.py", line 1691, in diff
return Derivative(f, *symbols, **kwargs)
File "/Users/thomastierney/anaconda/lib/python3.5/site-packages/sympy/core/function.py", line 1018, in __new__
expr = sympify(expr)
File "/Users/thomastierney/anaconda/lib/python3.5/site-packages/sympy/core/sympify.py", line 324, in sympify
raise SympifyError('could not parse %r' % a, exc)
SympifyError: Sympify of expression 'could not parse '3x'' failed, because of exception being raised:
SyntaxError: invalid syntax (<string>, line 1)
A few problems:
Using func as the name of a string is misleading (Python allows first-order functions, therefore you would expect it to actually be a function); let's call it fn_str instead.
If clean_func worked properly, it would return a sympy-parseable string - which you then pass to diff without converting from a string to a function.
The construction
for i in range(len(mystring)):
do_something(mystring[i])
is more clearly written as
for ch in mystring:
do_something(ch)
isinstance(func[i], int) doesn't work because func[i] is a one-character string, not an integer. Try str.isdecimal() instead.
self.math_symbols is not defined.
Python strings are immutable; every time you do mystring += ch it actually creates an entirely new string. Because of this, it is much more efficient to append to a list of string-chunks and then "".join() them when finished.
for c in range(48, 57): Python ranges do not include the end value, ie this will produce 48, 49, 50, ... 56. This means chr(57) ie "9" is never tested for.
Similarly, string slices do not include the end offset, so func[i:i+1] is one character, exactly equivalent to func[i]. You meant func[i:i+2].
Also, something like 3. is a valid Python float, but your code will not catch 3.x. Doing proper tokenization instead of character-by-character comparison would catch that.
The class is not a Derivative; you are trying to stuff your program into a mislabeled class.
A cleaned-up version:
import string
import sympy
# This could easily be just a function;
# I made it a class to keep the namespace clean
class FnStrCleaner:
# valid characters
NUMCHARS = set(string.digits + ".")
VARCHARS = set(string.ascii_letters)
OPCHARS = set("()+-*/^")
LEGALCHARS = NUMCHARS | VARCHARS | OPCHARS
#classmethod
def clean(cls, fn_str):
# generator expression - skip nonlegal chars
good_chars = (ch for ch in fn_str if ch in cls.LEGALCHARS)
out = []
# simple FSM to process character stream
prev_num = False # preceding character was part of a number
for ch in good_chars:
if prev_num:
if ch in cls.NUMCHARS:
out.append(ch)
# prev_num = True
elif ch in cls.VARCHARS:
out.append('*' + ch)
prev_num = False
else: # ch in cls.OPCHARS
out.append(ch)
prev_num = False
else:
if ch in cls.NUMCHARS:
out.append(ch)
prev_num = True
elif ch in cls.VARCHARS:
out.append(ch)
# prev_num = False
else: # ch in cls.OPCHARS
out.append('**' if ch == '^' else ch)
# prev_num = False
return "".join(out)
def get_function(prompt, locals=None):
fn_str = input(prompt)
fn_str = FnStrCleaner.clean(fn_str)
return sympy.sympify(fn_str, locals=locals)
def main():
x = sympy.Symbol("x")
f = get_function("Enter f(x): ", {'x': x})
df = sympy.diff(f, x)
print("f(x) =", f)
print("f'(x) =", df)
if __name__ == "__main__":
main()
which runs like
Enter f(x): 29x^3 + 12x^2 - 9x + 5
f(x) = 29*x**3 + 12*x**2 - 9*x + 5
f'(x) = 87*x**2 + 24*x - 9
Related
I'll try to be clear, please do any questions you need.
I'm working on mezcla, just trying to make things a little bit more pythonic.
To be specific, in debug there's a function called assertion, which takes an expression and evaluates it, giving an error message which doesn't raise an exception.
It works giving to the function an expression, like
from debug import assertion
def func():
return 2+2==5,
def probe(expr):
print(assertion(expr))
probe(
func()
)
##OR alone like
assertion(
2+2
==5)
And it should take the expression itself and print it. I'm looking for a way to get and evaluate a multiline expression, just like icecream, for example, can do this way:
In [2]: ic(2+2
...: ==5
...: )
ic| 2+2
==5: False
I tried this massive code, it reads the expression from ipython history, or it reads the file and line with inspect and iterates line by line of the script looking for the closure parenthesis.
def read_line(filename, line_number):
"""Returns contents of FILENAME at LINE_NUMBER"""
# ex: "debugging" in read_line(os.path.join(os.getcwd(), "debug.py"), 3)
try:
with open(filename) as file:
line_contents = file.readlines()[line_number - 1]
except OSError:
line_contents = "<stdin>"
except:
line_contents = "???"
return line_contents
def multi_assert(assertion, line_number=1):
"""Handles multiline assertions until 10 lines"""
line = ""
counter = 0
#While count(just for prevent infinite loop)
while counter < 10:
counter += 1
#Reads line, appends it and compares number of ()
line += read_line(assertion, line_number)
if line.count("(") > line.count(")"):
line_number += 1
else:
return line
break
def assertion(expression, message=None):
"""Issue warning if EXPRESSION doesn't hold, along with optional MESSAGE
Note: This is a "soft assertion" that doesn't raise an exception (n.b., provided the test doesn't do so)"""
if (not expression):
# Get source information for failed assertion
(_frame, filename, line_number, _function, _context, _index) = inspect.stack()[1]
# Read statement in file and extract assertion expression
# Calls to multi_assert to handle multiline
statement = multi_assert(str(filename), line_number + 1)
# If statement is from stdin, tries to get assert from ipython history
if statement == "<stdin>" and _context != None:
try:
ip = get_ipython()
statement = str(ip.history_manager.get_tail(1, raw=True, include_latest=True))
except:
statement = str(_context).replace(")\\n']", "")
return statement
It works, but it's too heavy, hacky and not specially ellegant, so i'm looking for any other way that return the gived assertion even if is multiline. Any kind of suggestion will be accepted and appreciated. Thanks
I've written simple code to handle just one case and correct the indentation (again simple and it relies on the user taking caution while using it) of a string containing a Python function declared using the def keyword and execute it.
def fix_index(string):
i=0;
t=string.find("def")+3;
string=string.replace(string[string.find("def"):t], "#")
while string.find(" ") != -1:
string = string.replace(" ", "")
i += 1
l=list(string);l[string.find(":")-i+2]+="$$$$"
return "".join(l).replace("$$$$", " ").replace("#", "def ").lstrip();
def switch(exp):
def exec(obj):
items = obj.items();
for k, v in items:
if(k==exp):
print(fix_index(v))
return eval(fix_index(v))();
return {"case":exec};
bread = "bread"
switch(bread)["case"]({
"cheese":
"""
def a():
print("cheese");
""",
"bread":
"""
def b():
print("bread");
"""
})
the output for the formatted function string:
C:\Users\User>python -u "c:\Users\User\folder\switch.py"
def b():
print("bread");
the error I'm getting:
Traceback (most recent call last):
File "c:\Users\User\folder\switch.py", line 27, in <module>
switch(bread)["case"]({
File "c:\Users\User\folder\switch.py", line 21, in exec
return eval(fix_index(v))();
File "<string>", line 1
def b():
^
SyntaxError: invalid syntax
I've also just realized I didn't name the function what I indented intended to (should've posted this when awake to avoid accidental pun).
Anyways what I fail to understand is what part in my produced string exactly contains "invalid syntax".
I'll appreciate any help.
if what you are looking for is to reproduce a switch statement, you can do it with the following function:
def switch(v): yield lambda *c: v in c
It simulates a switch statement using a single pass for loop with if/elif/else conditions that don't repeat the switching value:
for example:
for case in switch(x):
if case(3):
# ... do something
elif case(4,5,6):
# ... do something else
else:
# ... do some other thing
It can also be used in a more C style:
for case in switch(x):
if case(3):
# ... do something
break
if case(4,5,6):
# ... do something else
break
else:
# ... do some other thing
For your example, it could look like this:
meal = "bread"
for case in switch(meal):
if case("cheese"):
print("Cheese!")
elif case("bread"):
print("Bread!")
or this:
meal = "bread"
for case in switch(meal):
if case("cheese"):
print("Cheese!")
break
if case("bread"):
print("Bread!")
break
I stumbled upon the following problem on a python challenge: Write a function that satisfies the following rule for any number of function calls.
f()()()()()(s) == 'fooooo' + s;
example:
f('it') == 'fit';
f()('x') == 'fox';
f()()('bar') == 'foobar';
f()()()('l') == 'foool';
The function should be stateless and should not use any variables outside the scope.
The function signature was:
def f(s=None):
# Your code here
I thought that in order to be able to chain multiple calls we will have to return a function when no string is passed into the function, but can't figure out how to build the expected string with no external variables. Suggestions?
def f(s=None):
if s is None:
# concatenate an 'o'?
return f
else:
# Last call, return the result str.
return s
An alternative to Nikola's answer is something like this:
def f(s=None):
if s: return f'f{s}'
def factory(prefix):
def inner(s=None):
return f'f{prefix}{s}' if s else factory(prefix + 'o')
return inner
return factory('o')
using a closure and no helper function.
Obviously, you need to store the number of 'o' somewhere in the memory (e.g. the code) of f. To achieve this, you can benefit from these 2 features of Python:
You can define functions inside other functions
There's this thing called argument binding which allows you to tell Python to fix the some or all of the arguments of your function. This is done through functools.partial
And here's the solution
from functools import partial
def f(s=None):
# Define a new function g which takes the current text and takes s
def g(current_text, s=None):
if s is not None:
return current_text + s
else:
# If called with an empty argument, just rebind current_text with an extra o
return partial(g, current_text + "o")
# Just call g with the initial conditions
return g("f", s)
print(f()()()()()("s")) # fooooos
print(f("s")) # fs
You can try this:
def f(s=None):
string = "f"
def ret(p=None):
nonlocal string
string += "o"
return ret if not p else string + p
return ret if not s else string + s
This is my go at it:
def f(x=None, seq=''):
if x:
return 'f' + seq + x
else:
def g(y=None, p=seq + 'o'):
return f(y, p)
return g
Edit If you really need the function signature to be f(x=None), use this:
def f(x=None):
def f_(x=None, seq=''):
if x:
return 'f' + seq + x
else:
def g(y=None, p=seq + 'o'):
return f_(y, p)
return g
return f_(x)
:^)
Just for printing the string:
def f(s=None):
def fo(s=None):
if s==None:
print('o',end='')
return fo
else:
print(s)
return
if s!=None:
print('f',end='')
print(s)
return
elif s==None:
print('fo',end='')
return fo
Cute problem. This is a compact way to do it:
def f(s=None, prefix="f"):
if s: return prefix + s
return lambda s=None: f(s, prefix=prefix+"o")
FP:
f=lambda s=None,prefix='f':prefix+s if s else lambda s=None,prefix=prefix+'o':f(s,prefix)
I'd like to be able to read data from an input file in Python, similar to the way that Fortran handles a list-directed read (i.e. read (file, *) char_var, float_var, int_var).
The tricky part is that the way Fortran handles a read statement like this is very "forgiving" as far as the input format is concerned. For example, using the previous statement, this:
"some string" 10.0, 5
would be read the same as:
"some string", 10.0
5
and this:
"other string", 15.0 /
is read the same as:
"other string"
15
/
with the value of int_var retaining the same value as before the read statement. And trickier still this:
"nother string", , 7
will assign the values to char_var and int_var but float_var retains the same value as before the read statement.
Is there an elegant way to implement this?
That is indeed tricky - I found it easier to write a pure-python stated-based tokenizer than think on a regular expression to parse each line (tough it is possible).
I've used the link provided by Vladimir as the spec - the tokenizer have some doctests that pass.
def tokenize(line, separator=',', whitespace="\t\n\x20", quote='"'):
"""
>>> tokenize('"some string" 10.0, 5')
['some string', '10.0', '5']
>>> tokenize(' "other string", 15.0 /')
['other string', '15.0', '/']
>>> tokenize('"nother string", , 7')
['nother string', '', '7']
"""
inside_str = False
token_started = False
token = ""
tokens = []
separated = False
just_added = False
for char in line:
if char in quote:
if not inside_str:
inside_str = True
else:
inside_str = False
tokens.append(token)
token = ""
just_added = True
continue
if char in (whitespace + separator) and not inside_str:
if token:
tokens.append(token)
token = ""
just_added = True
elif char in separator:
if not just_added:
tokens.append("")
just_added = False
continue
token += char
if token:
tokens.append(token)
return tokens
class Character(object):
def __init__(self, length=None):
self.length = length
def __call__(self, text):
if self.length is None:
return text
if len(text) > self.length:
return text[:self.length]
return "{{:{}}}".format(self.length).format(text)
def make_types(types, default_value):
return types, [default_value] * len[types]
def fortran_reader(file, types, default_char="/", default_value=None, **kw):
types, results = make_types(types, default_value)
tokens = []
while True:
tokens = []
while len(tokens) < len(results):
try:
line = next(file)
except StopIteration:
raise StopIteration
tokens += tokenize(line, **kw)
for i, (type_, token) in enumerate(zip(types, tokens)):
if not token or token in default_char:
continue
results[i] = type_(token)
changed_types = yield(results)
if changed_types:
types, results = make_types(changed_types)
I have not teste this thoughtfully - but for the tokenizer -
it is designed to work in a Python forstatement if the same fields are repeated over and over again - or it can be used with Python's iterators send method to change the values to be read on each iteration.
Please test, and e-mail me (address at my profile) some testing file. If there is indeed nothing similar, maybe this deserves some polishing and be published in Pypi.
Since I was not able to find a solution to this problem, I decided to write my own solution.
The main drivers are a reader class, and a tokenizer. The reader gets one line at a time from the file, passes it to the tokenizer, and assigns to the variables it is given, getting the next line as necessary.
class FortranAsciiReader(file):
def read(self, *args):
"""
Read from file into the given objects
"""
num_args = len(args)
num_read = 0
encountered_slash = False
# If line contained '/' or read into all varialbes, we're done
while num_read < num_args and not encountered_slash:
line = self.readline()
if not line:
raise Exception()
values = tokenize(line)
# Assign elements one-by-one into args, skipping empty fields and stopping at a '/'
for val in values:
if val == '/':
encountered_slash = True
break
elif val == '':
num_read += 1
else:
args[num_read].assign(val)
num_read += 1
if num_read == num_args:
break
The tokenizer splits the line into tokens in accordance with the way that Fortran performs list directed reads, where ',' and white space are separators, tokens may be "repeated" via 4*token, and a / terminates input.
My implementation of the tokenizer is a bit long to reproduce here, and I also included classes to transparently provide the functionality of the basic Fortran intrinsic types (i.e. Real, Character, Integer, etc.). The whole project can be found on my github account, currently at https://github.com/bprichar/PyLiDiRe. Thanks jsbueno for inspiration for the tokenizer.
Let's say I have some python code in a string
code = """
a = 42
a
"""
and I exec that string of code:
result = exec(code)
Then result will always be None. Is there any way at all to get the value of the last expression evaluated? In this case, that would be 5, since a was the last expression.
EDIT: Here's another example of the functionality I'm asking about. Let's say we have the python code (stored in the variable code)
a = 100
sqrt(a)
Then how can I execute the code in such a way as to give me the result 10 - that is, sqrt(a)?
EDIT EDIT: A further example: the code I wish to exec is
function_a()
function_b()
function_c()
Is there any way I can define some kind of magic_exec function so that
magic_exec(code)
will provide me with the value of function_c()?
The request is certainly valid because I need such a function as well during the creation of a Python-based environment. I solved the problem with the following code that utilizes the Python ast mechanism:
def my_exec(script, globals=None, locals=None):
'''Execute a script and return the value of the last expression'''
stmts = list(ast.iter_child_nodes(ast.parse(script)))
if not stmts:
return None
if isinstance(stmts[-1], ast.Expr):
# the last one is an expression and we will try to return the results
# so we first execute the previous statements
if len(stmts) > 1:
exec(compile(ast.Module(body=stmts[:-1]), filename="<ast>", mode="exec"), globals, locals)
# then we eval the last one
return eval(compile(ast.Expression(body=stmts[-1].value), filename="<ast>", mode="eval"), globals, locals)
else:
# otherwise we just execute the entire code
return exec(script, globals, locals)
The code should be pretty self-explanatory, basically it
separate the script into multiple statements
if the last one is an expression, execute the first part as statements, and the last part as expression.
Otherwise execute the entire script as statements.
This doesn't get you the last evaluated value, but gets the whole list of local variables.
>>> loc = {}
>>> exec(code, {}, loc)
>>> loc
{'a': 42}
exec('a = 4')
print a % prints 4
>>> code = """
... a = 42
... b = 53"""
>>> exec(code)
>>> a
42
>>> b
53
Or if you're saying you don't know the last thing is b for instance, then you can have this:
code = """
a = 4
b = 12
abc_d=13
"""
t = re.findall(r'''.*?([A-Za-z0-9_]+)\s*?=.*?$''', code)
assert(len(t)==1)
print t[0] % prints 13
To be honest I can't say I'm very happy with this. It feels very hacky and I haven't tested it all that heavily. On the other hand I'm quite pleased with it. Was quite fun to do. Anyway, hope this helps you or at least comes close to what you want. locals() gives a dict so the output list order does not match the input order for the items that failed the first eval. If you don't want ';' as delimiters then you can change it to '\n'.
import math
def magic_exec(_command):
_command = _command.split(';')
_result = None
_before = list(locals()) # Get list of current local variables
for _code in _command:
_code = _code.strip() # .strip() prevent IndentationError
try:
if eval(_code) != None: # For functions with no return
_result = eval(_code)
except (NameError, SyntaxError):
try:
_before = list(locals())
exec(_code)
except NameError as e: # For undefined variables in _command
print("An Error Occurred with line ' {0} ' as was skipped: {1}".format(_code, e))
del _code # delete temp var _code
# Get new list of locals that didn't exist at the start
_after = [val for val in list(locals()) if val not in _before]
if _after:
return eval(_after[0])
else:
return _result
#Dummy class and functions
class Class1(object):
def __init__(self, x):
self._x = x
def get_val(self):
return self._x
def __repr__(self):
return type(self).__name__
def func1(x):
return x + x
def func2(x):
print(x*x)
if __name__ == '__main__':
code = \
"""
a = 42; a; v; y = 2; b = func1(5); s = 'Hello'; func2(10); c = 25; l = []; l.append('Value');
t = math.sqrt(c); pass; 20*10; print('TEST'); math.sqrt(c); d = Class1('World'); d.get_val();
def func3(x): return x ** 2; s = func3(15)
"""
values = magic_exec(code)
print(values)
I would like to add to user2283347's excellent answer that it works only up to Python 3.7. In Python 3.8 the signature of ast.Module.__init__ has changed. It now requires a second argument which in our case can be an empty list.
Details: ast.Module(body=stmts[:-1]) in
if len(stmts) > 1:
exec(compile(ast.Module(body=stmts[:-1]), filename="<ast>", mode="exec"), globals, locals)
has to be changed to
ast.Module(stmts[:-1], []) if you use Python 3.8 or above (note the second argument []). Otherwise the following TypeError will be raised:
TypeError: required field "type_ignores" missing from Module
Unfortunately this change is not very well documented. I found the solution after extensive Googling here: "IPython broken on 3.8-dev" .