# ---
# jupyter:
#   jupytext:
#     text_representation:
#       extension: .py
#       format_name: light
#       format_version: '1.5'
#       jupytext_version: 1.14.4
#   kernelspec:
#     display_name: Python 3 (ipykernel)
#     language: python
#     name: python3
# ---

# Python objects are either scalar-> indivisible or non-scalar-> have inernal structure.
# Python contains the following scalars: int, float, bool, None
# type() functions returns object's type

a_number = 9.99
print(type(a_number))

# Arithmetic operations Have usual precedence. Use () to + precedence.
# +,-,*,/ will return a float if one of the operands if a float. Else, return ints.
# //: floor division ignores remainder.
# %: modulo, returns remainder
# a**b: raise a to power b

# +
a_whole_number = 100
print(a_number + a_whole_number)
print(a_whole_number/a_number)



# +
#I am a comment
# -

# input() function used to request user input

your_name = input()
print(your_name)

# len() function returns object's size

# int(), float(), str() functions can be used to typecast

a_number = "999"
a_new_number = int(a_number)
print(type(a_number))
print(type(a_new_number))



# Conditonals & branching

def a_function(x):
    if x%2 == 0:
        if x%3 == 0:
            print(x,"is divisible by 2 and 3")
        else:
            print(x,"is divisible by 2 but not by 3")
    elif x%3 == 0:
        print(x,"is divisible by 3 but not 2")


a_function(9)


# break stop the loop
# continue causes loop to return to top/beginning

# for loops

# +
#example forloops. Count up to value
def count_up_to(x):
    sum = 0
    for i in range(x + 1):
        sum = sum + i
    print(sum)

count_up_to(100)
# -

#range can be called with 3 arguments.
#first one is the start, second is the final number - 1 and last argument is the step
#will count in steps of 2 up to 8 since 10 -1 = 9
for i in range(0, 10, 2):
    print(i)

#range can also step backward by using negative argument
for i in range( 5, -1, -1):
    print(i)

#sys library can be used to terminate a program
import sys
while True:
    print("type exit to exit")
    response = input()
    if response == 'exit':
        sys.exit()

# +
#print() function automatically adds newline as the end of the string
#print takes optional parameters end and sep
#end can be used to disable endline and be able to print two different print statements on same line
print("hello", end = '')
print ("world")

#print() automatically separates prints with a space 
#spacing can be changed with sep argument
print("Hi", "potato","man", sep = " * ")

# +
#global statement must be used to be able to modify global variables within functions
#ex if we have a global variable named MAXNUMBER and we want to modify it within a function
#then inside the function we could write global MAXNUMBER
#this tells py to use the global variable instead of creating a new local variable by the same name
# -

# lists

#can access list of lists using list_name[][]
#First[] indicates which sublist to access and the second which element to access
a_list = [['cat', 'dog', 'lion'], ['gato', 'perro', 'leon']]
print(a_list[1][0])

#use negative index to get values at end of list
print(a_list[1][-1])

#slicing a list [start:end] excludes the end value
a_new_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
print(a_new_list[0:5])

#[:end] start from beginning of list
#[start:] slice until the end of the list
print(a_new_list[5:])

#reassign values using []
a_list[0][1] = "green"
print(a_list)

#lists can be concat with +
#lists can be replicated with *
print([1,2,3] + ['a', 'b', 'c'])
print(['solo'] * 6)

#del to delete from list
del a_list[0]

#loop through lists
for item in a_list:
    print(item)

#in and not can be used to check items on list
print(5 in a_new_list)
print(5 not in a_new_list)

#enumerate() function returns list with index along with item stored in index
for index, item in enumerate(a_new_list):
    print(index, item)

#random.choice() and random.shuffle() canbe used on lists
import random
print(random.choice(a_new_list))#return an item from the list at random
random.shuffle(a_new_list) #randomly reorder items in the list
print(a_new_list[0])

# #### List Methods

#index() returns index if value exists in the list
print(a_new_list.index(5))

#append() to the end of the list
#insert() at given index
a_new_list.append(11)
a_new_list.insert(5, 12)

#remove(), if multiple copies, only first will be removed
#remove() takes values as argument
#different from del
#del list[index]
a_new_list.remove(0)

#sort()
a_new_list.sort()
print(a_new_list)
#can also sort in reverse
a_new_list.sort(reverse = True)
print(a_new_list)

#sort() gives higher priority to caps over lowercase
#To sort everything in lowercase use
a_list = ['a', 'z', 'A', 'Z']
a_list.sort(key=str.lower)
print(a_list)

#reverse() order of values in list
a_new_list.reverse()
print(a_new_list)

#python strings are immutable
#to add to a string, need to slice and concat a new string
name = "<NAME>"
new_name = name[0:6] + "the" + name[7:]
print(new_name)

# ### tuples- immutable data type

a_tuple = ("hello", 5, "ayo")

#tuple values can not be modified, deleted or added
a_tuple[0]= 99 #causes an error

# +
#can cast to tuple or list using tuple() or list().
#similarly to casting using int(), str(), float()

alist = (list(a_tuple)) #cast tuple into list
print(type(alist))
print(alist)

# +
#lists and dicts are often passed by reference meaning
#list = new_list makes a copy by reference. Making changes to make changes the other

#copy() and deepcopy()
#id() gives memory address
import copy
newlist = copy.copy(alist) #same contents but different addresses
#therefore making changes to either list will not reflect on the other
print(id(alist))
print(id(newlist))

#deepcopy() is used for list of lists.
# -

