A List is a numerically ordered sequence of elements that can store elements of heterogeneous types, is iterable, mutable and allows duplicate elements.
append()
, extend()
, and insert()
methodspop()
and remove()
methodssplit()
and join()
)help(list)
Help on class list in module builtins: class list(object) | list(iterable=(), /) | | Built-in mutable sequence. | | If no argument is given, the constructor creates a new empty list. | The argument must be an iterable if specified. | | Methods defined here: | | __add__(self, value, /) | Return self+value. | | __contains__(self, key, /) | Return key in self. | | __delitem__(self, key, /) | Delete self[key]. | | __eq__(self, value, /) | Return self==value. | | __ge__(self, value, /) | Return self>=value. | | __getattribute__(self, name, /) | Return getattr(self, name). | | __getitem__(...) | x.__getitem__(y) <==> x[y] | | __gt__(self, value, /) | Return self>value. | | __iadd__(self, value, /) | Implement self+=value. | | __imul__(self, value, /) | Implement self*=value. | | __init__(self, /, *args, **kwargs) | Initialize self. See help(type(self)) for accurate signature. | | __iter__(self, /) | Implement iter(self). | | __le__(self, value, /) | Return self<=value. | | __len__(self, /) | Return len(self). | | __lt__(self, value, /) | Return self<value. | | __mul__(self, value, /) | Return self*value. | | __ne__(self, value, /) | Return self!=value. | | __repr__(self, /) | Return repr(self). | | __reversed__(self, /) | Return a reverse iterator over the list. | | __rmul__(self, value, /) | Return value*self. | | __setitem__(self, key, value, /) | Set self[key] to value. | | __sizeof__(self, /) | Return the size of the list in memory, in bytes. | | append(self, object, /) | Append object to the end of the list. | | clear(self, /) | Remove all items from list. | | copy(self, /) | Return a shallow copy of the list. | | count(self, value, /) | Return number of occurrences of value. | | extend(self, iterable, /) | Extend list by appending elements from the iterable. | | index(self, value, start=0, stop=9223372036854775807, /) | Return first index of value. | | Raises ValueError if the value is not present. | | insert(self, index, object, /) | Insert object before index. | | pop(self, index=-1, /) | Remove and return item at index (default last). | | Raises IndexError if list is empty or index is out of range. | | remove(self, value, /) | Remove first occurrence of value. | | Raises ValueError if the value is not present. | | reverse(self, /) | Reverse *IN PLACE*. | | sort(self, /, *, key=None, reverse=False) | Sort the list in ascending order and return None. | | The sort is in-place (i.e. the list itself is modified) and stable (i.e. the | order of two equal elements is maintained). | | If a key function is given, apply it once to each list item and sort them, | ascending or descending, according to their function values. | | The reverse flag can be set to sort in descending order. | | ---------------------------------------------------------------------- | Static methods defined here: | | __new__(*args, **kwargs) from builtins.type | Create and return a new object. See help(type) for accurate signature. | | ---------------------------------------------------------------------- | Data and other attributes defined here: | | __hash__ = None
- A Python List is a numerically ordered sequence of elements that can store multiple items of heterogeneous types and allows duplicate elements.
- A Python List can be created by placing comma separated values in square brackets.
# It allows us to store elements of different data types (number, boolean, string, list) in one container.
list1 = [1,2,3,4,5] #list of integers
print(list1)
list2 = [3.7, 6.5, 3.8, 7.95 ] #list of floats
print(list2)
list3 = ["hello", "this", "F", "good show"] #list of strings
print(list3)
list4 = [True, False, True] #list of boolean
print(list4)
list5 = [] #this will create an empty list
print(list5)
print(type(list5))
[1, 2, 3, 4, 5] [3.7, 6.5, 3.8, 7.95] ['hello', 'this', 'F', 'good show'] [True, False, True] [] <class 'list'>
# Nested list: list can also have another list as an item
list1 = ["Arif", 30, 5.5, [10,'rauf']]
print(list1)
['Arif', 30, 5.5, [10, 'rauf']]
# we can create empty list
empty_list = []
print (empty_list)
[]
#printing multiple lists
list1 = [1,2,3,4,5]
list2 = ["Arif", 30, 5.5, [10,'rauf']]
empty_list = []
print(list1, list2, empty_list, sep='\n')
[1, 2, 3, 4, 5] ['Arif', 30, 5.5, [10, 'rauf']] []
list1 = ["Arif", 30, 5.5]
print("list1: ", list1)
list1: ['Arif', 30, 5.5]
x = [1, 2, 3]
y = [1, 2, 3]
id(x), id(y), x is y, x==y
(140681132808768, 140681132808320, False, True)
a = [1, 2, 3]
b = [2, 3, 1]
id(a), id(b), a is b, a == b
(140681142453440, 140681142415936, False, False)
[]
and assignment `=' operatornumbers = [10, 20, 30, 40, 50]
numbers[2] = 555
print("numbers: ", numbers)
numbers: [10, 20, 555, 40, 50]
# lists allow duplicate elements
names = ['Arif', 'Rauf', 'Hadeed', 'Arif', 'Mujahid']
print(names)
['Arif', 'Rauf', 'Hadeed', 'Arif', 'Mujahid']
# A list having two sub-lists within it
a = [1,2,3,[4,5],[6,7,8,9],10,11]
# A list having a sublist, which is further having a sublist and that again having a sublist
b = [1,2,3,[4,5,[6,7,8,[9,10,11]]]]
a, b
([1, 2, 3, [4, 5], [6, 7, 8, 9], 10, 11], [1, 2, 3, [4, 5, [6, 7, 8, [9, 10, 11]]]])
# you can unpack list elements
mylist = ['learning', 'is', 'fun', 'with', 'Arif']
a, b, c, d, e = mylist # the number of variables on the left must match the number of elements in the list
print (a, d, e)
print(type(a))
learning with Arif <class 'str'>
# you can pack individual elements to a list
tuple1 = a,b,c,d,e
mylist2 = list(tuple1)
print(mylist2)
print(type(mylist2))
['learning', 'is', 'fun', 'with', 'Arif'] <class 'list'>
tuple1 = a,b,c,d,e
tuple1
type(tuple1)
tuple
index()
method of List class#You can access elements of list using indexing which starts from zero
list1 = ["Arif", 30, 5.5, [10,'rauf']]
print(list1[3])
#accessing Nested list element
print(list1[0][2]) #accessing third element of string at index 0
print(list1[3][1]) #accessing second element of Nested list
[10, 'rauf'] i rauf
#Negative indexing starts looking at the list from the right hand side
list1 = ["Arif", 30, 5.5, [10,'rauf']]
print(list1[-1]) #accessing last element
print(list1[-2]) #accessing second last element
[10, 'rauf'] 5.5
help(list1.index)
Help on built-in function index: index(value, start=0, stop=9223372036854775807, /) method of builtins.list instance Return first index of value. Raises ValueError if the value is not present.
# index(value) method is used when you know the list element and wants to get its index
# index(value) method returns the index of the first matched item with its only argument
mylist = [27, 4.5, 'arif', 64, 'hadeed', 19, 'arif']
print("\nmylist: ", mylist)
mylist.index('arif')
mylist: [27, 4.5, 'arif', 64, 'hadeed', 19, 'arif']
2
mylist[start:end:step]
list1 = ['a','b','c','d','e','f','g','h','i']
list1
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
list1[::]
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i']
list1[3:]
['d', 'e', 'f', 'g', 'h', 'i']
list1[:4]
['a', 'b', 'c', 'd']
list1[2:5]
['c', 'd', 'e']
list1[:-2]
['a', 'b', 'c', 'd', 'e', 'f', 'g']
list1[-1]
'i'
# Slicing by using strides
list1 = ['a','b','c','d','e','f','g','h','i']
print(list1[::]) # A default step of 1
print(list1[::1]) # A step of 1
print(list1[::2]) # A step of 2
print(list1[::3]) # A step of 3
['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'] ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i'] ['a', 'c', 'e', 'g', 'i'] ['a', 'd', 'g']
# Reverse slicing
list1 = ['a','b','c','d','e','f','g','h','i']
print(list1[::-1]) # Take 1 step back each time
print(list1[5:1:-1]) # Take 1 step back each time
#if start is less than end in case of a negative step, it will return empty string
print(list1[2:10:-1])
print(list1[::-2]) # Take 2 steps back
['i', 'h', 'g', 'f', 'e', 'd', 'c', 'b', 'a'] ['f', 'e', 'd', 'c'] [] ['i', 'g', 'e', 'c', 'a']
Here are some experiments you should try out:
a_list[2:10]
a_list[12:10]
a_list[2:]
or a_list[:5]
a_list[-2:-5]
or a_list[-5:-2]
(can you explain the results?)The flexible and interactive nature of Jupyter notebooks makes them an excellent tool for learning and experimentation. If you are new to Python, you can resolve most questions as soon as they arise simply by typing the code into a cell and executing it. Let your curiosity run wild, discover what Python is capable of and what it isn't!
list1 = ['a','b','c','d','e','f','g','h','i']
print(list1[::3])
print(list1[3:6])
print(list1[2:8:-1])
print(list1[-1:-3:-1])
['a', 'd', 'g'] ['d', 'e', 'f'] [] ['i', 'h']
+
operator can be used to concatenate two or more lists# Add some elements to the end of an existing list using concatenation operator
a = [1,2,3]
b = a + [4,5]
# Add some elements to the beginning of an existing list using concatenation operator
c = [0] + b
a, b, c
([1, 2, 3], [1, 2, 3, 4, 5], [0, 1, 2, 3, 4, 5])
# use + operator to concatenate two lists
food_items1 = ['fruits', 'bread', 'veggies']
food_items2 = ['meat', 'spices', 'burger']
food = food_items1 + food_items2
print(food)
['fruits', 'bread', 'veggies', 'meat', 'spices', 'burger']
# You can concatenate two heterogeneous lists
list1 = [5, 3.4, 'hello']
list2 = [31, 9.7, 'bye']
list3 = list1 + list2
print(list3)
[5, 3.4, 'hello', 31, 9.7, 'bye']
num1 = [1,2,3]
num2 = num1 + [4, 5, 6, [7, 8]]
print (num2)
[1, 2, 3, 4, 5, 6, [7, 8]]
*
operator can be used to repeat or replicate a list multiple times# use list * n syntax to create large lists by repeating the list n times
name = ['Arif', 'Hadeed', 'Mujahid']
a = name * 3
print(a)
['Arif', 'Hadeed', 'Mujahid', 'Arif', 'Hadeed', 'Mujahid', 'Arif', 'Hadeed', 'Mujahid']
#list of 100 A's
buf = ['A']
newbuf = buf * 100
print(newbuf)
['A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A', 'A']
Remember, unlike String methods, most methods of List class modify the original List rather than returning a new one.
Lists are dynamic, as we write our Python program, we can actually make changes to our already created list, whithout having to go for compiling it again.
If we have to add certain elements to an already created list, the original list gorws dynamically without the need of compiling/running the program again (as in case of heap memory in C/C++)
We can use a list methods to perform various tasks on a list object and modify it.
Some list methods that can be used to alter a list are (append, insert, extend, remove, pop, clear, index, count, sort, reverse, split and join)
Check out some common list operations here: https://www.w3schools.com/python/python_ref_list.asp .
mylist = ['data science', 'machine learning', 2, 5, 7]
# Let us change the second element of this list
mylist[1] = 'big data'
mylist
['data science', 'big data', 2, 5, 7]
# We can use the slice index to modify multiple list elements in one go
mylist = ['data science', 'machine learning', 2, 5, 7]
mylist[0:2] = ['english', 'urdu'] # Note we are replacing two elements with two elements
mylist
['english', 'urdu', 2, 5, 7]
# We can replace more number of elements with lesser number of elements in a list
mylist = ['data science', 'machine learning', 2, 5, 7]
mylist[0:2] = ['BIG DATA'] # Note we are replacing two elements with one element
mylist
['BIG DATA', 2, 5, 7]
# We can replace one element with more number of elements in a list
mylist = ['data science', 'machine learning', 2, 5, 7]
mylist[0] = ['math', 'stat'] # Note we are replacing one element with two elements
mylist
[['math', 'stat'], 'machine learning', 2, 5, 7]
mylist = ['data science', 'machine learning', 2, 5, 7]
mylist[0:1] = ['math', 'stat'] # Note we using a slice instead of single index, although it means just zero
mylist
['math', 'stat', 'machine learning', 2, 5, 7]
list.append(value)
method¶list.append(value)
method is used when you want to add a single element at the end of the listlist1 = []
help(list1.append)
Help on built-in function append: append(object, /) method of builtins.list instance Append object to the end of the list.
list1 = [2, 4, 6, 8]
list1.append(4.631)
list1.append('hello')
list1
[2, 4, 6, 8, 4.631, 'hello']
# You cannot add multiple elements using append
# If you try it will actually add those elements as a sub-list
list1 = [2, 4, 6, 8]
list1.append([4.631, 'hello'])
list1
[2, 4, 6, 8, [4.631, 'hello']]
list.extend(sublist)
method¶list.extend(sublist)
method is used when you want to add multiple elements at the end of the listlist1 = []
help(list1.extend)
Help on built-in function extend: extend(iterable, /) method of builtins.list instance Extend list by appending elements from the iterable.
list1 = [2, 4, 6, 8]
list1.extend([4.631, 'hello'])
list1
[2, 4, 6, 8, 4.631, 'hello']
# another example of extend()
fruits = ['apple', 'banana', 'cherry']
vegs = ['potato', 'tomato', 'radish']
fruits.extend(vegs)
fruits
['apple', 'banana', 'cherry', 'potato', 'tomato', 'radish']
list.insert(index, value)
method¶list.insert(index, value)
method is used when you want to add an element at a specific location in the listlist1 = []
help(list1.insert)
Help on built-in function insert: insert(index, object, /) method of builtins.list instance Insert object before index.
myfamily = ["Farooq", 'Rauf', 'Hadeed']
print("\nOriginal family list: ", myfamily)
myfamily.insert(2,'Arif')
print("After insert: ", myfamily)
Original family list: ['Farooq', 'Rauf', 'Hadeed'] After insert: ['Farooq', 'Rauf', 'Arif', 'Hadeed']
# you can insert a sub-list
myfamily = ["Farooq", 'Rauf', 'Arif']
myfamily.insert(2,['Maaz','Mohid'])
myfamily
['Farooq', 'Rauf', ['Maaz', 'Mohid'], 'Arif']
list.pop()
and list.remove()
methods¶list.pop(index)
method¶list.pop()
method without any argument removes the last item (right most element) in the list and returns itlist1 = []
help(list1.pop)
Help on built-in function pop: pop(index=-1, /) method of builtins.list instance Remove and return item at index (default last). Raises IndexError if list is empty or index is out of range.
list1 = ['learning', 'is', 'fun', 'with', 'arif', 'butt']
print("Original list: ", list1)
x = list1.pop()
print("\nAfter pop(): ", list1)
print("Element popped is: ", x)
Original list: ['learning', 'is', 'fun', 'with', 'arif', 'butt'] After pop(): ['learning', 'is', 'fun', 'with', 'arif'] Element popped is: butt
y = list1.pop(2)
print("\nAfter pop(2): ", list1)
print("Element popped is: ", y)
After pop(2): ['learning', 'is', 'with', 'arif'] Element popped is: fun
z = list1.pop(-2)
print("\nAfter pop(-2): ", list1)
print("Element popped is: ", z)
After pop(-2): ['learning', 'is', 'arif'] Element popped is: with
list.remove(val)
method¶list.remove(value)
method is used when you want to remove a specific element without returning itlist1 = []
help(list1.remove)
Help on built-in function remove: remove(value, /) method of builtins.list instance Remove first occurrence of value. Raises ValueError if the value is not present.
list2 = ['learning', 'is', 'fun', 'with', 'arif', 'butt', 'fun']
print("\nOriginal list: ", list2)
x = list2.remove('fun')
print("After remove('fun'): ", list2)
print("Return value of remove() is: ", x)
Original list: ['learning', 'is', 'fun', 'with', 'arif', 'butt', 'fun'] After remove('fun'): ['learning', 'is', 'with', 'arif', 'butt', 'fun'] Return value of remove() is: None
list.clear()
method to remove all the list elements¶list1 = []
help(list1.clear)
Help on built-in function clear: clear() method of builtins.list instance Remove all items from list.
list2 = ['learning', 'is', 'fun', 'with', 'arif', 'butt']
list2.clear()
print("\nAfter clear() the list becomes empty: ", list2)
After clear() the list becomes empty: []
del
Keyword to delete one or more list items or delete the list entirely from memory¶help('del')
The "del" statement ******************* del_stmt ::= "del" target_list Deletion is recursively defined very similar to the way assignment is defined. Rather than spelling it out in full details, here are some hints. Deletion of a target list recursively deletes each target, from left to right. Deletion of a name removes the binding of that name from the local or global namespace, depending on whether the name occurs in a "global" statement in the same code block. If the name is unbound, a "NameError" exception will be raised. Deletion of attribute references, subscriptions and slicings is passed to the primary object involved; deletion of a slicing is in general equivalent to assignment of an empty slice of the right type (but even this is determined by the sliced object). Changed in version 3.2: Previously it was illegal to delete a name from the local namespace if it occurs as a free variable in a nested block. Related help topics: BASICMETHODS
# use del keyword to delete one or more list items by using index or to delete the list entirely from memory
list3 = [1,2,3,4,5,6,7,8,9]
del list3[3:6]
list3
[1, 2, 3, 7, 8, 9]
# use del keyword to delete one or more list items by using index or to delete the list entirely from memory
list3 = [1,2,3,[53, 41, 99, 12], 8,9]
print("\nOriginal list", list3)
del list3[3]
print("After del list1[3] the list becomes", list3)
Original list [1, 2, 3, [53, 41, 99, 12], 8, 9] After del list1[3] the list becomes [1, 2, 3, 8, 9]
#to delete entire list
mylist = ['learning', 'is', 'fun', 'with', 'arif', 'butt']
del mylist
#mylist
# convert a string into list using list()
str1 = 'Learning is fun' #this is a string
print(type(str1))
print("Original string: ", str1, "and its type is: ", type(str1))
l1 = list(str1)
print("l1: ", l1, "and its type is: ", type(l1))
<class 'str'> Original string: Learning is fun and its type is: <class 'str'> l1: ['L', 'e', 'a', 'r', 'n', 'i', 'n', 'g', ' ', 'i', 's', ' ', 'f', 'u', 'n'] and its type is: <class 'list'>
str.split()
to Split a List into Strings¶str1 = ""
help(str1.split)
Help on built-in function split: split(sep=None, maxsplit=-1) method of builtins.str instance Return a list of the words in the string, using sep as the delimiter string. sep The delimiter according which to split the string. None (the default value) means split according to any whitespace, and discard empty strings from the result. maxsplit Maximum number of splits to do. -1 (the default value) means no limit.
str1 = 'Learning is fun' #this is a string
list1 = str1.split(' ')
list1
['Learning', 'is', 'fun']
str2 = "Data Science is GR8 Degree" #this is a string
list2 = str2.split('c')
list2
['Data S', 'ien', 'e is GR8 Degree']
str.join()
to Join Strings into a List¶str.split()
method, and is used to joing multiple strings by inserting the string in between on which this method is calledstr1 = ""
help(str1.join)
Help on built-in function join: join(iterable, /) method of builtins.str instance Concatenate any number of strings. The string whose method is called is inserted in between each given string. The result is returned as a new string. Example: '.'.join(['ab', 'pq', 'rs']) -> 'ab.pq.rs'
list1 = ['This', 'is', 'getting', 'more', 'and', 'more', 'interesting']
list1
['This', 'is', 'getting', 'more', 'and', 'more', 'interesting']
str2 = ' '.join(list1)
print(str2)
print(type(str2))
This is getting more and more interesting <class 'str'>
delimiter = " # "
str3 = delimiter.join(list1)
print(str3)
print(type(str3))
This # is # getting # more # and # more # interesting <class 'str'>
help(len)
Help on built-in function len in module builtins: len(obj, /) Return the number of items in a container.
help(max)
Help on built-in function max in module builtins: max(...) max(iterable, *[, default=obj, key=func]) -> value max(arg1, arg2, *args, *[, key=func]) -> value With a single iterable argument, return its biggest item. The default keyword-only argument specifies an object to return if the provided iterable is empty. With two or more arguments, return the largest argument.
help(sum)
Help on built-in function sum in module builtins: sum(iterable, /, start=0) Return the sum of a 'start' value (default: 0) plus an iterable of numbers When the iterable is empty, return the start value. This function is intended specifically for use with numeric values and may reject non-numeric types.
list_num = [3, 8, 1, 6, 0, 8, 4]
print("length of list: ", len(list_num))
print("max element in list: ", max(list_num))
print("min element in list: ",min(list_num))
print("Sum of element in list: ",sum(list_num))
length of list: 7 max element in list: 8 min element in list: 0 Sum of element in list: 30
=
Operator¶=
operator to create a copy/alias of an object.list1 = [1, 2, 3, 4]
list2 = list1
# Both variables point to same memory object, so have the same ID
print('ID of Old List:', id(list1))
print('ID of New List:', id(list2))
ID of Old List: 140681144259456 ID of New List: 140681144259456
# If you modify an element of one object, the change will be visible in both
list2[2] = 9
print('\nOld List:', list1)
print('New List:', list2)
Old List: [1, 2, 9, 4] New List: [1, 2, 9, 4]
=
operator use slice operatorcopy.copy()
method# Shallow copy using slice operator while doing assignment
list1 = [1, 2, 3, 4]
list2 = list1[:]
# Both variables point to different memory objects, so have the different ID
print('ID of Old List:', id(list1))
print('ID of New List:', id(list2))
ID of Old List: 140428124989824 ID of New List: 140428124140096
# If you modify an element of one object, the change will be not visible in both
list2[2] = 9
print('\nOld List:', list1)
print('New List:', list2)
Old List: [1, 2, 3, 4] New List: [1, 2, 9, 4]
# Shallow copy using the `copy.copy()` method
import copy
list1 = [1, 2, 3, 4]
list2 = copy.copy(list1)
# Both variables point to different memory objects, so have different ID
print('ID of Old List:', id(list1))
print('ID of New List:', id(list2))
ID of Old List: 140681144262720 ID of New List: 140681144264704
# If you modify an element of one object, the change will not be visible in other
list2[3] = 'a'
print('\nOld List:', list1)
print('New List:', list2)
Old List: [1, 2, 3, 4] New List: [1, 2, 3, 'a']
This seems woking fine, then why this is called shallow copy
Limitation of Shallow Copy
import copy
old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
new_list = copy.copy(old_list)
# Both variables point to different memory object, having same references of original object elements
print('ID of Old List:', id(old_list))
print('ID of New List:', id(new_list))
ID of Old List: 140681144288256 ID of New List: 140681144288000
# If you modify an element of one object, the change will be visible in both
new_list[2][2] = 'a'
# See contents of both lists
print('\nOld List:', old_list)
print('New List:', new_list)
Old List: [[1, 2, 3], [4, 5, 6], [7, 8, 'a']] New List: [[1, 2, 3], [4, 5, 6], [7, 8, 'a']]
copy.deepcopy()
Method¶import copy
old_list = [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
new_list = copy.deepcopy(old_list)
# Both variables point to different memory object, having their own object elements
print('ID of Old List:', id(old_list))
print('ID of New List:', id(new_list))
ID of Old List: 140681144290368 ID of New List: 140681144289024
# If you modify an element of one object, the change will be made only to that object element
new_list[2][2] = 'a'
# See contents of both lists
print('\nOld List:', old_list)
print('New List:', new_list)
Old List: [[1, 2, 3], [4, 5, 6], [7, 8, 9]] New List: [[1, 2, 3], [4, 5, 6], [7, 8, 'a']]
list.sort()
method can sort the list elements (ascending + descending) on which it is called and returns None (modifies the list on which it is called).list.reverse()
method simply reverse the list elements on which it is called and returns None (modifies the list on which it is called).sorted(iterable)
is a built-in function, which returns a sorted version of the list, which is passed to it as parameter (list remains as it is). Limitation is it is a bit slow as it makes a copy of the list. Advantage is that it can sort any iterable.list.sort()
method¶# Modifies list on which it is called and returns none/void
list_num = [3, 8, 1, 6, 0, 8, 4]
print("Original numbers list: ", list_num)
list_num.sort()
print("Ascending Sort: ", list_num)
list_num.sort(reverse=True)
print("Descending Sort: ", list_num)
Original numbers list: [3, 8, 1, 6, 0, 8, 4] Ascending Sort: [0, 1, 3, 4, 6, 8, 8] Descending Sort: [8, 8, 6, 4, 3, 1, 0]
list_names = ["XYZ", "ABC", "MNO", "ARIF"]
print("\nOriginal names list: ", list_names)
list_names.sort()
print("Ascending Sort: ", list_names)
list_names.sort(reverse=True)
print("Descending Sort: ", list_names)
Original names list: ['XYZ', 'ABC', 'MNO', 'ARIF'] Ascending Sort: ['ABC', 'ARIF', 'MNO', 'XYZ'] Descending Sort: ['XYZ', 'MNO', 'ARIF', 'ABC']
list.sort()
Method¶Example 1: Suppose given a list with elements ['ccc', 'aaaaa', 'd', 'bb']
and I want to sort it by length of strings within the list so that the output list is like : ['d', 'bb', 'ccc', 'aaaaa]
list.sort(*, key=None, reverse=False)
key
parameter of list.sort()
methodlist.sort()
method will run this one argument function on all the elements of the list and return a single value, and build up a shadow list of those new values.len()
, which when called on every element of list1
will return a shadow list containing length of every string within the original list containing [3,4,2,1]
list1.sort()
method will then sort list1
elements based on the values of the shadow list and generate the required sorted list :)list1 = ['ccc', 'aaaaa', 'd', 'bb']
list1.sort()
list1
['aaaaa', 'bb', 'ccc', 'd']
#Example 1:
list1 = ['ccc', 'aaaaa', 'd', 'bb']
list1.sort(key=len)
list1
['d', 'bb', 'ccc', 'aaaaa']
Example 2: Suppose given a list with elements ['abcz', 'xyza', 'bas', 'arif']
and I want to sort it by last character of strings within the list so that the output list is like : ['xyza', 'arif', 'bas', 'abcz']
list.sort(*, key=None, reverse=False)
key
parameter of list.sort()
methodlist.sort()
method will run this one argument function on all the elements of the list and return a single value, and build up a shadow list of those new values.list1
will return a shadow list containing only the last character of every string within the original list containing ['z', 'a', 's', 'f']
list1.sort()
method will then sort list1
elements based on the values of the shadow list and generate the required sorted list :)def last(s):
return s[-1]
list1 = ['abcz', 'xyza', 'bas', 'arif']
list1.sort(key=last)
list1
['xyza', 'arif', 'bas', 'abcz']
in
Operator on Lists¶help('in')
Membership test operations ************************** The operators "in" and "not in" test for membership. "x in s" evaluates to "True" if *x* is a member of *s*, and "False" otherwise. "x not in s" returns the negation of "x in s". All built-in sequences and set types support this as well as dictionary, for which "in" tests whether the dictionary has a given key. For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression "x in y" is equivalent to "any(x is e or x == e for e in y)". For the string and bytes types, "x in y" is "True" if and only if *x* is a substring of *y*. An equivalent test is "y.find(x) != -1". Empty strings are always considered to be a substring of any other string, so """ in "abc"" will return "True". For user-defined classes which define the "__contains__()" method, "x in y" returns "True" if "y.__contains__(x)" returns a true value, and "False" otherwise. For user-defined classes which do not define "__contains__()" but do define "__iter__()", "x in y" is "True" if some value "z", for which the expression "x is z or x == z" is true, is produced while iterating over "y". If an exception is raised during the iteration, it is as if "in" raised that exception. Lastly, the old-style iteration protocol is tried: if a class defines "__getitem__()", "x in y" is "True" if and only if there is a non- negative integer index *i* such that "x is y[i] or x == y[i]", and no lower integer index raises the "IndexError" exception. (If any other exception is raised, it is as if "in" raised that exception). The operator "not in" is defined to have the inverse truth value of "in". Related help topics: SEQUENCEMETHODS
list_num = [3, 8, 1, 6, 0, 8, 4]
rv1 = 9 in list_num
print(rv1)
rv2 = 9 not in list_num
print(rv2)
list_names = ["XYZ", "ABC", "MNO", "ARIF"]
rv3 = "ARIF" in list_names
print(rv3)
False True True
help(id)
Help on built-in function id in module builtins: id(obj, /) Return the identity of an object. This is guaranteed to be unique among simultaneously existing objects. (CPython uses the object's memory address.)
# in case of strings, both a and b refers to the same memory location containing string 'hello'
a = 'hello'
b = 'hello'
id(a), id(b)
(140681142371248, 140681142371248)
# in case of list, both a and b refers to two different objects in the memory having same values
x = ['hello']
y = ['hello']
id(x), id(y)
(140681144288320, 140681104115968)
help('is')
Comparisons *********** Unlike C, all comparison operations in Python have the same priority, which is lower than that of any arithmetic, shifting or bitwise operation. Also unlike C, expressions like "a < b < c" have the interpretation that is conventional in mathematics: comparison ::= or_expr (comp_operator or_expr)* comp_operator ::= "<" | ">" | "==" | ">=" | "<=" | "!=" | "is" ["not"] | ["not"] "in" Comparisons yield boolean values: "True" or "False". Comparisons can be chained arbitrarily, e.g., "x < y <= z" is equivalent to "x < y and y <= z", except that "y" is evaluated only once (but in both cases "z" is not evaluated at all when "x < y" is found to be false). Formally, if *a*, *b*, *c*, …, *y*, *z* are expressions and *op1*, *op2*, …, *opN* are comparison operators, then "a op1 b op2 c ... y opN z" is equivalent to "a op1 b and b op2 c and ... y opN z", except that each expression is evaluated at most once. Note that "a op1 b op2 c" doesn’t imply any kind of comparison between *a* and *c*, so that, e.g., "x < y > z" is perfectly legal (though perhaps not pretty). Value comparisons ================= The operators "<", ">", "==", ">=", "<=", and "!=" compare the values of two objects. The objects do not need to have the same type. Chapter Objects, values and types states that objects have a value (in addition to type and identity). The value of an object is a rather abstract notion in Python: For example, there is no canonical access method for an object’s value. Also, there is no requirement that the value of an object should be constructed in a particular way, e.g. comprised of all its data attributes. Comparison operators implement a particular notion of what the value of an object is. One can think of them as defining the value of an object indirectly, by means of their comparison implementation. Because all types are (direct or indirect) subtypes of "object", they inherit the default comparison behavior from "object". Types can customize their comparison behavior by implementing *rich comparison methods* like "__lt__()", described in Basic customization. The default behavior for equality comparison ("==" and "!=") is based on the identity of the objects. Hence, equality comparison of instances with the same identity results in equality, and equality comparison of instances with different identities results in inequality. A motivation for this default behavior is the desire that all objects should be reflexive (i.e. "x is y" implies "x == y"). A default order comparison ("<", ">", "<=", and ">=") is not provided; an attempt raises "TypeError". A motivation for this default behavior is the lack of a similar invariant as for equality. The behavior of the default equality comparison, that instances with different identities are always unequal, may be in contrast to what types will need that have a sensible definition of object value and value-based equality. Such types will need to customize their comparison behavior, and in fact, a number of built-in types have done that. The following list describes the comparison behavior of the most important built-in types. * Numbers of built-in numeric types (Numeric Types — int, float, complex) and of the standard library types "fractions.Fraction" and "decimal.Decimal" can be compared within and across their types, with the restriction that complex numbers do not support order comparison. Within the limits of the types involved, they compare mathematically (algorithmically) correct without loss of precision. The not-a-number values "float('NaN')" and "decimal.Decimal('NaN')" are special. Any ordered comparison of a number to a not-a-number value is false. A counter-intuitive implication is that not-a-number values are not equal to themselves. For example, if "x = float('NaN')", "3 < x", "x < 3" and "x == x" are all false, while "x != x" is true. This behavior is compliant with IEEE 754. * "None" and "NotImplemented" are singletons. **PEP 8** advises that comparisons for singletons should always be done with "is" or "is not", never the equality operators. * Binary sequences (instances of "bytes" or "bytearray") can be compared within and across their types. They compare lexicographically using the numeric values of their elements. * Strings (instances of "str") compare lexicographically using the numerical Unicode code points (the result of the built-in function "ord()") of their characters. [3] Strings and binary sequences cannot be directly compared. * Sequences (instances of "tuple", "list", or "range") can be compared only within each of their types, with the restriction that ranges do not support order comparison. Equality comparison across these types results in inequality, and ordering comparison across these types raises "TypeError". Sequences compare lexicographically using comparison of corresponding elements. The built-in containers typically assume identical objects are equal to themselves. That lets them bypass equality tests for identical objects to improve performance and to maintain their internal invariants. Lexicographical comparison between built-in collections works as follows: * For two collections to compare equal, they must be of the same type, have the same length, and each pair of corresponding elements must compare equal (for example, "[1,2] == (1,2)" is false because the type is not the same). * Collections that support order comparison are ordered the same as their first unequal elements (for example, "[1,2,x] <= [1,2,y]" has the same value as "x <= y"). If a corresponding element does not exist, the shorter collection is ordered first (for example, "[1,2] < [1,2,3]" is true). * Mappings (instances of "dict") compare equal if and only if they have equal *(key, value)* pairs. Equality comparison of the keys and values enforces reflexivity. Order comparisons ("<", ">", "<=", and ">=") raise "TypeError". * Sets (instances of "set" or "frozenset") can be compared within and across their types. They define order comparison operators to mean subset and superset tests. Those relations do not define total orderings (for example, the two sets "{1,2}" and "{2,3}" are not equal, nor subsets of one another, nor supersets of one another). Accordingly, sets are not appropriate arguments for functions which depend on total ordering (for example, "min()", "max()", and "sorted()" produce undefined results given a list of sets as inputs). Comparison of sets enforces reflexivity of its elements. * Most other built-in types have no comparison methods implemented, so they inherit the default comparison behavior. User-defined classes that customize their comparison behavior should follow some consistency rules, if possible: * Equality comparison should be reflexive. In other words, identical objects should compare equal: "x is y" implies "x == y" * Comparison should be symmetric. In other words, the following expressions should have the same result: "x == y" and "y == x" "x != y" and "y != x" "x < y" and "y > x" "x <= y" and "y >= x" * Comparison should be transitive. The following (non-exhaustive) examples illustrate that: "x > y and y > z" implies "x > z" "x < y and y <= z" implies "x < z" * Inverse comparison should result in the boolean negation. In other words, the following expressions should have the same result: "x == y" and "not x != y" "x < y" and "not x >= y" (for total ordering) "x > y" and "not x <= y" (for total ordering) The last two expressions apply to totally ordered collections (e.g. to sequences, but not to sets or mappings). See also the "total_ordering()" decorator. * The "hash()" result should be consistent with equality. Objects that are equal should either have the same hash value, or be marked as unhashable. Python does not enforce these consistency rules. In fact, the not-a-number values are an example for not following these rules. Membership test operations ========================== The operators "in" and "not in" test for membership. "x in s" evaluates to "True" if *x* is a member of *s*, and "False" otherwise. "x not in s" returns the negation of "x in s". All built-in sequences and set types support this as well as dictionary, for which "in" tests whether the dictionary has a given key. For container types such as list, tuple, set, frozenset, dict, or collections.deque, the expression "x in y" is equivalent to "any(x is e or x == e for e in y)". For the string and bytes types, "x in y" is "True" if and only if *x* is a substring of *y*. An equivalent test is "y.find(x) != -1". Empty strings are always considered to be a substring of any other string, so """ in "abc"" will return "True". For user-defined classes which define the "__contains__()" method, "x in y" returns "True" if "y.__contains__(x)" returns a true value, and "False" otherwise. For user-defined classes which do not define "__contains__()" but do define "__iter__()", "x in y" is "True" if some value "z", for which the expression "x is z or x == z" is true, is produced while iterating over "y". If an exception is raised during the iteration, it is as if "in" raised that exception. Lastly, the old-style iteration protocol is tried: if a class defines "__getitem__()", "x in y" is "True" if and only if there is a non- negative integer index *i* such that "x is y[i] or x == y[i]", and no lower integer index raises the "IndexError" exception. (If any other exception is raised, it is as if "in" raised that exception). The operator "not in" is defined to have the inverse truth value of "in". Identity comparisons ==================== The operators "is" and "is not" test for an object’s identity: "x is y" is true if and only if *x* and *y* are the same object. An Object’s identity is determined using the "id()" function. "x is not y" yields the inverse truth value. [4] Related help topics: EXPRESSIONS, BASICMETHODS
x = ['hello']
y = ['hello']
print (x is y) # is operator is checking the memory address (ID) of two lists
print (x == y) # == operator is checking the contents of two lists
False True
x = 'hello'
y = 'hello'
print (x is y) # is operator is checking the memory address (ID) of two strings
print (x == y) # == operator is checking the contents of two strings
True True
array(typecode [, initializer])
Return a new array whose items are restricted by typecode, and initialized from the optional initializer value, which must be a list, string or iterable over elements of the appropriate type.
Arrays represent basic values and behave very much like lists, except the type of objects stored in them is constrained. The type is specified at object creation time by using a type code, which is a single character.
The following type codes are defined:
Type code C Type Minimum size in bytes 'b' signed integer 1 'B' unsigned integer 1 'u' Unicode character 2 (see note) 'h' signed integer 2 'H' unsigned integer 2 'i' signed integer 2 'I' unsigned integer 2 'l' signed integer 4 'L' unsigned integer 4 'q' signed integer 8 (see note) 'Q' unsigned integer 8 (see note) 'f' floating point 4 'd' floating point 8
# To use Python arrays, you have to import Python's built-in array module
import array
# declaring array of integers
arr1 = array.array("i", [3, 6, 9, 2])
print(arr1)
print(type(arr1))
# declaring array of floats
arr2 = array.array("f", [3.4, 6.7, 9.5, 2])
print(arr2)
print(type(arr2))
array('i', [3, 6, 9, 2]) <class 'array.array'> array('f', [3.4000000953674316, 6.699999809265137, 9.5, 2.0]) <class 'array.array'>
arr2.insert
Try answering the following questions to test your understanding of the topics covered in this notebook:
a_list[2:5]
contain?a_list[:2]
and a_list[2:]
represent?a_new_list = a_list
create a copy of the list a_list
?Go to https://pythontutor.com/ and copy paste following code to visualize aliasing, shallow and deep copy
def func(s):
return s[-1]
l1 = ['arif', 'zeeshan', 'baz']
sorted(l1, key=func, reverse=True)
['baz', 'zeeshan', 'arif']
func('arif')
'f'