Names and Objects¶
Names are references to objects!
In static (compiled) languages we think of variables as values with a name tag. The value may change, but the name tag remains at its place. The object itself may or may not be changed. There is no name without an object, but there may be objects without a name.
In Python we never(!) talk of pointers, but that is what happens behind the scene. A name refers to exactly one object at a time, but can be changed to refer to an other object later.
Everything in Python is an Object¶
The above example already introduced some objects: integers, floating point numbers, strings and, yes, functions. There are many more of course.
All objects have a type and a unique identity. There are two built-in functions, which give us the type and the id of an object.
number = 3456
print("number:", number, type(number))
show_t('number')
number: 3456 <class 'int'>
number >> 3456, type: <class 'int'>
a_type = type(7)
show_t('a_type')
a_type >> <class 'int'>, type: <class 'type'>
string = "hello world"
show_t('string')
string >> hello world, type: <class 'str'>
Names may refer to the same objects. This can be tested with the ‘is’ condition. Names may refer to different but equal objects. This is tested with the ‘==’condition.
# equality vs. identity
float_1 = 17.99
float_2 = 17.99
float_3 = float_1
print("float_1:", float_1, type(float_1), id(float_1))
print("float_2:", float_2, type(float_2), id(float_2))
print("float_3:", float_3, type(float_3), id(float_3))
print("equal?", float_1 == float_2 == float_3)
print("1 is 2?", float_1 is float_2, ", 1 is 3?:", float_1 is float_3)
float_1: 17.99 <class 'float'> 115639280
float_2: 17.99 <class 'float'> 115639328
float_3: 17.99 <class 'float'> 115639280
equal? True
1 is 2? False , 1 is 3?: True
show_t('None; True; False')
None >> None, type: <class 'NoneType'>
True >> True, type: <class 'bool'>
False >> False, type: <class 'bool'>
As we can see, Python uses the terms ‘type’ and ‘class’ and in fact, they are essentially the same.
Some more data types¶
The datatypes we have seen so far are skalar values. One name points to one object. Here some collection types.
Tuple - an immutable sequence¶
Tuples can contain any type of (mixed) objects. Once created, a tuple can not be changed. A tuple is specified as values separated by a comma. The parenthesis are not always required.
tup_1 = ('a', 7, True)
tup_1 >> ('a', 7, True), type: <class 'tuple'>
Tuples can occur on both sides of an assignment. This is called ‘tuple-packing’ and ‘tuple-unpacking’.
tup_2 = 9, 'xyz', 3*7, True # assign values as a tuple
(t1, t2, t3, t4) = tup_2 # unpack values from a tuple
t1, t2, t3, t4 = tup_2 # also valid w/o parenthesis
tup_2 >> (9, 'xyz', 21, True)
t1 >> 9
t2 >> xyz
t3 >> 21
t4 >> True
len(tup_2) >> 4
t1, t2, t3 = t2, t3, t1 # tuples on both sides allow for swapping
t1 >> xyz
t2 >> 21
t3 >> 9
String - an immutable sequence of characters¶
A string is a very powerful class in Python, with a lot of operations defined. In Python 3.x strings are encoded as UTF-8, an encoding, which can represent all languages in this world. As storage devices and networks operate on bytes, there has to be an explicit decoding step on input and an encoding step on output.
As a programmer we have to know our data. Python forces us to be explicit about our assumptions.
An immutable sequence of characters? - Actually there is no character type in python, a character is just a string of length 1.
st = "hello world" # apostrophes (") and quotes (') have the same meaning
ch = 'X'
st >> hello world, type: <class 'str'>
ch >> X, type: <class 'str'>
Just a few examples of string methods, there are many(!) more:
st = 'what a wonderful world'
st.upper() >> WHAT A WONDERFUL WORLD
st.capitalize() >> What a wonderful world
st.center(30) >> what a wonderful world
st.split() >> ['what', 'a', 'wonderful', 'world']
st.split("o",1) >> ['what a w', 'nderful world']
# 'join()' is also a string method
wordlst = ('my', 'favourite', 'language')
sep = ', '
''.join(wordlst) >> myfavouritelanguage
sep.join(wordlst) >> my, favourite, language
Addition and multiplication are valid string operations
'hello'+sep+'world' >> hello, world
'[##]'*7 >> [##][##][##][##][##][##][##]
List - a mutable sequence¶
A list is specified with square brackets. A ist can contain any number and types of objects.
list_1 = [5, 3, 8, 5, 0]
list_2 = ['a', 'b', list_1, 'z'] # list containing another list
list_1 >> [5, 3, 8, 5, 0], type: <class 'list'>
list_2 >> ['a', 'b', [5, 3, 8, 5, 0], 'z'], type: <class 'list'>
Lists and tuples and strings allow for indexing and slicing.
lst = ['abc', 'def', 'ghi', 'jkl', 'xyz']
lst >> ['abc', 'def', 'ghi', 'jkl', 'xyz']
lst[0] >> abc
lst[-1] >> xyz
lst[1:3] >> ['def', 'ghi']
lst[2:] >> ['ghi', 'jkl', 'xyz']
lst[:3] >> ['abc', 'def', 'ghi']
lst[:] >> ['abc', 'def', 'ghi', 'jkl', 'xyz']
lst[::-1] >> ['xyz', 'jkl', 'ghi', 'def', 'abc']
Lists can be modified in many(!) different ways.
lst = ['abc', 'def', 'ghi', 'jkl', 'xyz']
first = lst.pop(0)
lst.append(first) # pop() and append() are methods of the list class
first >> abc
lst >> ['def', 'ghi', 'jkl', 'xyz', 'abc']
lst[2:4] = list('9876') # replace a slice by a new list
lst >> ['def', 'ghi', '9', '8', '7', '6', 'abc']
sorted(lst) >> ['6', '7', '8', '9', 'abc', 'def', 'ghi']
About Mutability¶
What does the distinction between mutabile and immutable really mean? With strings and numbers, it is easy. They are simply immutable. The immutability of a tuple means that it always refers to the same objects. None of them can be removed or replaced.
tup = (1, 'abc', (9,8))
tup >> (1, 'abc', (9, 8)) type: <class 'tuple'>
tup[2] = 'x'
TypeError Traceback (most recent call last)
----> 1 tup[2] = 'x'
TypeError: 'tuple' object does not support item assignment
But then, when an object in an immutable collection itself is mutable, then it an be changed.
tup = (1, 'abc', [2,3])
tup[2].append(4)
tup >> (1, 'abc', [2, 3]) type: <class 'tuple'>
tup[2] >> [2, 3] type: <class 'list'>
tup >> (1, 'abc', [2, 3, 4])
Immutability is absolute for scalar types. For any kind of collections, if refers only to the first level, to the immediately referenced object.
As a side quest: how to change the second level list object to an empty list?
# tup[2] = [] # this would not work
tup[2][:] = [] # this works: we replace the elements of the existing list
tup >> (1, 'abc', [])
Dictionaries - A mapping type¶
Dictionaries map keys to values. The keys are unique, and must be immutable objects, the values can be anything.
One way to specify a dictionary literal is with curly braces:
dic1 = {'key2':222, 'key1':111, 'key4':444, 'key3':333, 'key1': 555}
dic1 >> {'key2': 222, 'key1': 555, 'key4': 444, 'key3': 333}, type: <class 'dict'>
There are different ways to access the keys or objects in a dictionary:
dic1['key2'] >> 222
len(dic1) >> 4
dic1.keys() >> dict_keys(['key2', 'key1', 'key4', 'key3']), type: <class 'dict_keys'>
list(dic1.keys()) >> ['key2', 'key1', 'key4', 'key3'], type: <class 'list'>
sorted(dic1.keys()) >> ['key1', 'key2', 'key3', 'key4'], type: <class 'list'>
dic1.values() >> dict_values([222, 555, 444, 333]), type: <class 'dict_values'>
dic1.items() >> dict_items([('key2', 222), ('key1', 555), ('key4', 444), ('key3', 333)]), type: <class 'dict_items'>
If the keys are like valid python names, there is a shorter way to define a dictionary:
a = 'c'
dic2 = dict(a=5, b=10, c=15) # names are taken literally
dic3 = {a:4, 8:a, 'a':9} # names are evaluated
dic2 >> {'a': 5, 'b': 10, 'c': 15}, type: <class 'dict'>
dic3 >> {'c': 4, 8: 'c', 'a': 9}, type: <class 'dict'>
Dictionaries are everywhere in Python. The Python interpreter itself relies havily on dictionaries. All names are stored in dictionaries, and in most cases there is a way to access the names of a given object. locals() is a method which returns all locally defined names as a dicitionary.
type(locals()) >> <class 'dict'>
globals()['a'] >> c
dir(a)[30:35] >> ['__sizeof__', '__str__', '__subclasshook__', 'capitalize', 'casefold']
Sets - a collection class for uniqe values¶
A set is like a dictionary without values, just a collection of keys. Set elements must be immutable. The Set class implements all mathematical set operations.
items = list("hello world")
s1 = set(items[:8])
s2 = set(items[5:])
items >> ['h', 'e', 'l', 'l', 'o', ' ', 'w', 'o', 'r', 'l', 'd']
s1 >> {'l', 'h', 'e', 'w', 'o', ' '}, type: <class 'set'>
s2 >> {'r', 'l', 'w', 'o', 'd', ' '}, type: <class 'set'>
sorted(s1) >> [' ', 'e', 'h', 'l', 'o', 'w']
sorted(s2) >> [' ', 'd', 'l', 'o', 'r', 'w']
s1.union(s2) >> {' ', 'r', 'l', 'h', 'w', 'o', 'd', 'e'}
s1.intersection(s2) >> {'w', ' ', 'l', 'o'}
s1.symmetric_difference(s2) >> {'r', 'd', 'e', 'h'}