COP 3223H meeting -*- Outline -*- * Statements ** vs. expressions and declarations These catagories of computations are common across many programming languages (including both C and Python) ------------------------------------------ BROAD CLASSIFICATION OF COMPUTATIONS Expressions like 2+3, or square(7) Statements like t = t+1, or print("hi!") (in C end with a semicolon, ;) Declarations like from math import isclose , or global total (in C gives type of names) ------------------------------------------ ... have a value (data), returned to the calling context ... have an effect, that changes the state of the program or the environment, have no value ... tell the language something, often this is an effect on the static name space or type system that is kept by the compiler or interpreter ** primitive statements in Python We'll first look at some of the primitive (i.e, non-compound) statements in Python; these are also similar to those seen in other lanugages *** assignment ------------------------------------------ ASSIGNMENT STATEMENT Purpose: give a (computed) value a name, name a constant (for readability) Syntax: ::= | ... ::= = | ... Examples: DAYS_IN_YEAR = 365.25 t = 1 t = t + 1 c = sqrt(a**2 + b**2) Semantics: ------------------------------------------ ... first is evaluated, if it encounters an error, then the statment encounters the same error otherwise, if returned a value, V, then if the is defined already (in the current local, nonlocal, or global namespace), then the is bound to the 's value V else the a binding is created for the in the current local namespace and is bound to 's value V Note: in C same but with a semicolon at the end Note: unlike statically-typed languages, Python does not require that be declared before being assigned. Q: Does an assignment statment do too many different kinds of things? You might argue that, since it both establishes initial bindings and changes bindings. ------------------------------------------ ASSIGNMENT EXAMPLES >>> isum Traceback (most recent call last): File "", line 1, in isum NameError: name 'isum' is not defined >>> isum = 1 + 2 + 3 + 4 + 5 >>> print(isum) >>> isum = isum + 6 + 7 + 8 + 9 + 10 >>> print(isum) >>> isum += 11 + 12 + 13 + 14 + 15 >>> print(isum) >>> avg15 = isum/15 >>> print(avg15) ------------------------------------------ ... try these out in the interpreter These also show printing ** assert statements ------------------------------------------ ASSERT STATEMENTS Purpose: To state a property that must hold, to abort the computation otherwise Syntax: ::= assert | ... Examples: assert square(2) == 4 assert math.isclose(square(5), 25) Semantics: ------------------------------------------ ... the is evaluated, if it's value is True, then the statement does noting (else). Otherwise, if 's value is false, the statement raises an AssertionError (Note: that assertion evaluation is conditioned on the value of __debug__ being True, which is the default, but can be switched off and is off when code optimization is requested by -O) Similar in C, but with a semicolon at the end Q: If we know some property should be true, why assert it? Because we might have made a mistake, because we want to catch errors early before they get out of control (this is helpful in debugging) ** pass statment ------------------------------------------ PASS STATEMENT Purpose: To tell the interpreter to do nothing Syntax: ::= pass | ... Example: pass Semantics: does nothing (has no effect) ------------------------------------------ Q: Why would you want to have pass in your language? So that it is possible to say to "do nothing" in certain cases... In C, this is just a semicolon, i.e., an empty statement ** return statement ------------------------------------------ RETURN STATEMENT Purpose: To return a value from a function, To end execution of a function Syntax: ::= return | return | ... Static Semantics: A return statement may only occur inside a function definition's body Examples: def multiply(a, b): """Return the product of a and b.""" return a*b return a // (b-b) # never executed! Now the expression multiply(3,4)+5 has value 12+5 and the error from a // (b-b) never is executed def print_product(a,b): """Prints the product of a and b.""" print(a*b) return print("you'll never see this output!") Semantics: ------------------------------------------ ... for the form return , evaluates , which may return a value V. Then leaves the currently executing function with the value V as the value returned by the function (so that the next executed code is after the function call) for the form return, leaves the currently executing function with the value None as the value returned by the function in C the same, but with a semicolon at the end Q: Why have a way to explicitly stop processing in a function? We many want to decide what to do dynamically, based on inputs... ** calls Technically these are expression statments, expressions used as statments ------------------------------------------ PROCEDURE CALLS Purpose: To perform some variation of a named task, to make code more understandable by chunking some statements together to allow abstraction (by specification and parameterization) Syntax: ::= ( ) | ( ) | ... ::= | , Guidance: you should only use functions that do not return a value (i.e., return None) as statements Examples: print("done") Semantics: ------------------------------------------ ... the actuals are evaluated from left to right, yielding a tuple of values (if there is an error, then that error happens to the statement) then a new evaluation context (stack frame) is created for the function named by , based on the saved evaluation context of that function, augmented by binding each formal parameter name to the corresponding actual parameter values (if the number of formals and actuals do not match, an error occurs) and the code of the function is executed in this new evaluation context. Q: What would guess is the C syntax for these? the same, but with a semicolon at the end. *** semantics is call-by-value ------------------------------------------ PYTHON USES CALL-BY-VALUE I.e., actual argument expressions are evaluated, in the calling context, and their values are passed to the function # second ignores its first argument >>> def second(a,b): return b >>> second(7//0, 3) Traceback (most recent call last): File "", line 1, in second(7//0, 3) ZeroDivisionError: integer division or modulo by zero # the error above shows Python doesn't use # call by name or lazy evaluation # assignarg assigns its argument >>> def assignarg(a): a = 3 >>> myarg = 5 >>> assignarg(myarg) >>> myarg 5 >>> # since myarg stays 5, assignarg >>> # only affected its local copy of a >>> # thus Python does not use >>> # either call by reference or value-result ------------------------------------------ C also uses call by value C++ also has call by reference, with a different syntax *** the DRY principle "Never write the same thing twice" is also known as "DRY" meaning "Don't repeat yourself!" ------------------------------------------ ZEN FOR PROGRAMMERS, PART I Never write the same thing twice, instead put it in a procedure/fuction Example: To call pytest on a tst file test_f.py from IDLE you type import pytest pytest.main(["test_f.py", "--capture=sys"]) To avoid writing that twice, make a function: def run_pytest(filename): """Run pytest on filename (a string).""" import pytest pytest.main([filename, "--capture=sys"]) Use: >>> run_pytest("test_circlearea.py") >>> run_pytest("test_multiply.py") ------------------------------------------ Note: Python considers "import" to be a statement, so it can go in the body of a function like this! ** sequencing Technically this in Python isn't gramatically a statement, but it doesn't hurt much to consider it so. ------------------------------------------ SUITES: STATEMENT LISTS Purpose: To execute actions in order (sequentially), to do several things describe a compound action, as in the body of a function Syntax: ::= ::= | Static Semantics: The list of statments must end with a newline (or a semicolon), and must all have the same indentation A is ended by a line with less indentation. Examples: print("Hello") print("World!") t = 7 print("t is", t) t = t +1 print("t is", t) Semantics: A such as S1 S2 is executed by ------------------------------------------ ... first executing S1, then exeucting S2 (provided S1 terminates normally) however if S1 encounters an error, then the suite encounters the same error and S2 is not executed, also if S1 returns control (e.g., if S1 is a return statement) then the suite returns control (and S2 is not executed). Q: Why not run all statements in a statement-list in parallel instead of sequentially? Maybe we tend to think sequentially, but this is historical and corresponds to the computers we have In C the syntax is the same, just listing statements one after another