--- title: Python description: draft: false tags: - python - programming author: TrudeEH showToc: true --- ## Python3 [Documentation](https://docs.python.org/3/) - `number`: Replace with a number. - `*object`: Any number of objects. - `[, ndigits]`: Anything between `[]` is optional. ## Run Python Code Python is an interpreted language. It is not compiled like C. ```Shell python hello.py ``` Skipping the compiling step makes the language easier to use but at the cost of performance. ## Print Print information on the console. ```Python print("hello, world") answer = "Some Text" print(f"Your answer is: {answer}") # formatted string (any type) print("Your answer is: " + answer) # answer must be string print("Your answer is:", answer) # answer can be any type print("Same line", end="") print("!" * 4) # "!!!! # '' and "" do the same. ``` ## Input Prompt the user for information. ```Python # String Input name = input("Prompt: ") number = int(input("Prompt: ")) name = name.strip() # Remove whitespaces from a string ``` ## Variables Python infers the type of variable automatically. ```Python answer = "Some Text" counter = 0 counter = counter + 1 counter += 1 # Counter++ does not exist in Python ``` ### Global Variables Use the global keyword to modify a global variable while outside its scope. ```Python balance = 0 def deposit(n): global balance balance += n ``` ## Comments ```Python # Comment """ Multi Line Comment """ ``` ### Docstring The documentation for a function. This can later be used to generate manuals and documentation Some IDEs do so automatically. ```Python def code(): """ Does something. """ ... ``` ### Type Hints Type hints are not enforced. These can be read by debuggers, but Python itself allows these types to be violated. ```Python def meow(n: int) -> None: # -> None indicates that the function does not return a value ... number: int = int(input("Number: ")) ``` ## Data Types | Data Type Category | Data Types | | ------------------ | ---------------------------------- | | Text Type | `str` | | Numeric Types | `int`, `float`, `complex` | | Sequence Types | `list`, `tuple`, `range` | | Mapping Type | `dict` | | Set Types | `set`, `frozenset` | | Boolean Type | `bool` | | Binary Types | `bytes`, `bytearray`, `memoryview` | | None Type | `NoneType` | > `float` types can overflow, much like C, but `int` never does, and instead grows as large as needed to fit a given value. ### Type Conversion ```Python int("1") # convert to int float(2) # convert to float ``` ### Data Type Functions ```Python round(5.12) # round a number round(3.43, 2) # round to the 2nd decimal place len(list) # length of a list (nº of elements) ``` ### Unpack Values Values can be split using `*`. This method works with lists, dictionaries, and tuples. For dictionaries, `*dict` returns key names and `**dict` returns the value names. Unpack can also be used when returning values from functions. ```Python def total(gold, silver, copper): return gold * 100 + silver * 10 + copper coins = [100, 50, 25] print(total(*coins), "Copper coins") ``` ### F Strings A string that executes formatting. ```Python f'Some text: {1+1}' # Executes 1+1 and prints "Some text: 2" f'{z:.2f}' # Variable z is rounded to the 2nd decimal place. ``` ### Ranges Produce a range of numbers. ```Python range(10) # 1, 2, 3... 10 if 80 <= score < 90: code... ``` ### Lists Arrays are flexible in Python, unlike C. Elements can be added and removed at runtime. ```Python scores = [72, 73, 33] scores.append(32) # scores += [43, 45] # Adds a list to another list average = sum(scores) / len(scores) print(f"Average: {average}") scores[1:] \#Slice the list -> From 1 to the end. (Skips element 0) scores[1:-1] \#Slice the list -> From 1 to the end. (Skips element 0 and last) ``` #### List Comprehension Create a new list with a function applied to every element of the list. ```Python words = ["This", "is", "CS50"] uppercased = [word.upper() for word in words] print(*uppercased) ``` In this example, every element in `words` is uppercased. This feature is useful to filter items as well: ```Python students = [ {"name": "Trude", "class": "code"}, {"name": "Maxs", "class": "art"}, {"name:" "JCionx", "class": "code"}, ] codeStudents = [ student["name"] for student in students if student["class"] == "code" ] ``` #### Filter Function Similar code, but for a more functional oriented programming approach. ```Python def is_class(s): return s["class"] == "code" coders = filter(is_class, students) ``` ### Sets Similar to a list, but can only contain unique items. ```Python values = set(1, 1, 1, 2) print(values) # 1, 2 ``` ### Dictionaries A list, but instead of using an index, uses keywords. ```Python students = { "Key1": "Value", "Key2": "Value", "Key3": "Value", } print(students["Key1"]) ``` #### Loop over Dictionary ```Python for student in students: print(student) # Iterates over keys. print(students[student]) # Iterates over values. ``` #### Dictionary Comprehension Filter dictionaries. ```Python students = ["Trude", "Maxs", "JCionx"] # Classic for coders = [] for student in students: coders.append({"name": student, "class": "code"}) # List comprehension (3 dicts in a list) coders = [{"name": student, "class": "code"} for student in students] # Dict comprehension (1 dict with name: class for all 3) coders = {student: "code" for student in students} ``` ### Hashtable ```Python table = [ {"Key1": "Value", "Key2": "Value", "Key3": "Value"}, {"Key1": "Value", "Key2": "Value", "Key3": "Value"}, {"Key1": "Value", "Key2": "Value", "Key3": "Value"}, {"Key1": "Value", "Key2": "Value"}, ] print(table[0]["Key2"]) for tab in table: print(tab["Key1"]) # Print all Key1 values. ``` ## Conditions ### IF ```Python if x < y: print("x is less than y") elif x > y: print("x is greater than y") else: print("x is not less than y") if test in numbers: code... ``` `()` are optional in the condition. ### `:=` Operator Syntax sugar to integrate an assignment operator with an IF statement. **Without it** ```Python matches = 1 == 1 if matches: print(matches) # True ``` **With it** ```Python if matches := 1 == 1: print(matches) # True ``` ## Loops ### While ```Python while True: print("meow") i = 0 while i < 3: print("meow") i += 1 ``` ### For #### Loop through an Array ```Python for _ in [0, 1, 2]: print("Hello World.") # _ throws away the value (no variable). ``` #### Loop through a Range ```Python for i in range(3): print("Loops from 0 - 3") ``` #### Loop through an Array, Using Its Value as the Index ```Python students = ["one", "two", "three"] for student in students: print(student) ``` #### Enumerate Iterate over a sequence, and get the current value and index. ```Python students = ["Trude", "Maxs", "JCionx"] for i, student in enumerate(students): print(i + 1, student) ``` ### Map Execute a function on every element of a list. Takes two arguments: - Name of a function - Data structure to apply the function on ```Python words = ["This", "is", "CS50"] uppercased = map(str.upper, words) print(*uppercased) ``` ### Keywords - `break`: leave the loop - `continue`: skip to next iteration ## Libraries ```Python import lib lib.component(...) from lib import component component(...) ``` ### Packages A package is a collection of libraries in the same directory, often built by other developers. You can download user packages on [pypi.org](http://pypi.org/). #### Install Packages with Pip `pip` is a package manager for Python. ```Bash pip install package ``` ### Useful Libraries - `random` Generate random numbers - `shuffle` - `choice` - `randint` - `statistics` - `mean` - `requests` Web requests (similar to `curl`) useful for interfacing with APIs #### APIs APIs usually communicate in JSON. ```Python import requests, sys import json response = requests.get("") print(response.json()) # Print respose formatted for json (as a py dictionary)) print(json.dumps(response.json(), indent=2)) # Print response indented to read easily. o = response.json() for result in o["results"]: print(result["trackName"]) # Print all songs in the response ``` ### Create Custom Library ```Python # LIBRARY: lib.py file def main(): hello("world") def hello(name): print(f"hello, {name}") # Only call main if ran from the command line. Not as a library. if __name__ == "__main__": main() ``` ```Python # MAIN CODE: main.py file from lib import hello hello("trude") ``` ## Functions ```Python def hello(name): print(f'Hello {name}') hello("trude") ``` ### Default Values ```Python def hello(to='world'): print("hello,", to) hello() # hello, world hello('trude') # hello, trude ``` ### Main Function ```Python def main(): # main code ... # other fucntions ... main() ``` ### Generator Function (Yield) Return one value at a time, without ending the function, and then loop again. Useful when working with a large amount of logic that takes a long time to execute. ```Python def sheep(n): for i in range(n): yield "SHEEP" * i ``` `yield` returns an iterator that allows the program to handle one element at a time and not lose track. ### Command Line Arguments ```Python from sys import argv if len(argv) == 2: print(f"Hello, {argv[1]}") else: print("Hello World.") # argv[0] is the program name # List all arguments for arg in argv: print(arg) ``` #### Flags To support commands like: `python file.py -n 2` ```Python import argparse parser = argparse.ArgumentParser(description="Meow like a cat") parser.add_argument("-n", default=1, type=int, help="number of times to meow") args = parser.parse_args() for _ in range(args.n): # .n is the -n flag print("meow") ``` #### Allow Function to Take Any Number of Arguments (Unpack) ```Python def f(*args, **kwargs): print("Positional: ", args) print("Named: ", kwargs) f(100, 50, 25) # Positional: (100, 50, 25) f(100, 50, 25, 5) # Positional: (100, 50, 25, 5) f(gold=10, silver=20, copper=1) # Named: {'gold': 10, 'silver'20 ...} ``` ### Pass Functions as Arguments ```Python def get_name(student): return student["name"] # Sort by student name for student in sorted(students, key=get_name): ... ``` #### Lambda Function Pass a function without having to give it a name. ```Python for student in sorted(students, key=lambda student: student["name"]): ... ``` ## Exceptions ```Python # Int try: # Try to execute x = int(input("What's your number?")) except ValueError: # If error is ValueError print("Not a number.") except: # If error print("Unknown Error.") else: # If success print(f"Your number+1 is {x + 1}") ``` ```Python try: ... except: pass # Catch the error, but then ignore it. ``` ## Operators ### Comparison Operators - `<` - `>` - `≤` - `≥` - `≠` - `==` ### Logic Operators - `and` - `or` ```Python s = input("Do you agree?") if s == 'y' or s == 'Y' or s == "Yes" or s == "yes": (...) # This can be improved: s = input("Do you agree?").lower() if s in ['y', 'yes']: (...) ``` ## Scope 1. Program 2. Function (Variables in loops and IFs are global to the function) ## Exit with Error Codes ```Python from sys import exit exit(1) # Exit with error code 1 exit("Failed for some reason") ``` ## Unit Tests Helper code to test the main code and (hopefully) catch bugs before they can reach production. ### Manual Tests **Main code** ```Python def main(): x = int(input("X value: ")) print(f"X^2 is {square(x)}") def square(n): return(n * n) if __name__ = "__main__": main() ``` **Test code (Using IF)** ```Python from file import square def main(): test_square() def test_square(): if square(2) != 4: print("2 squared was not 4.") if square(3) != 9: print("3 squared was not 9.") if __name__ == "__main__": main() ``` **Improved test code (Using assert)** ```Python from file import square def main(): test_square() def test_square(): try: assert square(2) == 4 except AssertionError: print("2 squared was not 4.") try: assert square(3) == 9 except AssertionError: print("3 squared was not 9.") if __name__ == "__main__": main() ``` ### **Pytest** `pytest` is an external program that simplifies python's unit testing. ```Python import pytest from file import square def test_square(): assert square(2) == 4 assert square(3) == 9 assert square(-2) == 4 assert square(-3) == 9 assert square(0) == 0 def test_str(): # Verify that an error will happen for a str with pytest.raises(TypeError): square("cat") ``` Test with `pytest`: ```Shell pytest test_file.py ``` > A function with side effects (doesn't return a value) can't be tested. ## File I/O ### Write to a File (Replace) ```Python name = "trude" file = open("name.txt", "w") file.write(name) file.close() ``` ### Append to a File (Add) ```Python name = "trude" file = open("name.txt", "a") file.write(name) file.close() ``` ### Open a File and close Automatically ```Python name = "trude" with open("name.txt", "a") as file: file.write(name) \\#File is only open in this scope ``` ### Read a File `"r"` is optional when reading. ```Python with open("name.txt", "r") as file: lines = file.readlines() # Return all lines as a list for line in lines: print(line.rstrip()) # Print all lines and remove \\n separating each line ``` #### Better Code for Reading Every line ```Python with open("name.txt") as file: for line in file: print(line.rstrip()) ``` #### Sort File Lines ```Python names = [] with open("name.txt") as file: for line in file: names.append(line.rstrip()) for name in sorted(names): print(f"Hello, {name}") ``` ### CSV Files #### Read **Python** ```Python import csv students = [] with open("students.csv") as file: reader = csv.reader(file) for name, home in reader: students.append({"name": name, "home": home}) for student in students: print(f"{student['name]"} is from {student['home']}") ``` **CSV File** ```Plain trude, myHome maxs, hisHome jcionx, anotherHome ``` #### Dict Reader Ignores column order and doesn't break the program if new columns are added. It is recommended to **always** use `**DictReader**` **instead of** `**Reader**`**.** **Python** ```Python import csv students = [] with open("students.csv") as file: reader = csv.DictReader(file) for row in reader: students.append({"name": row["name"], "home": row["home"]}) for student in students: print(f"{student['name]"} is from {student['home']}") ``` **CSV File** ```Plain name, home trude, myHome maxs, hisHome jcionx, anotherHome ``` #### Write ```Python import csv name = 'trude' home = 'myHome' with open("students.csv", "a") as file: writer = csv.writer(file) writer.writerow([name, home]) ``` #### Dict Writer ```Python import csv name = 'trude' home = 'myHome' with open("students.csv", "a") as file: writer = csv.DictWriter(file, fieldnames=["name", "home"]) writer.writerow({"name": name, "home": home}) ``` ### Other Formats - TXT - CSV - JSON - BINARY - Images (PIL library) #### Animated GIF Program ```Python from PIL import Image items = ["image1.png", "image2.png"] images = [] for item in items: image = Image.open(item) images.append(image) images[0].save( "images.gif", save_all=True, append_images=[images[1]], duration=200, loop=0 ) ``` ## Regular Expressions (regex) Define patterns for comparisons. ```Python import re email = input("Email Address: ").strip() if re.search(r"^.+@.+\\.edu$", email, re.IGNORECASE): print("Email is valid.") else: print("Email is not valid.") ``` ### Regular Expression Symbols - `.` any character except a newline - 0 or more repetitions - `+` 1 or more repetitions - `?` 0 or 1 repetition - `\\` escape character - `{m}` m repetitions - `{m,n}` m-n repetitions (range) - `^` match the start of the string - `$` match the end of the string - `[]` set of characters - `[^]` complementing the set - `\\d` decimal digit - `\\D` not a decimal digit - `\\s` whitespace characters - `\\S` not a whitespace character - `\\w` word character, numbers and underscore - `\\W` not a word character - `A|B` either A or B - `(…)` a group - `(?:…)` non-capturing version Always use `r"string"` (raw string) so python won't interpret `\\` as a special character. ### RE Lib Flags - `re.IGNORECASE` - `re.MULTILINE` - `re.DOTALL` ### Examples #### Regular Expression for Email Validation ```Python import re regex = re.compile(r"^[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*@(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$") def isValid(email): if re.fullmatch(regex, email): print("Valid email") else: print("Invalid email") ``` [More regular expressions](https://www.labnol.org/internet/regular-expressions-forms/28380/) `compile` caches the query to make new lookups faster. When a regular expression becomes too complex, the rule of thumb is to **use a library instead**. ## Object-Oriented Programming: OOP ### Tuple An immutable (constant) list. ```Python tuple = (val1, val2) ``` #### Return More than One Value from a Function ```Python def get_student(): n = input("Name: ") h = input("House: ") return n, h name, house = get_student() print(name) student = get_student() print(student[0]) ``` To make the values mutable, return a list instead. ```Python return [item1, item2] ``` ### Class Classes are 'blueprints' for data; They define custom data types. For example, you can use a class to represent a real-world entity. #### Basic Class ```Python class Student: ... # '...' is a valid placeholder student = Student # Create an object from the Student class student.name = "trude" # Add a new instance variable student.house = "myHome" print(student.name) # 'trude' ``` #### Attributes ```Python class Student: def __init__(self, name, house): # Initialization function self.name = name self.house = house name = 'trude' house = 'myHome' student = Student(name, house) ``` - `self` is the object created from the class definition. - When an object is created, the `__init__` function is executed. #### Attributes Error Handling ```Python class Student: def __init__(self, name="Unknown", house): # Initialization function if not name: raise ValueError("Missing name") # Create and output a custom error if house not in ["myHome", "yourHome", "hisHome"]: raise ValueError("Invalid house") self.name = name self.house = house ``` #### Change what Happens when an Object is Printed ```Python class Student: def __init__(self, name, house): # Initialization function self.name = name self.house = house def __str__(self): return f"A Student - {self.name}" student = Student("trude", "myHome") print(student) # "A Student - trude" ``` #### Methods ```Python class Mage: def __init__(self, name, clan, element): # Initialization function self.name = name self.clan = clan self.element = element def castElement(self): match self.element: case "Fire": return "FIRE SPELL" case "Water": return "Water SPELL" case "Earth": return "Earth SPELL" case "Air": return "AIR SPELL" case _: return "NO SPELL" mage = Mage("trude", "magicINC", "Fire") print(mage.castElement()) ``` #### Properties Dot notation (`student.name = ""`) can bypass any data validation. To avoid this, make attributes constant. A constant attribute is referred to as a property. ```Python class Mage: def __init__(self, name, clan, element): # Initialization function self.name = name self.clan = clan self.element = element # Getter (function to get house attribute) @property # Indicates that this is a getter def clan(self): return self._clan # Add _ to avoid name collision. # Setter (function to set a value) @clan.setter # Indicates that this is a setter def clan(self, clan): if clan not in ["magicINC", "spellInator", "glyphGens"]: raise ValueError("Invalid Clan") self._clan = clan mage = Mage("trude", "spellInator", "Fire") mage.clan = "not-a-clan" ``` #### Example ```Python class Mage: ... def main(): mage = get_mage() print(f"{mage.name} from {mage.clan}") def get_mage(): mage = Mage() mage.name = "trude" mage.clan = "spellInator" return mage if __name__ == "__main__": main() ``` #### Class Methods A method that relates to the class itself, and doesn't depend on the objects individually. ```Python import random class Hat: def __init__(self): self.clans = ["magicINC", "spellInator", "glyphGen"] def sort(self, name): print(name "is in", random.choice(self.clans)) hat = Hat() hat.sort("Trude") ``` #### Class Variables - `self.var` A variable unique for each object - `var` A variable shared with all objects ```Python class Hat: clans = ["magicINC", "spellInator", "glyphGen"] @classmethod def sort(cls, name): print(name "is in", random.choice(cls.clans)) Hat.sort("Trude") ``` A class method is a method global to the class, instead of created for each object. It relates to the data type (class) itself. #### Example ```Python class Mage: ... @classmethod def get(cls): name = input("Name: ") clan = input("Clan: ") return cls(name, clan) # name and clans are returned to the class as self.name and self.clan. ... def main(): mage = Mage.get() print(mage) if __name__ == "__main__": main() ``` ### Inheritance Inherit code from a parent class to avoid code duplication. ```Python class Person: def __init__(self, name) if not name: raise ValueError("Missing name") self.name = name class Student(Person): # Pass Person to Student def __init__(self, name, house): super().__init__(name) # Execute Person's __init__ method. self.house = house ... class Professor(Person): def __init__(self, name, subject): super().__init__(name) self.subject = subject ... person = Person("Random Guy") student = Student("Trude", "myHome") professor = Professor("Someone", "Math") ``` ### Operator Overloading Customize what an operator does with a Class. - `__add__` Behavior of '+' operator. ```Python class Vault: def __init__(self, gold=0, silver=0, copper=0): self.gold = gold self.silver = silver self.copper = copper def __str__(self): return f"{self.gold} Gold, {self.silver} Silver, {self.copper} Copper." def __add__(self, other): gold = self.gold + other.gold silver = self.silver + other.silver copper = self.copper + other.copper return Vault(gold, silver, copper) trude = Vault(100, 50, 25) jcionx = Vault(25, 50, 100) print(trude) # 100 Gold, 50 Silver, 25 Copper print(jcionx) # 25b Gold, 50 Silver, 100 Copper total = trude + jcionx print(total) # 125b Gold, 100 Silver, 125 Copper ```