<jupyter_start><jupyter_text>Python Fundamentals Review This notebook is based off of a cursory walkthrough of Python basics found here: https://www.stavros.io/tutorials/python/I've added some description and explanation, rephrased comentary, and adjusted naming conventions for my own learning process and for later reference.  Getting Help What Help in Python? The language has built in methods for accessing documentation and detailed properties of any object.<jupyter_code># help(<object>) gives an explanation of how the object works
help(5)
# dir() shows you the object's methods
dir(5)
# <object>.__doc__ shows you it's documentation string
abs.__doc__<jupyter_output><empty_output><jupyter_text>Syntax<jupyter_code># variable assignment & incrementing
new_var = 7
new_var += 3
new_var
# and then decrementing the same object
new_var -= 1
new_var
# Here's a Single Line Comment!!! I won't run!
"""And Here is a multi-line comment!
These are useful for creating docstrings for new functions
They can also be a safe format to import unknown text into,
in case you don't know exactly what kind of punctuation it 
will contain."""

# Next let's do some concatenation...
new_string = 'Hello'
new_string += ' world!'
print(new_string)
# This will will swap the values of the objects and
# reassign them to the previous names
new_var, new_string = new_string, new_var<jupyter_output><empty_output><jupyter_text>Data Types **Included Data Structures:*** lists --> One-dimensional arrays which can contain other objects* tuples --> imutable one-dimensional arrays* dictionaries --> Associatve arrays, or Hash tablesSets function like lists which will only contain unordered, imutable, unique elements.<jupyter_code># a list can hold other objects of different types
sample = [1, ['another', 'list'], ('a', 'tuple')]
mylist = ['List item 1', 2, 3.14]
mylist[0] = 'List item 1 again' # this will change the item
mylist[-1] = 3.21 # This refers to the last item

mydict = {'Key 1': 'Value 1', 2: 3, 'pi': 3.14}
mydict['pi'] = 3.15 # Here's how you change dict values

mytuple =(1, 2, 3)
myfunction = len # let's check the length of the dict 'mylist'

print(myfunction(mylist))
# Here's some examples of methods to access data in arrays

mylist = ['list item 1', 2, 3.14]
print(mylist[:])
print(mylist[0:2])
print(mylist[-3:-1])
print(mylist[1:])
# Using a third parameter will "step" will make python step
# in N increments rather than just 1
print(mylist[::2])<jupyter_output>['list item 1', 3.14]<jupyter_text>Strings<jupyter_code># print("Name: %s\

strString = """This is
a multiline
string."""

# WARNING: Watch out for the trailing s in "%(key)s".
print("This %(verb)s a %(noun)s." %{"noun": "test", "verb": "is"})


name = "Stavros"
# "Hello, {}!".format(name)

# F strings are the typical way to insert functions or data inside of strings
print(f'Hello, {name}!')<jupyter_output>This is a test.
Hello, Stavros!<jupyter_text>Flow Control Statements<jupyter_code>print(range(10))
rangelist = list(range(10))
print(rangelist)
for number in range(10):
    # Check if number is one of
    # the numbers in the tuple.
    if number in (3, 4, 7, 9):
        # "break" terminates a for with executing the "else" clause
        break
    else:
        continue # starts the next iteration of the loop
else:
    # optional clause can be used in case the loop doesn't "break"
    pass # do nothing
if rangelist[1] == 2:
    print('the second item (lists are 0-based) is 2')
elif rangelist[1] == 3:
    print('The second item (lists are 0-based) is 3')
else:
    print('No idea...')
# Here's an inifinite "while" loop

# while rangelist[1] == 1:
#     print('We are trapped in an infinite loop!')<jupyter_output><empty_output><jupyter_text>Functions<jupyter_code># Same as def funcvar(x): return x + 1
funcvar = lambda x: x + 1
print(funcvar(1))
# an_int and a_string are optional, they have default values
# if one is not passed (2 and "A default string", respectively).
def passing_example(a_list, an_int=2, a_string="A default string"):
    a_list.append('A new item')
    an_int = 4
    return a_list, an_int, a_string

my_list = [1, 2, 3]
my_int = 10
print(passing_example(my_list, my_int))
my_list
my_int<jupyter_output><empty_output><jupyter_text>Classes<jupyter_code>#This works similarly to a constructor in JavaScript
class MyClass(object):
    common = 10
    def __init__(self):
        self.my_variable = 3
    def my_function(self, arg1, arg2):
        return self.my_variable
    
