Symbol vs Operator in Python - python

I'm reviewing for a test over some basic Python syntax stuff and I'm wanting to make sure I have a proper understanding of the difference between a symbol and an operator. A symbol can be a string of characters or a operator and an operator can only be something that does something to characters or strings right?

Operator is a syntactic representation for some important Python function. For example, and infix + operator as in a + b. There is a module called operator to represent standard operators as functions. Also, special methods (as in the hus787 comment above) can override operators for instances of a class.
Symbol is an element of Python grammar. Symbol can represent a whole program, a statement, operator, name, literal, etc, even indent and dedent (in case of Python).
This terminology is not Python specific even.

A symbol in a programming language is either a binding to some value (eg. variable identifiers), a value itself (eg. "foo", 123, True), keyword (eg. def, class, import,try, except,...) or other language specific construct( (), {}, [],...).
So a symbol does not always have to be a string of characters.
In contrast, an operator defines a specific function among one or more values. (There are unary, binary, tertiary,... operators)
eg. + in 1+1, < in a<b are operators
It's noteworthy if you are considering this idea in a compiler's standpoint, everything you write in your code is a symbol. That is even +, - , *, / , are mere symbols to a lexical analyzer. (I assume that this fact is out of the scope of your question). Hence we will restrict our answer to the domain of language syntax.
However this idea is universal for any programming language

Related

Python opposite of eval()

Is it possible to do the opposite of eval() in python which treats inputs as literal strings? For example f(Node(2)) -> 'Node(2)' or if there's some kind of higher-order operator that stops interpreting the input like lisp's quote() operator.
The answer to does Python have quote is no, Python does not have quote. There's no homoiconicity, and no native infrastructure to represent the languages own source code.
Now for what is the opposite of eval: you seem to be missing some part the picture here. Python's eval is not the opposite of quote. There's three types values can be represented as: as unparsed strings, as expressions, and as values (of any type).
quote and LISP-style "eval" convert between expressions and values.
Parsing and pretty printing form another pair between expressions and strings.
Python's actual eval goes directly from strings to values, and repr goes back (in the sense of printing a parsable value1).
Since SO does not support drawing diagrams:
So if you are looking for the opposite of eval, it's either trivially repr, but that might not really help you, or it would be the composition of quote and pretty priniting, if there were quoting in Python.
This composition with string functions a bit cumbersome, which is why LISPs let the reader deal with parsing once and for all, and from that point on only go between expressions and values. I don't know any language that has such a "quote + print" function, actually (C#'s nameof comes close, but is restricted to names).
Some caveats about the diagram:
It does not commute: you have deal with pure values or disregard side effects, for one, and most importantly quote is not really a function in some sense, of course. It's a special form. That's why repr(x) is not the same as prettyprint(quote(x)), in general.
It's not depicting pairs of isomorphisms. Multiple expressions can eval to the same value etc.
1That's not always the case in reality, but that's what it's suppsedly there for.

Why don't f-strings play nicely with dictionaries?

