<jupyter_start><jupyter_text>Chapter 8 Lists and Dictionaries Both the list and dictionary object types are collections of other objects. List and dictionary are flexible: they can be change in=place, can grow and shrinnk on demnad, and may contain and be nested in any other kind of object.  Python lists are:1. Ordered collections of arbitary objectsFrom a functional view, lists are just places to collect other objects so you can treat them as groups. Lists also maintain a left-to-right positional ordering among the items they contain(i.e., they are sequences)2. Accessed by offset Just as with strings, you can fetch a component object out of a list by indexing the list on the object's offset. Because items in lists are ordered by their positions, you can also do tasks such as slicing and concatenation.3. Variable-length, heterogenenous, and arbitarily nestableUnlike strings, lists can grow ans shrink in-place (their lengths can vary), and they can contain any sort of object, not just one-charater strings (they're heterogeneous). Because lists can contain other complex objects, they also support arbitary nesting.You can create lists of lists of lists, and so on.4. Of the category "mutable sequence"lists are mutable (i.e., can be changed in－place) and can respond to all the　sequence operations used with strings, such as　indexing, slicing, and concatenation.　5. Arrays of object references<jupyter_code>## Table Common list literals and operation
L = []  # An empty list 
type(L), len(L)
L = ['abc',['def', 'ghi']] # Nested sublists
L
L = list('spam')
print(L)
L = list(range(-4,4))
print(L)
L[0], L[1], L[2], L[3], L[4], L[5], L[6], L[7]
L = range(-4,4)
print(L)
L[0], L[1], L[2], L[3], L[4], L[5], L[6], L[7]
len(L)
L = list(range(-4,4))
L1 = L[0:4] + L[4:8]
L2 = L[7:8]

print(L1, L2, L1+L2)
L *3 
for x in L : print(x) # Iteration, membership
3 in L
L.append(5) # Methods: growing
L
L.extend([5,6,7])
L
L.insert?
L
L.insert(4,'5')
L.index(0)
L.index?
L.count(5)
print(L)
L = 'werworjjrqwjafklallewrwraklalakjkwerkkrkklrwkrkwekwklrr'.split()
print(L)
print(L.sort())
L = []
for i in 'werworjjrqwjafklallewrwraklalakjkwerkkrkklrwkrkwekwklrr':
    L.append(i)
print(L)
L.sort()
print(L)
L.reverse()
print(L)
del L[0:5]
print(L)
L.pop(0), L.pop(1), L.pop(2), L.pop(3)
print(L)
L.remove('w')
print(L)
L = []
for i in 'werweiawedahdgjasdawieiasfhauifawueuweuuafhuiasdhfuisdhafuihas':
    L.append(i)
L
List = [x*2 for x in L ] # List comprehensions and maps
print(List)
list(map(ord,'spam')) # ASCII
for k in 'spam':
    print(ord(k))
L1 = []
for i in """Many of the operations in Table 8-1 should look familiar, as they are the same sequence
operations we put to work on strings—indexing, concatenation, iteration, and so on.
Lists also respond to list-specific method calls (which provide utilities such as sorting,
reversing, adding items to the end, etc.), as well as in-place change operations (deleting
items, assignment to indexes and slices, and so forth). Lists have these tools for change
operations because they are a mutable object type.""":
    L1.append(i)
print(L1)
L1.reverse()
print(L1)
S = ''
S.join?
for i in L1:
    L2 = ''.join(i)
print(L2)
''.join(L1)<jupyter_output><empty_output><jupyter_text>Lists in Action<jupyter_code>len([1,2,3]) #Length
[1,2,3,4,5] + [5,6,7,8,9] # Concatenation
['Ni!'] * 4 # Repetition
str([1,2]) + "34" # Same as "[1,2]" + "34"
[1,2,4,5,6] + [24,5,6,4]
['s','t']+list("fakjfaiowioeiowioafhasfaiioawehiiahiaw")
[1,2] + list("34") == [1,2]+["3",'4']
[1,2] + list("34") is [1,2]+['3','4']<jupyter_output><empty_output><jupyter_text>List Iteration and Comprehensions<jupyter_code>3 in [1,2,3,4,5,6] # Membership
for x in [1,2,3]:     # Iteration
    print(x,end=' ')
    x = x + 1
    print(x, end = ' ')
res = [c*4 for c in 'SPAM']
res
res = [] 
for c in 'SPAM':   # List comprehension equivalent
    res.append(c*4)
res
dic = {}
for i in range(4):
    dic[i] = res[i]
dic
type(dic)
list(map(abs,[-1,-2,0,1,2])) # map function across
map(abs,[-1,-2,0,1,2])
list(map(ord,L1))<jupyter_output><empty_output><jupyter_text>Indexing, Slcing, and Matrixes<jupyter_code>L = ['spam','Spam', 'SPAM!']
L[2] # Offsets start at zero
L[-2] # Negative: count from the right
L[1:] # Slicing fetches sections<jupyter_output><empty_output><jupyter_text>One note here: because you can nest lists and other object types within lists, you willsometimes need to string together index operations to go deeper into a data structure.For example, one of the simplest ways to represent matrixes (multidimensional arrays)in Python is as lists with nested sublists.<jupyter_code>4-4+44, (5-5)*5+55,66+6-6 + 6-6
matrix = [[1,2,3],[4,5,6],[7,8,9]]
matrix[1],matrix[1][1],matrix[2][0]
print(matrix)<jupyter_output>[[1, 2, 3], [4, 5, 6], [7, 8, 9]]<jupyter_text>Changing Lists In-Place<jupyter_code>L = ['spam', "Spam","SPAM!"] # Lists
S = "'spam','Spam','SPAM!'" # Strings
for i in range(len(S)-1):
    print(S[i], end=' ')
    if i < len(L):
        print(L[i])