# ### dictionaries

#dictionaries are mutable. Contain key/value pairs.
#instead of using index, we use keys to access the values
person = {"Name": "joe", "Age": 22, "Profession": "potato salesman"}
print(person["Name"], person["Profession"])

#items() returns key/value pairs
#values() returns values
#keys() returns keys
for value in person.values():
    print(value)
for key in person.keys():
    print(key)
for item in person.items():
    print(item)

#can use the values() and keys() along with list() to return values in a list
person_values_list = list(person.values())
person_keys_list = list(person.keys())
print(person_keys_list)

#items() to assign keys and values to different variables in a loop
for key, value in person.items():
    print(key, value)

#in not in can be used with dictionaries as well
print("joe" in person.values())
print("Isaac" not in person.values())
print("Name" in person)
print("Name" in person.keys())

#get() to check if keys exists in dict
#get() takes 2 arguments, values to search for and default return value if not found
#get() will return None by default if not found
print(person.get("What"), 0)
print(person.get("Name"))

#setdefault() can be used to add a new key/value pair?
person.setdefault("Income",66)
#if key already exists, then function returns the value mapped to the key
person.setdefault("Income", 99)

#interesting example maps frequencies of char from an input string
message = " It was a bright cold day in April, and the clocks were striking thirteen."
count = {}
for character in message:
    count.setdefault(character, 0) # set the default value of each character to 0
    count[character] = count[character] + 1
print(count)

#pprint provides pprint() and pformat() functions
#useful for formatting dictionary outputs
#pprint useful for nested dicts or lists
#pprint.pformat(dict) to prettify text as a string instead of printing to screen
import pprint
pprint.pprint(count)

a_var = pprint.pformat(count)

print(a_var)

#tic tac toe
theBoard = {'top-L': ' ', 'top-M': ' ', 'top-R': ' ',
            'mid-L': ' ', 'mid-M': ' ', 'mid-R': ' ',
            'low-L': ' ', 'low-M': ' ', 'low-R': ' '}


def printBoard(board):
    print(board['top-L'] + '|' + board['top-M'] + '|' + board['top-R'])
    print('-+-+-')
    print(board['mid-L'] + '|' + board['mid-M'] + '|' + board['mid-R'])
    print('-+-+-')
    print(board['low-L'] + '|' + board['low-M'] + '|' + board['low-R'])


turn = 'X'
for i in range(9):
    printBoard(theBoard)
    print('Turn for ' + turn + '. Move on which space?')
    move = input()
    theBoard[move] = turn
    if turn == 'X':
        turn = 'O'
    else:
        turn = 'X'
printBoard(theBoard)

#nested dicts and lists
all_guests = {'Alice': {'apples': 5}, 'pretzels': 12},
                'Bob': {'ham': 3, 'apples': 2},
                'Carol': {'cups': 3, 'more apples lol': 3}
def totalBrought(guests, item):
    numBrought = 0
    for k, v in guests.items():
        numBrought = numBrought = v.get(item,0)
    return numBrought #ugh,didn't finish this one


# ###string operations

# +
#scape characters
#\'
#\"
#\t
#\n
#\\

#raw strings ignore all scape characters
print(r'That is Carol\'s cat.')

#can also use """ """ or ''' ''' doesn't require scaping
""" this is a multiline comment
hahahha
"""
# -

#string can be indexed and sliced the same way as lists
a_string = "Hello, world!"
print(a_string[0:5])

#In and not in 
print("Hello" in a_string)

#string interpolation...use %s instead of converting parameters to string using str()
name = "isaac"
age = 99
"my name is %s. I am %s years old." %(name, age)

#f strings
f'my name is {name}. next year I will be {age + 1}.'

#string methods upper(), lower(), isupper(), islower()
a_string = a_string.upper()
print(a_string)
print(a_string.isupper())

# +
#other string methods
#isalpha() true if string contains only letters and isn't blank
#isalnum() true if string contains only letters and numbers and not blank
#isdecimal() true if consists of numeric values and not blank
#isspace() true if string consists only of spaces, tabs, and newlines, not blank
#islittle() true is string consists of words beginning with Upper letter followed by lowercase


# -

#startswith() endswith() returns true if string starts/end with sequence
print(a_string)
print(a_string.startswith("Hello"))
print(a_string.startswith("HELLO"))

#join() and split()
#join() is called on a string and passed a list a parameter
new_str = 'ABC'.join(['my', 'name', 'is', 'potato'])

#split() is called on a string and it returns a list of strings
'My name is potato'.split()

#split uses space to separate string
#can pass parameter to deparate strings using that instead of space
new_str.split('ABC')

# +
#ex 
import pprint
astr = """Dear Alice, 
How have you been? I am fine.
There is a container in the fridge
that is labeled 'milk experiment.'


Please do not drink it.
Sincerely,
Bob"""

pprint.pprint(astr.split('\n'))
# -

#partition() returns a string split at the given sequence
before, sep, after = 'Hello, world!'. partition('w') #saves the resulting strings in vars
print(before, sep, after)

#remove whitespace strip(), rstrip(), lstrip()
astring = "       Hello, world            "
astring.strip() #remove all white space at both ends
astring.rstrip() #remove white space from right
astring.lstrip() #remove white space from left

#Can also indicate which characters to strip at the ends
spam = 'spamspambaconspameggspam'
spam.strip('amps')

import pyperclip
pyperclip.copy('Hello, world!')
pyperclip.paste()



StarcoderdataJupyterScriptsDedupFiltered