f-strings don't behave nicely when used with dictionaries, as mentioned here.
Here is an example of the not-so-nice behavior:
d = {'foo': 'bar'}
# Both work as expected
d["foo"]
d['foo']
# This only works when different quotations are used in the inner and outer strings
f'{d["foo"]}'
f"{d['foo']}"
# This doesn't work
f'{d['foo']}'
f"{d["foo"]}"
# The .format() method doesn't care
'{}'.format(d['foo'])
The last two f-strings listed result in a SyntaxError: invalid syntax, which happens because the string '{d['foo']}' is evaluated as '{d['foo']}'.
What is the underlying reason everything inside the curly brackets of f-strings doesn't get evaluated separately, as when using the old .format() method, and what could possibly be the reason for implementing f-strings in this way?
I love f-strings, but this seems like a point in favor of the old method.
F-strings are literal strings. Including unescaped quotes within quotes (of the same type) is invalid syntax. This makes sense, since the result is ambiguous: the interpreter will not know when the string should end. One traditional way of including quotes within quotes is to use a backslash. But PEP498 forbids backslashes in expressions within f-strings:
Backslashes may not appear inside the expression portions of
f-strings...You can use a different type of quote inside the expression...
Therefore, the only way left to access a dictionary value given a key in an f-string expression is to use a different type quote. Using single quotes, or double quotes, everywhere is ambiguous and gives SyntaxError.
str.format is a regular method, and as such works differently: d['foo'] is evaluated before the string is constructed. Just like when you feed arguments to a function, the arguments are evaluated before the function does anything.
This has nothing to do with f-strings. f strings are common strings once evaluated. What you are trying would be a problem with standard strings too
The problem is that
'a "b" c'
is declares the literal a "b" c
while
'a 'b' c'
the quotes close and reopen. So, it is equivalent to string a, followed by variable b, followed by string c.
That's the whole reason python supports both types of quotation marks
According to PEP 498, f-strings rely on the implementation of regular string literals, and as such are subject to the same constraints. In addition to those, f-strings have their own constraints, such as the exclusion of backslashes anywhere within expressions.
A rationale for the choice of implementation has not been published, but ease of implementation, backwards compatibility, syntax highlighting, and consistency with existing interpolation syntax all featured in discussions among core developers on the publicly accessible mailing lists.1
A central point of contention was whether the proposed concept constitutes a string with special properties or a string interjected with code.2 The former view was favored. PEP 536, embodying the dissenting view and additionally seeking to lift several syntactic constraints, was subsequently filed.
Based on this discussion, a tentative compromise of prohibiting backslashes within expressions was agreed upon, leaving string delimiter inversion as the only remaining option for indexing dictionaries within f-string expressions.
Select discussions predating f-string introduction (Python 3.6):
[Python-ideas] Briefer string format
Re: [Python-ideas] Briefer string format
Re: [Python-ideas] Briefer string format
[Python-ideas] String interpolation for all literal strings
[Python-Dev] PEP-498: Literal String Formatting
[Python-Dev] PEP 498 f-string: is it a preprocessor?
[Python-Dev] PEP 498 href="interpolated f-string" tweak
[Python-Dev] Parsing f-strings from PEP 498 -- Literal String Interpolation
[Python-ideas] Let’s make escaping in f-literals impossible

What is the equivalent of Python's ast.literal_eval() in Julia?