L[1]='eggs'
print(L, end = '\t')
# S[1] = 'eggs' # 'str' object does not support item assignment
print(S)
L[0:2] = ['eat','more'] # Slice assignment: delete + insert
L # Replace items 0.1
for i in range(11):
    for j in range(11):
        print(('%d x %d = %d')%(i,j,i*j),end = '   ')
for i in range(1,10):
    for m in range (1,i+1):
        sum = i * m
        if m < i:
            if m == 3 and i ==4:
                print('',m,'x',i,'=',sum, end='   ')
            else:
                print(m, 'x', i,  '=', sum ,end= '   ') 
        else:
            if m==3:
                print('', m, 'x',i, '=',sum)
            else:
                print(m, 'x', i, '=', sum)
del L[0:2]
L
L = ['spam', 'Spam', 'SPAM!']
L
L.insert?
L.insert(1,'more')
L
L.sort()
L
S[0] + S[1] + S[2]
S
S1 = S + ',more'
S1
L.reverse()
L
S = ''
for i in L:
    S = S + i + ','
S
S = """','""".join(L)
S
S1 = """'""" + S + """'"""
S1
S1, L
L[1] = 'eggs'   # Index assignment
S2 = S1.replace('Spam','eggs')  # you have to make a new copy
print(L, S2,S1)
L[0:2] = ['eat','more'] # Slice assigment: delete + insert
L<jupyter_output><empty_output><jupyter_text>Both index and slice assignments are in-place changes--they modify the subject list directly, rather than generating a new list object for the result.   List method calls<jupyter_code>L = ['eat','more', 'spam']
L.append('please')
L
L.sort()
L
L = ['abc','ABD','aBe'] # Sort with mixed case
L.sort()
L
for i in L:                         
    for j in i:
        print(ord(j),'=>',j, end='  \t')<jupyter_output>65 => A  	66 => B  	68 => D  	97 => a  	66 => B  	101 => e  	97 => a  	98 => b  	99 => c<jupyter_text>Another commonly seen method, sort, orders a list in place; it uses Python standard comparsion tests (here, string comparsion), and by default sorts in ascending order.<jupyter_code>L[2:5] = L[3:6]