# This is the class instantiation
class_instance = MyClass()
class_instance.my_function(1, 2)
# This variable is shared by all instances
class_instance_2 = MyClass()
class_instance.common
class_instance_2.common
# Note how we use the class name instead of the instance
MyClass.common = 30
#Then check the updated values of the instances
class_instance.common
class_instance_2.common
# This will not update the variable on the class,
# instead it will bind a new object to the old variable name
class_instance.common = 10
class_instance.common
class_instance_2.common<jupyter_output><empty_output><jupyter_text>**I can change the "Common" variable in our class:**<jupyter_code>MyClass.common = 50
# But this has not been changed because
# it is now an "instance" variable
class_instance.common
# However this is still a "class" variable
class_instance_2.common<jupyter_output><empty_output><jupyter_text>**Multiple Inheritance**:<jupyter_code># 
class OtherClass(MyClass):
    # "self" in a python class refers to the class instance
    # This is similar to contextual "this" in JavaScript
    def __init__(self, arg1):
        self.my_variable = 3
        print(arg1)

class_instance = OtherClass('hello')
class_instance.my_function(1, 2)
class_instance.test = 10
class_instance.test<jupyter_output><empty_output><jupyter_text>Exceptions Exceptions are handled with `try-except[exceptionname]` blocks:<jupyter_code>def some_function():
    try:
        # Division by zero raises an exception
        10 / 0
    except ZeroDivisionError:
        print('Oops, invalid.')
    else:
        # Exception didn't occur, we're good.
        pass
    finally:
        # "finally" is executed after the code block runs
        # and all exceptions are handled, including 
        # any new exceptions that are raised in the process.
        print("We're done with that.")

some_function()<jupyter_output>Oops, invalid.
We're done with that.<jupyter_text>Importing Import external libraries and functions from libraries using the `import [libname]` and `from [libname] import [func_name]` keywords.<jupyter_code>import random
from time import clock

random_int = random.randint(1, 100)
print(random_int)<jupyter_output>77<jupyter_text>File I/O Python includes a variety of built in libraries. Here's an example of serializing(converting data structures to strings with the `pickle` library) with file I/O:<jupyter_code>import pickle
my_list = ['this', 'is', 4, 13327]
# open the file C:\\binary.dat for writing. 
# The letter r before thje filename string 
# is used to prevent backslash escaping.
my_file = open(r'C:\\binary.dat', 'wb')
pickle.dump(my_list, my_file)
my_file.close()

my_file = open(r"C:\\text.txt", "w")
my_file.write("This is a sample string")
my_file.close()

my_file = open(r"C:\\text.txt")
print(my_file.read())

my_file.close()
# Open the file for reading.
my_file = open(r"C:\\binary.dat", "rb")
loaded_list = pickle.load(my_file)
my_file.close()
print(loaded_list)<jupyter_output>['this', 'is', 4, 13327]<jupyter_text>Miscellaneous * Conditions can be chained together: `1 < a < 3` checks that a is both less than 3 and greater than 1* use `del` to delete variables or items in arrays* **List comprehensions** allow you to create and manipulate lists:<jupyter_code>list1 = [1, 2, 3]
list2 = [3, 4, 5]
print([x * y for x in list1 for y in list2])
print([x for x in list1 if 4 > x > 1])
# Check if a condition is true for any items.
# "any" returns true if any item in the list is true.
any([i % 3 for i in [3, 3, 4, 4, 3]])

# Because 4 % 3 = 1, and 1 is true, so any() returns True
# Check for how many items a condition is true
sum(1 for i in [3, 3, 4, 4, 3] if i == 4)
del list1[0]
print(list1)
del list1<jupyter_output><empty_output><jupyter_text>**Global Variables** must be declared at the beginning of a function with the `global` keyword. In absense of this, Python will bind the object to a new local variable.<jupyter_code>number = 5

def my_fund():
    # This will print 5.
    print(number)

def another_func():
    # This will raise an exception...
    print(number)
    number = 3
    # because the variable has not been bound before printing
    # Python knows the variable will be bound later and
    # creates a new local object instead of using the global

def yet_another_func():
    global number
    # This will correctly access the global:
    number = 3

yet_another_func()
print(number)<jupyter_output>3
{'fraction_non_alphanumeric': 0.10361823680967851, 'fraction_numerical': 0.018595272768007168, 'mean_word_length': 4.545341614906833, 'pattern_counts': {'":': 2, '<': 55, '<?xml version=': 0, '>': 58, 'https://': 1, 'lorem ipsum': 0, 'www.': 1, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '14', 'dir': 'jupyter-structured-clean-dedup', 'id': '658899', 'license': 'MIT', 'n_tokens_mistral': 2978, 'n_tokens_neox': 2835, 'n_words': 1077, 'path': 'python_fundamentals_review.ipynb', 'repo_name': 'JSilvas/fundamentals', 'size': '8927'}