Is there anything in Julia which is equivalent to Python's literal_eval provided by the package ast (Abstract Syntax Tree)?
A summary of its (literal_eval) description:
This function only evaluates Python literal structures: strings,
bytes, numbers, tuples, lists, dicts, sets, booleans, and None, and
can be used for safely evaluating strings from untrusted sources
without the need to parse the values oneself. It is not capable of
evaluating arbitrarily complex expressions, for example involving
operators or indexing.
There is no equivalent, although you could potentially write one fairly easily by parsing code and then recursively ensuring that you only have certain syntactic forms in the resulting expression before evaluating it. However, unlike Python where a lot of basic types and their syntax and behavior are built in and unchangeable, Julia's "built in" types are just user-defined types that happen to be defined before the system starts up. Let's explore what happens, for example, when you use vector literal syntax:
julia> :([1,2,3]) |> dump
Expr
head: Symbol vect
args: Array{Any}((3,))
1: Int64 1
2: Int64 2
3: Int64 3
typ: Any
julia> f() = [1,2,3]
f (generic function with 2 methods)
julia> #code_lowered f()
CodeInfo(:(begin
nothing
return (Base.vect)(1, 2, 3)
end))
julia> methods(Base.vect)
# 3 methods for generic function "vect":
vect() in Base at array.jl:63
vect(X::T...) where T in Base at array.jl:64
vect(X...) in Base at array.jl:67
So [1,2,3] is just a syntactic form that is lowered as a call to the Base.vect function, i.e. Base.vect(1,2,3). Now, we might in the future make it possible to "seal" some functions so that one can't add any submethods or overwrite their behavior in any way, but currently modifying the behavior of Base.vect for some set of arguments is entirely possible:
julia> function Base.vect(a::Int, b::Int, c::Int)
warn("SURPRISE!")
return invoke(Base.vect, Tuple{Any,Any,Any}, a, b, c)
end
julia> [1,2,3]
WARNING: SURPRISE!
3-element Array{Int64,1}:
1
2
3
Since an array literal is overloadable in Julia it's not really a purely literal syntax. Of course, I don't recommend doing what I just did – "SURPRISE!" is not something you want to see in the middle of your program – but it is possible and therefore the syntax is not "safe" in the sense of this question. Some other constructs which are expressed with literals in Python or JavaScript (or most scripting languages), are explicitly function calls in Julia, such as constructing dictionaries:
julia> Dict(:foo => 1, :bar => 2, :baz => 42)
Dict{Symbol,Int64} with 3 entries:
:baz => 42
:bar => 2
:foo => 1
This is just a function call to the Dict type with three pair object arguments, not a literal syntax at all. The a => b pair syntax itself is also just a special syntax for a function call to the => operator, which is an alias for the Pair type:
julia> dump(:(a => b))
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol =>
2: Symbol a
3: Symbol b
typ: Any
julia> :foo => 1.23
:foo=>1.23
julia> =>
Pair
julia> Pair(:foo, 1.23)
:foo=>1.23
What about integer literals? Surely those are safe! Well, yes and no. Small integer literals are currently safe, since they are converted in the parser directly to Int values, without any overloadable entry points (that could change in the future, however, allowing user code to opt into different behaviors for integer literals). Large enough integer literals, however, are lowered to macro calls, for example:
julia> :(18446744073709551616)
:(#int128_str "18446744073709551616")
An integer literal that is too big for the Int64 type is lowered as macro call with a string argument containing the integer digits, allowing the macro to parse the string and return an appropriate integer object – in this case an Int128 value – to be spliced into the abstract syntax tree. But you can define new behaviors for these macros:
julia> macro int128_str(s)
warn("BIG SUPRISE!")
9999999999999999
end
julia> 18446744073709551616
WARNING: BIG SUPRISE!
9999999999999999
Essentially, there is no meaningful "safe literal subset" of Julia. Philosophically, Julia is very different from Python: instead of building in a fixed set of types with special capabilities that are inaccessible to user-defined types, Julia includes powerful enough mechanisms in the language that the language can be built from within itself – a process known as "bootstrapping". These powerful language mechanisms are just as available to Julia programmers as they are to the programmers of Julia. This is where much of Julia's flexibility and power comes from. But with great power comes great responsibility and all that... so don't actually do any of the things I've done in this answer unless you have a really good reason :)
To get back to your original problem, the best one could do to create a parser for safe literal object construction using Julia syntax would be to implement a parser for a subset of Julia, giving literals their usual meaning in a way that cannot be overloaded. This safe syntax subset could include numeric literals, string literals, array literals, and Dict constructors, for example. But it would probably be more practical to just use JSON syntax and parse it using Julia's JSON package.
I know I'm late here, but Meta.parse does the job:
julia> eval(Meta.parse("[1,2,3]"))
3-element Array{Int64,1}:
1
2
3
Specifically, Meta.parse turns your string into an Expr which eval then turns into a useable data structure. Definitely works in Julia 1.0.
https://discourse.julialang.org/t/how-to-convert-a-string-into-an-expression/11160

How to change priority in math order(asterisk)

I want users to input math formula in my system. How can convert case1 formula to case2 formula using Python? In another word, I would like to change math order specifically for double asterisks.
#case1
3*2**3**2*5
>>>7680
#case2
3*(2**3)**2*5
>>>960
Not only is this not something that Python supports, but really, why would you want to? Modifying BIDMAS or PEMDAS (depending on your location), would not only give you incorrect answers, but also confuse the hell out of any devs looking at the code.
Just use brackets like in Case 2, it's what they're for.
If users are supposed to enter formulas into your program, I would suggest keeping it as is. The reason is that exponentiation in mathematics is right-associative, meaning the execution goes from the top level down. For example: a**b**c = a**(b**c), by convention.
There are some programs that use bottom-up resolution of the stacked exponentiation -- MS Excel and LibreOffice are some of them, however, it is against the regular convention, and always confused the hell out of me.
If you would like to override this behavior, and still be mathematically correct, you have to use brackets.
You can always declare your own power method that would resolve the way you want it -- something like numpy.pow(). You could overload the built-in, but that's too much hastle.
Read this
Below is the example to achieve this using re as:
expression = '3*2**3**2*5'
asterisk_exprs = re.findall(r"\d+\*\*\d+", expression) # List of all expressions matching the criterion
for expr in asterisk_exprs:
expression = expression.replace(expr, "({})".format(expr)) # Replace the expression with required expression
# Value of variable 'expression': '3*(2**3)**2*5'
In order to evaluate the mathematical value of str expression, use eval as:
>>> eval(expression)
960

Safe expression parser in Python

How can I allow users to execute mathematical expressions in a safe way?
Do I need to write a full parser?
Is there something like ast.literal_eval(), but for expressions?
The examples provided with Pyparsing include several expression parsers:
https://github.com/pyparsing/pyparsing/blob/master/examples/fourFn.py is a conventional arithmetic infix notation parser/evaluator implementation using pyparsing. (Despite its name, this actually does 5-function arithmetic, plus several trig functions.)
https://github.com/pyparsing/pyparsing/blob/master/examples/simpleBool.py is a boolean infix notation parser/evaluator, using a pyparsing helper method operatorPrecedence, which simplifies the definition of infix operator notations.
https://github.com/pyparsing/pyparsing/blob/master/examples/simpleArith.py and https://github.com/pyparsing/pyparsing/blob/master/examples/eval_arith.py recast fourFn.py using operatorPrecedence. The first just parses and returns a parse tree; the second adds evaluation logic.
If you want a more pre-packaged solution, look at plusminus, a pyparsing-based extensible arithmetic parsing package.
What sort of expressions do you want? Variable assignment? Function evaluation?
SymPy aims to become a full-fledged Python CAS.
Few weeks ago I did similar thing, but for logical expressions (or, and, not, comparisons, parentheses etc.). I did this using Ply parser. I have created simple lexer and parser. Parser generated AST tree that was later use to perform calculations. Doing this in that way allow you to fully control what user enter, because only expressions that are compatible with grammar will be parsed.
Yes. Even if there were an equivalent of ast.literal_eval() for expressions, a Python expression can be lots of things other than just a pure mathematical expression, for example an arbitrary function call.
It wouldn't surprise me if there's already a good mathematical expression parser/evaluator available out there in some open-source module, but if not, it's pretty easy to write one of your own.
maths functions will consist of numeric and punctuation characters, possible 'E' or 'e' if you allow scientific notation for rational numbers, and the only (other) legal use of alpha characters will be if you allow/provide specific maths functions (e.g. stddev). So, should be trivial to run along the string for alpha characters and check the next little bit isn't suspicious, then simply eval the string in a try/except block.
Re the comments this reply has received... I agree this approach is playing with fire. Still, that doesn't mean it can't be done safely. I'm new to python (< 2 months), so may not know the workarounds to which this is vulnerable (and of course a new Python version could always render the code unsafe in the future), but - for what little it's worth (mainly my own amusement) - here's my crack at it:
def evalMaths(s):
i = 0
while i < len(s):
while s[i].isalpha() and i < len(s):
idn += s[i]
i += 1
if (idn and idn != 'e' and idn != 'abs' and idn != 'round'):
raise Exception("you naughty boy: don't " + repr(idn))
else:
i += 1
return eval(s)
I would be very interested to hear if/how it can be circumvented... (^_^) BTW / I know you can call functions like abs2783 or _983 - if they existed, but they won't. I mean something practical.
In fact, if anyone can do so, I'll create a question with 200 bounty and accept their answer.

Categories

Resources