Values and Variables

City University of Hong Kong

CS1302 Introduction to Computer Programming


import sys  # to access system-specific parameters

from ipywidgets import interact  # for interactive user interface controls

%reload_ext divewidgets
# use OPTLite to visualize code execution

Values

Programming is the art of instructing a computer to perform tasks by manipulating data, which is represented as values. In mathematical terms, a computer is a model of how values are stored and manipulated. Values are the building blocks of programs, and they represent a specific piece of data that can be manipulated in various ways.

While a machine language only uses binary numbers as values, higher-level programming languages provide more flexible and expressive ways to represent and manipulate values. Understanding the concept of value is crucial in programming, as it is the foundation upon which all computations are built.

Integers

In Python, we can enter an integer in different number systems other than the binary one:

# in decimal (base 10)
15
# in binary (base 2)
0b1111
# in octadecimal (base 8)
0o17
# in hexadecimal (base 16)
0xF

There are also expressions with integer values but are not integer literal:

4 + 5 + 6
### BEGIN SOLUTION
# There is no limit on how big an integer can be
10 ** sys.get_int_max_str_digits() - 1
# see what happens with -1 removed.
### END SOLUTION

Strings

A string value is a sequence of characters that can be written literally using quotes:

# single quote
print('\U0001f600:\n\tI\'m a string.')
# double quote
print("\N{grinning face}:\n\tI'm a string.")
# triple double quote
print(
    """😀:
	I'm a string."""
)

With Python, there are often many ways to do the same thing. The following one-line code (one-liner) also prints the multiple lines of strings:

print("\N{grinning face}:", "\tI'm a string.", sep="\n")

What are benefits of allowing single, double, and triple quotes for string literals?

  • By using double quotes, we don't need to escape the single quote in strings such as "I'm".
  • Triple quotes enable a multi-line string literal to include the newline character directly, resulting in a more readable representation of the literal.
### BEGIN SOLUTION
print(
    """
 (ง •̀_•́)ง 
 
 ╰(●’◡’●)╮ 
 
 (..•˘_˘•..)
 
 (づ ̄ 3 ̄)づ
"""
)

# install art module
!pip install art >/dev/null 2>&1
import art
yyyy, mm = "2023", "09"
art.tprint(f"""{yyyy}{mm}CS1302
Intro to Comp Progm'g""")
### END SOLUTION

User Input

Instead of entering a value in a program, a programmer can get user input values at runtime, i.e., as a program executes:

print("Your name is", input("Please input your name: "))
print("My name is", print("Python"))
Solution to Exercise 3 #
  • Unlike input, the function print does not return the string it is trying to print. Printing a string is, therefore, different from returning a string.
  • print actually returns a None object that gets printed as None.

Variables

To perform complex computations, computers require data to be stored in memory and accessed through addresses. This is where variables come in - they serve as a simplified storage unit that abstracts away the complexities of memory addressing and value storage. With variables, programmers can assign a meaningful name and a value of various types without having to worry about the underlying memory addresses and binary interpretations. This simplifies the programming process and enables developers to focus on the logic of their code, rather than the intricacies of memory management.

Assignment

To define a variable, we can use the assignment operator = as follows:

x = 15
print(x)

To understand the difference, let's see the effect of the following assignments step-by-step using OPTLite:

%%optlite -h 300
x = 15
x = x + 1

In the second line, the value of x is incremented, i.e., increased by 1. It would not make sense to say xx is equal to or defined to be x+1x + 1, since no choices of xx (other than \infty) satisfy this.

It is possible to assign different values to multiple variables in one line using the so-called tuple assignment syntax:

%%optlite -h 300
x, y, z = "15", "30", 15

One can also assign the same value to different variables in one line using a chained assignment:

%%optlite -h 300
x = y = z = 0

Once defined, variables can be deleted using del. Accessing a variable not assigned any value raises an error.

%%optlite -h 350
x = y = 0
del x, y
x, y

Identifiers

In Python, identifiers such as variable names are case sensitive, unlike older languages such as Pascal and Fortran. Furthermore, not all strings are valid identifiers:

What is the syntax for identifiers?

  1. Must start with a letter or _ (an underscore) followed by letters, digits, or _.

  2. Must not be a keyword reserved by python:

    False      await      else       import     pass
    None       break      except     in         raise
    True       class      finally    is         return
    and        continue   for        lambda     try
    as         def        from       nonlocal   while
    assert     del        global     not        with
    async      elif       if         or         yield