L<jupyter_output><empty_output><jupyter_text>This description needs to be clarified. We should realize that the value to be inserted is fetched before the deletion happens on the left<jupyter_code>L[len(L):] = ['ABd']
L
L[:0]= ['aBd']
L<jupyter_output><empty_output><jupyter_text>Unlike + concatenation, append does't have to generate new objects, so it's ususally faster. You can also use the clever assignments: L[len(L):]=[x] is like L.append(X), and L[:0]=[X] is like appending at the front of a list. Both delete an empty slice and insert X, changing L in-place quickly, like append.<jupyter_code>L.sort(key=str.lower)
L
L = ['abc','ABD','aBe']
L.sort(key=str.lower, reverse = True)
L
L.sort(key=str.lower)
L
print(type("""You can modify sort behavior by passing in keyword arguments—a special “name=value” syntax in function calls that specifies passing by name and is often used for giving configuration options. In sorts, the key argument gives a one-argument function that returns the value to be used in sorting, and the reverse argument allows sorts
to be made in descending instead of ascending order""".split()))
S ="""You can modify sort behavior by passing in keyword arguments—a special
“name=value” syntax in function calls that specifies passing by name and is often used
for giving configuration options. In sorts, the key argument gives a one-argument function that returns the value to be used in sorting, and the reverse argument allows sorts
to be made in descending instead of ascending order"""
len(S)
L = """You can modify sort behavior by passing in keyword arguments—a special “name=value” syntax in function calls that specifies passing by name and is often used for giving configuration options. In sorts, the key argument gives a one-argument function that returns the value to be used in sorting, and the reverse argument allows sorts
to be made in descending instead of ascending order""".split()
print(L)
L.sort(key=str.lower)
print(L,end='')
L = """You can modify sort behavior by passing in keyword arguments—a special “name=value” syntax in function calls that specifies passing by name and is often used for giving configuration options. In sorts, the key argument gives a one-argument function that returns the value to be used in sorting, and the reverse argument allows sorts
to be made in descending instead of ascending order""".split()
L.sort(key=str.lower, reverse = True)
print(L)
L = ['abc','ABD','aBe']
sorted(L, key = str.lower, reverse = True)
# [1,2,3,'spam'].sort() # '<' not supported between instances of 'str' and 'int'<jupyter_output><empty_output><jupyter_text>In Python 3.0, comparison of mixed types doesn't work.<jupyter_code>L = """You can modify sort behavior by passing in keyword arguments—a special “name=value” syntax in function calls that specifies passing by name and is often used for giving configuration options. In sorts, the key argument gives a one-argument function that returns the value to be used in sorting, and the reverse argument allows sorts
to be made in descending instead of ascending order""".split()
print(L,end=' ')
sorted(L,key = str.lower, reverse = True)  # Sorting built-in
print('\n')
print(L, end = ' ')
print('\n')
print(sorted([x.lower() for x in L], reverse = True), end = ' ')# Pretransform itemsL differs!
L = [1,2] 
L.extend([3,4,5]) # Add many items at end 
L
L.pop()  # Delete and return last items
L
L.reverse() # In-place reversal method
L
list(reversed(L))  # Reversal built-in with a result
L = [1,2,3]
L.extend("""Like strings, lists have other methods that perform other specialized operations. For
instance, reverse reverses the list in-place, and the extend and pop methods insert multiple items at the end of and delete an item from the end of the list, respectively.""".split())
print(L)<jupyter_output>[1, 2, 3, 'Like', 'strings,', 'lists', 'have', 'other', 'methods', 'that', 'perform', 'other', 'specialized', 'operations.', 'For', 'instance,', 'reverse', 'reverses', 'the', 'list', 'in-place,', 'and', 'the', 'extend', 'and', 'pop', 'methods', 'insert', 'multiple', 'items', 'at', 'the', 'end', 'of', 'and', 'delete', 'an', 'item', 'from', 'the', 'end', 'of', 'the', 'list,', 'respectively.']<jupyter_text>In some types of programs, the list pop method used here is often used in conjunction with append to implement a quick last-in-first-out (LIFO) stack structure. The end of the list severes as the top of the stack:<jupyter_code>L = [] # A list
L = """In some types of programs, the list pop method used here is often used in conjunction
with append to implement a quick last-in-first-out (LIFO) stack structure. The end of
the list serves as the top of the stack""".split()
print(L)
L1 = []
for i in L:
    L1.append(i)
print(L1)
for i in L1:
    print(L1.pop(),'=>', i , end = ' \t')
for i in range(len(L1)):
    print(L1.pop(), end = ' \t')
L.index('of')
L2 = """The pop method also accepts an optional offset of the item to be deleted and returned
(the default is the last item). Other list methods remove an item by value (remove), insert
an item at an offset (insert), search for an item’s offset (index), and more""".split()
len(L), len(L2)
print(L,' \n','\n', L2)
j = 1
for i in L:
        L2.insert(j, i)
        if j <= len(L):
            j = j + 2
print(L2, end = ' ')
k = 0
for i in L2:
    if k <= len(L):
        if k%2 != 0:
            print(L[int((k-1)/2)] == i, '->',k, end = '       \t')
    k = k + 1
len(L2), len(L)
print(L2, end =' ')
print(L2.count('of'))
print(L2.remove('of'))
print(L2.count('of'))
L2.count('of')
from numpy import *
for i in range(random.randint(4,100)):
    L2.insert(random.randint(len(L2)),'of')
print(L2)
a = 0
b = 0
c = 0
d = 0
e = 0
f = 0
for i in range(1000000):
    k = random.randint(1,7,1)
    if k == 1:
        a = a + 1
    elif k ==2:
        b = b + 1
    elif k ==3:
        c = c + 1
    elif k == 4:
        d = d + 1
    elif k == 5:
        e = e + 1
    elif k == 6:
        f = f + 1
print(a, b, c, d, e, f, a+b+c+d+e+f)
for j in range(1001):
    print(random.randint(1,6+1),end=' ')
    j = j +1<jupyter_output>2 1 2 2 6 4 3 2 1 4 1 1 5 5 2 2 2 1 1 6 4 1 1 1 4 6 3 2 4 1 1 5 3 2 1 2 4 1 2 5 2 4 4 1 4 3 5 4 2 4 4 3 6 3 3 2 3 1 2 4 4 5 2 5 3 2 1 5 1 2 5 6 2 6 5 6 6 3 3 2 1 5 5 5 3 6 3 1 4 6 2 5 6 3 5 3 3 2 6 2 3 3 1 5 3 6 4 5 5 4 2 3 4 3 4 4 5 5 2 2 3 5 1 2 1 1 1 5 6 5 6 2 5 3 6 5 2 5 1 4 3 6 6 6 2 1 5 2 2 4 5 3 6 2 5 5 3 3 3 1 2 6 6 1 4 4 6 2 4 2 3 6 6 6 6 2 5 4 6 1 2 1 1 5 4 5 4 4 1 6 1 4 2 6 5 3 5 1 3 3 1 3 4 4 2 1 5 5 5 4 2 6 6 1 6 3 4 6 1 1 6 3 5 2 4 1 5 3 5 1 3 5 3 3 1 5 2 6 4 1 2 6 5 2 1 5 5 3 4 5 2 2 5 4 4 5 6 5 6 2 4 5 1 5 4 6 1 1 2 2 5 2 1 4 4 3 5 3 5 4 3 1 1 6 1 2 6 2 5 5 6 2 3 1 1 5 6 5 6 2 6 3 6 3 6 6 5 4 3 6 5 5 6 1 2 6 1 5 5 1 3 3 1 6 4 1 6 1 2 5 4 3 1 4 6 5 2 6 3 4 4 6 5 2 5 4 6 2 1 6 2 6 1 6 3 3 5 2 3 4 5 6 1 4 5 1 6 3 2 4 2 6 3 3 4 3 3 4 4 5 5 5 5 2 3 3 1 2 1 2 3 6 4 3 1 6 1 1 4 5 2 6 3 1 1 1 5 2 4 5 5 1 2 1 4 4 6 6 5 4 4 2 5 5 6 4 6 2 6 5 1 1 4 1 2 6 6 4 1 5 6 1 5 2 2 1 3 1 2 5 2 5 6 5 6 4 1 6 1 3 4 2 1 6 1 4 3 3 2 4 6 6 4 6 4 2 6 6 6 6 3 6 5 5 5 4 4 3 3 5 3 2 5 6 6 1 4 5 4 5 [...]<jupyter_text>Other common list operations<jupyter_code>L = """Because lists are mutable, you can use the del statement to delete an item or section
in-place""".split()
print(L)
del L[0]  # Delete one item
print(L)
del L[1:] # Delete an entire section
L
L = """Because slice assignment is a deletion plus an insertion, you can also delete a section
of a list by assigning an empty list to a slice (L[i:j]=[]); Python deletes the slice named
on the left, and then inserts nothing. Assigning an empty list to an index, on the other
hand, just stores a reference to the empty list in the specified slot, rather than deleting
it:""".split()
print(L, end = ' ')
L[1:] = []
print(L)
L[0] = []
print(L)
len(L)<jupyter_output><empty_output><jupyter_text>Because slice assignment is a deletion plus an insertion, you can use it to delete some items in a list. That is, L[i:j] = [] deletes all the items from the index i to j, not inlcuding j. However, if you assign an empty list to an index, you get a reference to the empty list. rather than delete it.    Dictionaries The main difference between lists and dictionaries is that lists are ordered collections of objects while dictionaries are unordered ones. And most importantly, items in dictionaries are fetched by key instead of by positional offset. Some characteristics about Python dictionaries: 1. Accessed by key, not offset:Dictionaries are sometimes called associative arrays or hashes. A set of values are associtated with keys.2. Unordered collections of arbitary objects:<jupyter_code>L2 = """Apart from lists, dictionaries are perhaps the most flexible built-in data type in Python.
If you think of lists as ordered collections of objects, you can think of dictionaries as
unordered collections; the chief distinction is that in dictionaries, items are stored and
fetched by key, instead of by positional offset.""".split()
a = id(L2[0])
print(type(a))
for i in L2:
    print(id(i),'=>',i, end='    \t')
    print(a<=id(i))
    a = id(i)<jupyter_output><class 'int'>
2869700454832 => Apart    	True
2869700454960 => from    	True
2869700454704 => lists,    	False
2869700455088 => dictionaries    	True
2869700454128 => are    	False
2869700452720 => perhaps    	False
2869700454256 => the    	True
2869700453552 => most    	False
2869700453680 => flexible    	True
2869700454000 => built-in    	True
2869700453744 => data    	False
2869700454320 => type    	True
2869700453232 => in    	False
2869700453616 => Python.    	True
2869700453424 => If    	False
2869700453360 => you    	False
2869700453296 => think    	False
2869700453488 => of    	True
2869700452592 => lists    	False
2869700453040 => as    	True
2869700326384 => ordered    	False
2869700327344 => collections    	True
2869700327216 => of    	False
2869700327600 => objects,    	True
2869700327536 => you    	False
2869700327152 => can    	False
2869700326960 => think    	False
2869700327088 => of    	True
2869700326256 => dictionaries    	False
2869700326448 => as    	True
286970032[...]<jupyter_text>Actually, if the lists are not sorted in order, items are not stored in order.<jupyter_code>L2.sort()
a = id(L2[0])
print(type(a))
for i in L2:
    print(id(i),'=>',i, end='    \t')
    print(a<=id(i))
    a = id(i)
S = 'jfafasdjfjsklfalkflkdafklasfklasdfkljasdklfasdklfasdkl'
print(id(S))
S = 154444845544843454864645
print(id(S))
print(id('jfafasdjfjsklfalkflkdafklasfklasdfkljasdklfasdklfasdkl'))
print(id(154444845544843454864645))
print(L2, end = ' ')
len(L2)
Dict1 = {}
for i in range(len(L2)):
    Dict1[str(i)] = L2[i]
print(Dict1)
id('50')<jupyter_output><empty_output><jupyter_text>Unordered collections of arbitrary objects Unlike in a list, items stored in a dictionary aren’t kept in any particular order; infact, Python randomizes their left-to-right order to provide quick lookup. Keysprovide the symbolic (not physical) locations of items in a dictionary.<jupyter_code>Dict1.values()
Dict1.pop('49')
print(Dict1, end = ' ')
print(list(map(L2.count,L2)), end = ' ')
print(L2)<jupyter_output>[1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 2, 2, 1, 1, 1, 1, 1, 1, 1, 1, 4, 4, 4, 4, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 1, 1, 2, 2] ['Apart', 'KDA', 'If', 'Python.', 'and', 'are', 'are', 'as', 'as', 'built-in', 'by', 'by', 'can', 'chief', 'collections', 'collections;', 'data', 'dictionaries', 'dictionaries', 'dictionaries,', 'distinction', 'fetched', 'flexible', 'from', 'in', 'in', 'instead', 'is', 'items', 'key,', 'lists', 'lists,', 'most', 'objects,', 'of', 'of', 'of', 'of', 'offset.', 'ordered', 'perhaps', 'positional', 'stored', 'that', 'the', 'the', 'think', 'think', 'type', 'unordered', 'you', 'you']<jupyter_text>Of thge category "mutable mapping" Dictionaries can be changed in-place by assigning to keys (not indexes. Sequence operations do not work on Python dictionaries because they are unordered clollections.<jupyter_code># Dict1['0': '26'] = {} unhashable type: 'slice'<jupyter_output><empty_output><jupyter_text>Tables of object references (hash tables) Dictionaries are unordered tables of object references that support access by key. Dictionaries are sort of data structures which start small and grow on demand.  Table 8-2. Common dictionary literals and operations<jupyter_code>D = {}    # Empty dictionary
print(len(D))
D = {'spam':2, 'eggs': 3} # Two-item dictionary
print(len(D))
D = {'food': {'ham': 1, 'egg': 2}} # Nesting
print(len(D))<jupyter_output>0
2
1<jupyter_text>One-item Nesting dictionary is one-item dictionary<jupyter_code>D = dict(name='Bob',age=40)  # Altenative contruction techniques
len(D)
print(Dict1, end = ' ')
print(tuple(Dict1.keys()), end = ' ')
D = dict(zip(tuple(Dict1.keys()), tuple(Dict1.values())))
print(D, end = ' ')
D = dict.fromkeys(['a','b'])
D
D1 = dict.fromkeys(list(map(str,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20])))
print(D1,end =' ')
list(map(str,[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]))
L = []
for i in range(101):
    L.append(i)
del L[0]
print(L, end = ' ')

D= dict.fromkeys(list(map(str,L)))
print(D, end = ' ')
L2 = """The various types of physical forces described so far are fairly easy to understand, since they arise from straightforward electrostatic interactions involving charged or dipolar molecules. But there is a another type of force that—like the gravitational force—acts between all atoms and molecules, even totally neutral ones such as helium, carbon dioxide, and hydrocarbons. These forces have been variously known as dispersion forces, London forces, charge-fluctuation forces, electrodynamic forces, and induceddipole-induced-dipole forces. We shall refer to them as dispersion forces, since it is by this name that they are most widely known. The origin of this name has to do with their relation to the dispersion of light in the visible and UV regions of the spectrum, as we shall see.""".split()
len(L2)
L2[100:] = []
print(len(L2),'\n', L2)
for i in range(100):
    i = i + 1
    D[str(i)] = L2.pop()
print(D,end = ' ')
from numpy import *
type(random.randint(1,10))
D[str(random.randint(1,100))] # indexing key
'19' in D  # Membership: key present test
D.keys() # Methods: keys
D.values() # values
D.items()
D1 = D.copy() # copies
for key in D1.keys():
    if D1.get(key) == 'atoms':
        print(key)
D.get('2') # Return the value for key if key is in the dictionary, else default.
D.get('101')
D2 = {}
D2.update(D1) # """D.update([E, ]**F) -> None.  Update D from dict/iterable E and F. If E is present and has a .keys() method, then does:  for k in E: D[k] = E[k] If E is present and lacks a .keys() method, then does:  for k, v in E: D[k] = v In either case, this is followed by: for k in F:  D[k] = F[k]"""
print(D2, end =' ')
D2.update?
D = {x : x**2 for x in range(100)} # Dictionary comprehensions (Python 3.0)
print(D)
file_handle=open('1.txt',mode='w')
import cgi
form = cgi.FieldStorage()
if 'name' in form:
    showReply('Hello,' + form['name'].value)
import dbm
file = dbm.open('1','c')
for keys in D1.keys():
    file[keys] = D1[keys]
file.close()
Di = {'1': 'do', '2': 'to', '3': 'has', '4': 'name', '5': 'this', '6': 'of', '7': 'origin', '8': 'The', '9': 'known.', '10': 'widely', '11': 'most', '12': 'are', '13': 'they', '14': 'that', '15': 'name', '16': 'this', '17': 'by', '18': 'is', '19': 'it', '20': 'since', '21': 'forces,', '22': 'dispersion', '23': 'as', '24': 'them', '25': 'to', '26': 'refer', '27': 'shall', '28': 'We', '29': 'forces.', '30': 'induceddipole-induced-dipole', '31': 'and', '32': 'forces,', '33': 'electrodynamic', '34': 'forces,', '35': 'charge-fluctuation', '36': 'forces,', '37': 'London', '38': 'forces,', '39': 'dispersion', '40': 'as', '41': 'known', '42': 'variously', '43': 'been', '44': 'have', '45': 'forces', '46': 'These', '47': 'hydrocarbons.', '48': 'and', '49': 'dioxide,', '50': 'carbon', '51': 'helium,', '52': 'as', '53': 'such', '54': 'ones', '55': 'neutral', '56': 'totally', '57': 'even', '58': 'molecules,', '59': 'and', '60': 'atoms', '61': 'all', '62': 'between', '63': 'force—acts', '64': 'gravitational', '65': 'the', '66': 'that—like', '67': 'force', '68': 'of', '69': 'type', '70': 'another', '71': 'a', '72': 'is', '73': 'there', '74': 'But', '75': 'molecules.', '76': 'dipolar', '77': 'or', '78': 'charged', '79': 'involving', '80': 'interactions', '81': 'electrostatic', '82': 'straightforward', '83': 'from', '84': 'arise', '85': 'they', '86': 'since', '87': 'understand,', '88': 'to', '89': 'easy', '90': 'fairly', '91': 'are', '92': 'far', '93': 'so', '94': 'described', '95': 'forces', '96': 'physical', '97': 'of', '98': 'types', '99': 'various', '100': 'The'} 
Di.pop('99') # Delete items using pop,etc
len(Di)
Di['99'] = 'various' # Adding/changing keys
print(Di, end = ' ')
Di is {'1': 'do', '2': 'to', '3': 'has', '4': 'name', '5': 'this', '6': 'of', '7': 'origin', '8': 'The', '9': 'known.', '10': 'widely', '11': 'most', '12': 'are', '13': 'they', '14': 'that', '15': 'name', '16': 'this', '17': 'by', '18': 'is', '19': 'it', '20': 'since', '21': 'forces,', '22': 'dispersion', '23': 'as', '24': 'them', '25': 'to', '26': 'refer', '27': 'shall', '28': 'We', '29': 'forces.', '30': 'induceddipole-induced-dipole', '31': 'and', '32': 'forces,', '33': 'electrodynamic', '34': 'forces,', '35': 'charge-fluctuation', '36': 'forces,', '37': 'London', '38': 'forces,', '39': 'dispersion', '40': 'as', '41': 'known', '42': 'variously', '43': 'been', '44': 'have', '45': 'forces', '46': 'These', '47': 'hydrocarbons.', '48': 'and', '49': 'dioxide,', '50': 'carbon', '51': 'helium,', '52': 'as', '53': 'such', '54': 'ones', '55': 'neutral', '56': 'totally', '57': 'even', '58': 'molecules,', '59': 'and', '60': 'atoms', '61': 'all', '62': 'between', '63': 'force—acts', '64': 'gravitational', '65': 'the', '66': 'that—like', '67': 'force', '68': 'of', '69': 'type', '70': 'another', '71': 'a', '72': 'is', '73': 'there', '74': 'But', '75': 'molecules.', '76': 'dipolar', '77': 'or', '78': 'charged', '79': 'involving', '80': 'interactions', '81': 'electrostatic', '82': 'straightforward', '83': 'from', '84': 'arise', '85': 'they', '86': 'since', '87': 'understand,', '88': 'to', '89': 'easy', '90': 'fairly', '91': 'are', '92': 'far', '93': 'so', '94': 'described', '95': 'forces', '96': 'physical', '97': 'of', '98': 'types', '99': 'various', '100': 'The'} 
Di == {'1': 'do', '2': 'to', '3': 'has', '4': 'name', '5': 'this', '6': 'of', '7': 'origin', '8': 'The', '9': 'known.', '10': 'widely', '11': 'most', '12': 'are', '13': 'they', '14': 'that', '15': 'name', '16': 'this', '17': 'by', '18': 'is', '19': 'it', '20': 'since', '21': 'forces,', '22': 'dispersion', '23': 'as', '24': 'them', '25': 'to', '26': 'refer', '27': 'shall', '28': 'We', '29': 'forces.', '30': 'induceddipole-induced-dipole', '31': 'and', '32': 'forces,', '33': 'electrodynamic', '34': 'forces,', '35': 'charge-fluctuation', '36': 'forces,', '37': 'London', '38': 'forces,', '39': 'dispersion', '40': 'as', '41': 'known', '42': 'variously', '43': 'been', '44': 'have', '45': 'forces', '46': 'These', '47': 'hydrocarbons.', '48': 'and', '49': 'dioxide,', '50': 'carbon', '51': 'helium,', '52': 'as', '53': 'such', '54': 'ones', '55': 'neutral', '56': 'totally', '57': 'even', '58': 'molecules,', '59': 'and', '60': 'atoms', '61': 'all', '62': 'between', '63': 'force—acts', '64': 'gravitational', '65': 'the', '66': 'that—like', '67': 'force', '68': 'of', '69': 'type', '70': 'another', '71': 'a', '72': 'is', '73': 'there', '74': 'But', '75': 'molecules.', '76': 'dipolar', '77': 'or', '78': 'charged', '79': 'involving', '80': 'interactions', '81': 'electrostatic', '82': 'straightforward', '83': 'from', '84': 'arise', '85': 'they', '86': 'since', '87': 'understand,', '88': 'to', '89': 'easy', '90': 'fairly', '91': 'are', '92': 'far', '93': 'so', '94': 'described', '95': 'forces', '96': 'physical', '97': 'of', '98': 'types', '99': 'various', '100': 'The'} 
del Di['99']
print(list(Di.keys()), end = '')
D1 = Di.copy()
D2 = Di.copy()
print(D1.keys() & D2.keys(), end =' ')
Set = (D1.keys() & D2.keys())
L = list(D1.keys() & D2.keys())
L.sort()
print(L)<jupyter_output>['1', '10', '100', '11', '12', '13', '14', '15', '16', '17', '18', '19', '2', '20', '21', '22', '23', '24', '25', '26', '27', '28', '29', '3', '30', '31', '32', '33', '34', '35', '36', '37', '38', '39', '4', '40', '41', '42', '43', '44', '45', '46', '47', '48', '49', '5', '50', '51', '52', '53', '54', '55', '56', '57', '58', '59', '6', '60', '61', '62', '63', '64', '65', '66', '67', '68', '69', '7', '70', '71', '72', '73', '74', '75', '76', '77', '78', '79', '8', '80', '81', '82', '83', '84', '85', '86', '87', '88', '89', '9', '90', '91', '92', '93', '94', '95', '96', '97', '98']<jupyter_text>Lists and dictionaries are grown in different ways, though. You won't see dictionaries constructed using literals. As a matter of the fact, dictionaries are grown at runtime and in-place. You just need to assign new values to the new key. On the other hand, lists are commonly grown with append instead.  Dictionaries in Action Basic Dictionary Operations<jupyter_code>D = {'spam': 2, 'ham':1, 'eggs':3} # Make a dictionary
D['spam']
D # Order 
D<jupyter_output><empty_output><jupyter_text>Changing Dictionaries In-Place<jupyter_code>D['ham'] = ['grill','bake','fry']
D
del D['eggs']
D
D['brunch'] = 'Bacon' # Add new entry
D
D = {'spam':2,'ham':1,'eggs':3}
list(D.values())
len(list(D.items()))
type(list(D.items()))
D2 = dict.fromkeys(list(D.items()))
print(D2)
D3 = {x: x for x in list(D.items())}
print(D3)
D.get('spam')
print(D.get('toast'))
D.get('toast',88) # set default to 88, if 'toast' doesn't exist in a dictionary, return 88.
print(D, end = ' ')<jupyter_output>{'spam': 2, 'ham': 1, 'eggs': 3}<jupyter_text>The update method can be used for concatenation for dictionaries (but has nothing to do with left-to-right ordering)<jupyter_code>D1 = {'toast': 4, 'muffin':5}
D.update(D1)
print(D)
# pop a dictionary by key
D
D.pop('muffin')
D.pop('toast')
# pop a list by position
L = ['aa','bb','cc','dd'] 
L.pop()  # delete and return from the end
L
L.pop(1)
L<jupyter_output><empty_output><jupyter_text>A Languages Table<jupyter_code>table = {"Python": '<NAME>',
        'Perl': '<NAME>',
        'Tcl': '<NAME>'}
language = 'Python'
creator = table[language]
creator
for lang in table:
    print(lang, '\t', table[lang])  # Same as: for lang in table.keys()<jupyter_output>Python 	 <NAME>
Perl 	 <NAME>
Tcl 	 <NAME><jupyter_text>Dictionary Usage Notes 1. Sequence operation don't workDictionaries are mappings, not sequences; there is no notion of ordering among their items. 2. Assiging to new indexes adds entriesKeys can be created when you write a dictionary literal.3. Keys need not always be strings.You can use any immutable objects as keys. Tuples are sometimes used as dictonary keys too, allowing for compound key values. You can use dictionary as dictonary keys, too.  Using dictionaries to simulate flexible lists<jupyter_code>L = []
# L[99] = 'spam' # list assignment index out of range
D = {}
D[99] = 'spam'
D[99]
D<jupyter_output><empty_output><jupyter_text>Using dictionaries for sparse data structures<jupyter_code>Matrix = {}
Matrix[(2,3,4)] = 88
Matrix[(7,8,9)] = 99
X = 2 ; Y = 3; Z = 4 # ; separates statements
Matrix[(X,Y,Z)]
Matrix<jupyter_output><empty_output><jupyter_text>Dictionary comprehensions<jupyter_code>list(zip(['a','b','c'],[1,2,3])) # Zip together keys and values
D = dict(zip(['a','b','c'],[1,2,3])) # Make a dict from zip result
D 
D = {k: v for (k,v) in zip(['a','b','c'],[1,2,3])}
D
L1 = """Comprehensions actually require more code in this case, but they are also more general
than this example implies—we can use them to map a single stream of values to dictionaries as well, and keys can be computed with expressions just like values""".split()
print(L1, end = ' ')
L = []
for i in range(100):
    L.append(str(i))
print(L, end =' ')
L3 = ['0', '1', '1', '2', '2', '3', '3', '4', '4', '5', '5', '6', '6', '7', '7', '8', '8', '9', '9', '10', '10', '11', '11', '12', '12', '13', '13', '14', '14', '15', '15', '16', '16', '17', '17', '18', '18', '19', '19', '20', '20', '21', '21', '22', '22', '23', '23', '24', '24', '25', '25', '26', '26', '27', '27', '28', '28', '29', '29', '30', '30', '31', '31', '32', '32', '33', '33', '34', '34', '35', '35', '36', '36', '37', '37', '38', '38', '39', '39', '40', '40', '41', '41', '42', '42', '43', '43', '44', '44', '45', '45', '46', '46', '47', '47', '48', '48', '49', '49', '50'] 
for i in range(50):
    L3[i]=L[i:i+2]
print(L3, end = ' ')
L3.index(['49','50'])
L3 = L3[0:50]
print(L3, end = ' ')
print(L1, end = ' ')
len(L1)
L3 = L3[0:42]
# D = dict(zip(L3,L1)) # unhashable type: 'list' 
D = {x: x**2 for x in [1,2,3,4]} # Or: range(1,5)
D
D = {c: c*4 for c in 'SPAM'} # Loop over any iterable
D
D = {c.lower(): c + '!' for c in ['SPAM',"EGGS",'HAM']}
D<jupyter_output><empty_output><jupyter_text>Dictionary comprehensions are also useful for initializing dictionaries from keys lists, in much the same way as the fromkeys method we met at the end of the preceding section.<jupyter_code>D = dict.fromkeys(['a','b','c'], 0) # Initialize dict from keys
D
D = {k:0 for k in ['a','b','c']} # Same, but with a coomprehension
D = dict.fromkeys('spam') # Other iterators, default value
D
D = {k: None for k in 'spam'}
D
D = dict(a=1, b =2, c=3)
D
K = D.keys() # Makes a view object in 3.0, not a list
K
list(K)
V = D.values() # Ditto for values and items views
V
list(V)
list(D.items())
# K[0] # List operations fail unless converted # 'dict_keys' object is not subscriptable
list(K)[0]
for k in D.keys(): print(k) # Iterators used automatically in loops
for key in D:
    print(key)<jupyter_output>a
b
c<jupyter_text>Unlike 2.X’s list results, though, dictionary views in 3.0 are not carved in stone when created—they dynamically reflect future changes made to the dictionary after the view object has been created.<jupyter_code>D = {'a': 1, 'c': 3, 'b':2}
D
print(list(D.keys()))
print(list(D.values())) # views maintain same order as dictionary
del D['b'] # Change the dictionary in=place
D<jupyter_output><empty_output><jupyter_text>Dictionary views and sets<jupyter_code>D.keys() | {'x': 4} # Keys (and some items) views are set-like
 # D.values() & {'x': 4} unsupported operand type(s) for &: 'dict_values' and 'dict'
D = {'a': 11, 'b': 2, 'c': 3}
D.keys() & D.keys()   # Intersect keys views
D.keys() & {'b'}  # Intersect keys and sets
D.keys() & {'b': 1} # Intersect keys and dict
D.keys() | {'b', 'c', 'd'}  # Union keys and set<jupyter_output><empty_output><jupyter_text>Dictionary items views are set-like too if they are hashable—that is, if they contain only immutable objects<jupyter_code>D = {'a': 1}
list(D.items()) # Items set-like if hashable
D.items() | D.keys() # Union view and view
D.items() | D # dict treated same as its keys
D.items() | {('c', 3), ('d',4)} # Set of key/value pairs
dict(D.items() | {('c',3),('d', 4)}) # dict accepts iterable sets too<jupyter_output><empty_output><jupyter_text>Sorting dictionary keys<jupyter_code>D = {'a':1, 'b': 2, 'c': 3}
D
Ks = D.keys()  # Sorting a view object does't work
Ks = list(Ks) # Force it to be a list and then sort
Ks.sort()
for k in Ks:
    print(k, D[k])
D
Ks = D.keys()   # Or you can use sorted() on the keys
for k in sorted(Ks): print(k,D[k]) # sorted() accepts any iterable
D
for k in sorted(D): print(k, D[k])<jupyter_output>a 1
b 2
c 3<jupyter_text>The has_key method is dead: long live in?<jupyter_code>D
'c' in D
'x' in D
if 'c' in D: print('present', D['c']) # Perferred in 3.0
print(D.get('c'))
print(D.get('x'))
if D.get('c') !=None: print('present', D['c']) # Another option<jupyter_output>present 3<jupyter_text>Chapter Summary In this chapter, we explored the list and dictionary types—probably the two most common, flexible, and powerful collection types you will see and use in Python code. We learned that the list type supports positionally ordered collections of arbitrary objects, and that it may be freely nested and grown and shrunk on demand. The dictionary type is similar, but it stores items by key instead of by position and does not maintain any reliable left-to-right order among its items. Both lists and dictionaries are mutable, and so support a variety of in-place change operations not available for strings: for example, lists can be grown by append calls, and dictionaries by assignment to new keys  Test Your Knowledge: Quiz 1. Name two ways to build a list containing five integer zeros<jupyter_code>L = []
for i in range(5):
    L.append(0)
L
L1 = []
for i in range(5):
    L1.insert(i,0)
L1
[0]*5
[0,0,0,0,0]<jupyter_output><empty_output><jupyter_text>2. Name two ways to build a dictionary with two keys, 'a' and 'b', each having anassociated value of 0.<jupyter_code>D = {}
D.fromkeys(['a','b'],0)
D1 = {}
D1 = dict(a=0,b=0)
D1
D2 = dict([('a',0),('b',0)])
dict.fromkeys('ab',0)<jupyter_output><empty_output><jupyter_text>3. Name four operations that change a list object in-place<jupyter_code>L1, L
L1.clear(), L<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.18699841715031518, 'fraction_numerical': 0.07253339257449112, 'mean_word_length': 4.430855074649374, 'pattern_counts': {'":': 2, '<': 142, '<?xml version=': 0, '>': 180, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 85, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '35', 'dir': 'jupyter-structured-clean-dedup', 'id': '626862', 'license': 'MIT', 'n_tokens_mistral': 15044, 'n_tokens_neox': 13296, 'n_words': 5013, 'path': 'Ch8 Lists and Dictionaries.ipynb', 'repo_name': 'nickcafferry/Learning-Python-4th-Edition', 'size': '36057'}