@interact
def identifier_syntax(
    assignment=[
        "a-number = 15",
        "a_number = 15",
        "15 = 15",
        "_15 = 15",
        "del = 15",
        "Del = 15",
        "type = print",
        "print = type",
        "input = print",
    ]
):
    exec(assignment)
    print("Ok.")
Solution to Exercise 4
  1. a-number = 15 violates Rule 1 because - is not allowed. - is interpreted as an operator.
  2. 15 = 15 violates Rule 1 because 15 starts with a digit instead of letter or _.
  3. del = 15 violates Rule 2 because del is a keyword.

Type Conversion

The following program tries to compute the sum of two numbers from user inputs:

num1 = input("Please input an integer: ")
num2 = input("Please input another integer: ")
print(num1, "+", num2, "is equal to", num1 + num2)
Solution to Exercise 5 #

The two numbers are concatenated instead of added together.

input returns user input as a string. E.g., if the user enters 12, the input is

  • not treated as the integer twelve, but rather
  • treated as a string containing two characters, one followed by two.

To confirm this, we can use type to return the data type of an expression.

num1 = input("Please input an integer: ")
print("Your input is", num1, "with type", type(num1))
type(15), type(print), type(print()), type(input), type(type), type(type(type))

What happens when we add strings together?

"4" + "5" + "6"

How to fix the bug then?

We can convert a string to an integer using int.

int("4") + int("5") + int("6")

We can also convert an integer to a string using str.

str(4) + str(5) + str(6)
num1 = input("Please input an integer: ")
num2 = input("Please input another integer: ")
# print(num1, '+', num2, 'is equal to', num1 + num2)  # fix this line below
### BEGIN SOLUTION
print(num1, "+", num2, "is equal to", int(num1) + int(num2))
### END SOLUTION

Error types

In addition to writing code, a programmer spends significant time in debugging code that contains errors.

Can an error be automatically detected by the computer?

  • You have just seen an example of a logical error, which is due to an error in the logic.
  • The ability to debug or even detect such an error is, unfortunately, beyond Python's intelligence.

Other kinds of error may be detected automatically.
As an example, note that we can omit + for string concatenation, but we cannot omit it for integer summation:

print("Skipping + for string concatenation")
"4" "5" "6"
print('Skipping + for integer summation')
4 5 6

Python interpreter detects the bug and raises a syntax error.

Why Syntax error can be detected automatically?
Why is the print statement before the error not executed?

  • The Python interpreter can detect syntax error even before executing the code because
  • the interpreter simply fails to translate the code to lower-level executable code.

The following code raises a different kind of error.

print("Evaluating '4' + '5' + 6")
"4" + "5" + 6  # summing string with integer

Why Python throws a TypeError when evaluating '4' + '5' + 6?

There is no implementation of + operation on a value of type str and a value of type int.

  • Unlike the syntax error, the Python interpreter can only detect a type error at runtime (when executing the code.)
  • Hence, such an error is called a runtime error.

Do other languages work in the same way?

The following cell runs the corresponding javascript code using a cell magic:

%%javascript
let x = '4' + '5' + 6;
element.append(x + ' ' + typeof(x));
// no error because 6 is converted to a str implicitly
%%javascript
let x = '4' * '5' * 6;
element.append(x + ' ' + typeof(x));
// no error because 4 and 5 are converted to numbers implicitly

Javascript forces a type conversion to avoid a type error. It is called a weakly-typed language.

In comparison, C/C++ and Java are statically-typed languages that checks data type during compilation.

try:
    !gcc 456.cpp
except Error:
    print('Cannot run shell command.')
try:
    !javac 456.java
except Error:
    print('Cannot run shell command.')
num1 = input("Please input an integer: ")
num2 = input("Please input another integer: ")
print(num1, "+", num2, "is equal to", int(num1) + int(num2))
Solution to Exercise 8 #

Some possible invalid inputs are:

4 + 5 + 6, 15.0, fifteen

It raises a value error, which is a runtime error detected during execution.

Note that the followings are okay

int('-1'), eval('4 + 5 + 6')

Floating Point Numbers

Not all numbers are integers. In Enginnering, we often need to use fractions.

How to enter fractions in a program?

x = -0.1  # decimal number
y = -1.0e-1  # scientific notation
z = -1 / 10  # fraction
x, y, z, type(x), type(y), type(z)

What is the type float?

sign1.0mantissae1exponent=1×101\overbrace{-}^{\text{sign}} \underbrace{1.0}_{\text{mantissa}\kern-1em}e\overbrace{-1}^{\text{exponent}\kern-1em}=-1\times 10^{-1}

Integers in mathematics may be regarded as a float instead of int:

type(1.0), type(1e2)

You can also convert an int or a str to a float.

float(1), float("1")

Is it better to store an integer as float?

Python stores a floating point with finite precision, usually 64-bit/double precision:

sys.float_info

It cannot accurately represent a number larger than the max:

sys.float_info.max * 2

The precision also affects the check for equality.

(
    1.0 == 1.0 + sys.float_info.epsilon * 0.5,  # returns true if equal
    1.0 == 1.0 + sys.float_info.epsilon * 0.6,
    sys.float_info.max + 1 == sys.float_info.max,
)

Another issue with float is that it may keep more decimal places than desired.

1 / 3

How to round a floating point number to the desired number of decimal places?

round(2.665, 2), round(2.675, 2)

Why 2.675 rounds to 2.67 instead of 2.68?

Solution to Exercise 9 #

The value stored is 2.6749999523162841796875, which explains why it is rounded to 2.67 instead of 2.68.

The round function can also be applied to an integer.

round(150, -2), round(250, -2)

Why 250 rounds to 200 instead of 300?

  • Python 3 implements the default rounding method in IEEE 754.

String Formatting

Can we round a float or int for printing but not calculation?

This is possible with format strings:

x = 10000 / 3
print("x ≈ {:.2f} (rounded to 2 decimal places)".format(x))
x
  • {:.2f} is a replacement field or place holder
  • that gets replaced by a string
  • that represents the argument x of format
  • according to the format specification .2f, i.e.,
    a decimal floating point number rounded to 2 decimal places.
@interact(
    x="10000/3",
    align={"None": "", "<": "<", ">": ">", "=": "=", "^": "^"},
    sign={"None": "", "+": "+", "-": "-", "SPACE": " "},
    width=(0, 20),
    grouping={"None": "", "_": "_", ",": ","},
    precision=(0, 20),
)
def print_float(x, sign, align, grouping, width=0, precision=2):
    format_spec = (
        f"{{:{align}{sign}{'' if width==0 else width}{grouping}.{precision}f}}"
    )
    print("Format spec:", format_spec)
    print("x ≈", format_spec.format(eval(x)))
print("{:,.2f}".format(10000 / 3))

String formatting is useful for different data types other than float.
E.g., consider the following program that prints a time specified by some variables.

# Some specified time
hour = 12
minute = 34
second = 56

print("The time is " + str(hour) + ":" + str(minute) + ":" + str(second) + ".")

Imagine you have to show also the date in different formats.
The code can become very hard to read/write because

  • the message is a concatenation of multiple strings and
  • the integer variables need to be converted to strings.

Omitting + leads to syntax error. Removing str as follows also does not give the desired format.

print("The time is ", hour, ":", minute, ":", second, ".")  # note the extra spaces

To make the code more readable, we can use the format function as follows.

message = "The time is {}:{}:{}."
print(message.format(hour, minute, second))

According to the string formatting syntax, we can change the order of substitution using

  • indices (0 is the first item) or
  • names inside the placeholder {}:
print("You should {0} {1} what I say instead of what I {0}.".format("do", "only"))
print("The surname of {first} {last} is {last}.".format(first="John", last="Doe"))

You can also put variables inside the format specification directly and have a nested string formatting.

align, width = "^", 5
print(f"{{:*{align}{width}}}".format(x))  # note the syntax f"..."
@interact(
    expression=r"'ABC'",
    fill="*",
    align={"None": "", "<": "<", ">": ">", "=": "=", "^": "^"},
    width=(0, 20),
)
def print_object(expression, fill, align="^", width=10):
    format_spec = f"{{:{fill}{align}{'' if width==0 else width}}}"
    print("Format spec:", format_spec)
    print("Print:", format_spec.format(eval(expression)))
Solution to Exercise 11
  1. It returns a ValueError because align must be specified when fill is.
  2. The newline character is simply regarded a character. The formatting is not applied line-by-line. E.g., try 'ABC\nDEF'.