├── .gitignore ├── README.md ├── ajax_notes.md ├── binary_and_unicode.py ├── bokeh_example.py ├── booleans.py ├── builtin_functions.py ├── classes.py ├── composition.py ├── comprehensions.py ├── concurrency.py ├── conditionals.py ├── context_managers.py ├── data ├── NEO_historical.csv ├── accounts1.sqlite ├── accounts2.sqlite ├── backups │ └── test.txt ├── bohemian_rhapsody_lyrics.txt ├── cities.csv ├── death_valley_2014.csv ├── definitions ├── example.txt ├── filename.txt ├── img │ ├── bokeh-example.png │ ├── folium-example.png │ ├── matplotlib-csv-example.png │ ├── matplotlib-example.png │ ├── matplotlib-examples.png │ ├── pygal-example-1.png │ ├── pygal-example-2.png │ ├── pygal-intro.png │ ├── pygal-json-example.png │ ├── tkinter-example.png │ └── tkinter-module.png ├── map1.html ├── menu.json ├── music.pickle ├── music │ ├── Beatles │ │ └── Sgt. Pepper's Lonely Hearts Club Band │ │ │ ├── 1 - Sgt. Pepper's Lonely Hearts Club Band.emp3 │ │ │ ├── 10 - Lovely Rita.emp3 │ │ │ ├── 11 - Good Morning Good Morning.emp3 │ │ │ ├── 12 - Sgt. Pepper's Lonely Hearts Club Band (reprise).emp3 │ │ │ ├── 13 - A Day In The Life.emp3 │ │ │ ├── 2 - With A Little Help From My Friends.emp3 │ │ │ ├── 3 - Lucy In The Sky With Diamonds.emp3 │ │ │ ├── 4 - Getting Better.emp3 │ │ │ ├── 5 - Fixing A Hole.emp3 │ │ │ ├── 6 - She's Leaving Home.emp3 │ │ │ ├── 7 - Being For The Benefit Of Mr. Kite.emp3 │ │ │ ├── 8 - Within You Without You.emp3 │ │ │ └── 9 - When I'm Sixty-Four.emp3 │ ├── Bernie Torme │ │ └── Demolition Ball │ │ │ ├── 1 - Fallen Angel.emp3 │ │ │ ├── 10 - Draw The Line.emp3 │ │ │ ├── 11 - U.S. Maid.emp3 │ │ │ ├── 12 - Let It Go.emp3 │ │ │ ├── 13 - Walk It.emp3 │ │ │ ├── 14 - Man O'Means.emp3 │ │ │ ├── 15 - Monkey Business.emp3 │ │ │ ├── 16 - My Darlene.emp3 │ │ │ ├── 2 - Black Sheep.emp3 │ │ │ ├── 3 - Action.emp3 │ │ │ ├── 4 - Ball & Chain.emp3 │ │ │ ├── 5 - Slip Away.emp3 │ │ │ ├── 6 - Long Time Coming.emp3 │ │ │ ├── 7 - Spinnin' Your Wheels.emp3 │ │ │ ├── 8 - Don't Understand.emp3 │ │ │ └── 9 - Industry.emp3 │ └── Beth Orton │ │ └── Trailer Park │ │ ├── 1 - She Cries Your Name.emp3 │ │ ├── 10 - I Wish I Never Saw The Sunshine.emp3 │ │ ├── 11 - Galaxy Of Emptiness.emp3 │ │ ├── 2 - Tangent.emp3 │ │ ├── 3 - Don't Need A Reason.emp3 │ │ ├── 4 - Live As You Dream.emp3 │ │ ├── 5 - Sugar Boy.emp3 │ │ ├── 6 - Touch Me With Your Love.emp3 │ │ ├── 7 - Whenever.emp3 │ │ ├── 8 - How Far.emp3 │ │ └── 9 - Someone's Daughter.emp3 ├── my_files │ └── test.txt ├── neo_btc.csv ├── neo_usd.csv ├── numbers.json ├── pizzas ├── plants_shelf ├── population_data.json ├── practice.cfg ├── practice.xml ├── practice.yaml ├── practice1.txt ├── practice2.txt ├── singers.csv ├── supermarkets-commas.txt ├── supermarkets-semicolons.txt ├── supermarkets.csv ├── supermarkets.json ├── supermarkets.xlsx ├── test.log ├── test.txt ├── verlegenhuken.xlsx ├── volcanoes.csv └── world.json ├── data_types.py ├── data_visualization.md ├── dataclasses_module.py ├── date_time_examples.py ├── date_time_modules.py ├── debugging.py ├── decorators.py ├── demos ├── americas.svg ├── die_visual_1.svg ├── die_visual_2.svg ├── die_visual_3.svg ├── line_plot1.html ├── line_plot2.html ├── redis_dryer.py ├── redis_pub.py ├── redis_sub.py ├── redis_washer.py ├── sample_plot.png ├── scatter_plot1.html ├── scatter_plot2.html ├── tasks.py ├── tasks_with_info.py ├── tcp_client.py ├── tcp_server.py ├── udp_client.py └── udp_server.py ├── deployment.md ├── deployment_digitalocean.md ├── deployment_docker.md ├── deployment_heroku.md ├── design_patterns.md ├── dictionaries.py ├── documenting_naming.py ├── escape_characters.py ├── exceptions.py ├── files_read_write.py ├── flask_blueprints_and_app_factory.md ├── flask_bootstrap.md ├── flask_jinja_notes.md ├── flask_mqtt.md ├── flask_multiple_databases.md ├── flask_socketio.md ├── flask_user_roles.md ├── folium_webmaps.py ├── formatting.py ├── functions.py ├── generators.py ├── geopy_module.py ├── helper_functions.py ├── import_modules.py ├── inheritance.py ├── iterables_summary.py ├── iterating_with_for.py ├── json_example.py ├── jupyter_notebook.md ├── keywords.py ├── lists.py ├── logging_errors.py ├── magic_methods.py ├── match_statements.py ├── matplotlib_csv_example.py ├── matplotlib_example.py ├── matplotlib_intro.py ├── namespaces.py ├── networks.md ├── noSQL_datastores.py ├── numpy_modules.py ├── operators.py ├── package_management.md ├── packages_example.md ├── pandas_data_analysis.py ├── pickling.py ├── polymorphism.py ├── postgreSQL_example.py ├── postman.md ├── pyenv.md ├── pygal_github_api_example.py ├── pygal_hn_api_example.py ├── pygal_intro.py ├── pygal_json_example.py ├── queues.py ├── recursion.py ├── reflection.py ├── regular_expressions.py ├── relational_databases.py ├── resources.py ├── rest_APIs.md ├── rounding_example.py ├── sets.py ├── shelve_module.py ├── sqlite3_example1.py ├── sqlite3_example2.py ├── standard_library.py ├── strings.py ├── structured_file_formats.py ├── system.py ├── template.py ├── terminology.py ├── testing.py ├── timezones.py ├── timezones_example.py ├── tkinter_example.py ├── tkinter_module.py ├── tuples.py ├── virtual_environments.md ├── web_scraping.py ├── web_servers_frameworks.py ├── while_loops.py └── zip_function.py /.gitignore: -------------------------------------------------------------------------------- 1 | # https://git-scm.com/docs/gitignore 2 | # https://help.github.com/articles/ignoring-files/ 3 | 4 | # dependencies 5 | /node_modules 6 | 7 | # bytecode compiled 8 | __pycache__/ 9 | 10 | # environments 11 | .env 12 | /venv 13 | 14 | # logs 15 | /logs 16 | 17 | # database 18 | app.db 19 | dump.rdb 20 | 21 | # misc 22 | .DS_Store 23 | 24 | # project specific 25 | requirements.txt 26 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # python notes 2 | 3 | 4 | ## description 5 | In my path to learning Python, I'll be collecting my notes and example code here for reference and review purposes. Filenames reflect the topic as much as possible. The data directory contains resources for some of the examples. To date, my training is self-guided through the use of text books and online resources. 6 | 7 | ## credits 8 | Some examples and comments may come from the following: 9 | 10 | - [Introducing Python](http://shop.oreilly.com/product/0636920028659.do) - *Bill Lubanovic* 11 | - [Python Crash Course](https://nostarch.com/pythoncrashcourse) - *Eric Matthes* 12 | - [Python Playground](https://nostarch.com/pythonplayground) - *Mahesh Venkitachalam* 13 | - [The Python 3 Standard Library by Example](https://doughellmann.com/blog/the-python-3-standard-library-by-example/) - *Doug Hellmann* 14 | - [The Flask Mega-Tutorial](https://courses.miguelgrinberg.com/) - *Miguel Grinberg* 15 | - [Flask Web Development](http://shop.oreilly.com/product/0636920089056.do) - *Miguel Grinberg* 16 | - [Python Cookbook](http://shop.oreilly.com/product/0636920027072.do) - *David Beazley & Brian K. Jones* 17 | - [Python: Master the Art of Design Patterns](https://www.oreilly.com/library/view/python-master-the/9781787125186/) - *Phillips, Girighar, Kasampalis* 18 | - [Effective Python](https://effectivepython.com/) - *Brett Slatkin* 19 | - [Complete Python Masterclass](https://www.udemy.com/python-the-complete-python-developer-course/) - *Udemy course* 20 | - [The Python Mega Course: Build 10 Real World Applications](https://www.udemy.com/the-python-mega-course/) - *Udemy course* 21 | - [REST APIs with Flask and Python](https://www.udemy.com/rest-api-flask-and-python/) - *Udemy course* 22 | - [Think Python](https://greenteapress.com/wp/think-python/) - *Allen B. Downey* 23 | - [Learn Python the Hard Way](https://learnpythonthehardway.org/) - *Zed Shaw* 24 | - [Python documentation](https://docs.python.org/3/index.html) - Python Software Foundation 25 | 26 | Very special thanks to [aleph2c](https://github.com/aleph2c) for the unending explanations, knowledge and support. Check out his epic [python statechart library; Miros here](https://aleph2c.github.io/miros/index.html). 27 | 28 | ## images 29 | 30 | From [**bokeh_example.py**](bokeh_example.py) 31 | ![Bokeh Scatter Plot](data/img/bokeh-example.png "Bokeh Scatter Plot") 32 | 33 | From [**folium_webmaps.py**](folium_webmaps.py) 34 | ![Folium Webmap](data/img/folium-example.png "Volcano Plot") 35 | 36 | From [**matplotlib_intro.py**](matplotlib_intro.py) 37 | ![Matplotlib Plots](data/img/matplotlib-examples.png "Matplotlib Plots") 38 | 39 | From [**matplotlib_example.py**](matplotlib_example.py) 40 | ![Matplotlib Random Walk Plot](data/img/matplotlib-example.png "Random Walk Plot") 41 | 42 | From [**matplotlib_csv_example.py**](matplotlib_csv_example.py) 43 | ![Matplotlib Example Line Plot](data/img/matplotlib-csv-example.png "Line Plot") 44 | 45 | From [**pygal_intro.py**](pygal_intro.py) 46 | ![Pygal Plot Example](data/img/pygal-intro.png "Pygal Bar Plots") 47 | 48 | From [**pygal_json_example.py**](pygal_json_example.py) 49 | ![Pygal Mapping Tools Example](data/img/pygal-json-example.png "Pygal Mapping Tools") 50 | 51 | From [**pygal_api_example1.py**](pygal_api_example1.py) 52 | ![Pygal Plot Example](data/img/pygal-example-1.png "Pygal Plot from Github API") 53 | 54 | From [**pygal_api_example2.py**](pygal_api_example2.py) 55 | ![Pygal Plot Example](data/img/pygal-example-2.png "Pygal Plot from Hacker News API") 56 | 57 | From [**tkinter_module.py**](tkinter_module.py) 58 | Tkinter unit converter 59 | 60 | From [**tkinter_example.py**](tkinter_example.py) 61 | Tkinter Example 62 | -------------------------------------------------------------------------------- /booleans.py: -------------------------------------------------------------------------------- 1 | '''Booleans: True False Logic''' 2 | 3 | 4 | # Boolean values are the two constant objects False and True. In numeric 5 | # contexts, they behave like the integers 0 and 1, respectively. The built-in 6 | # function bool() can be used to check any value as a Boolean, if the value can 7 | # be interpreted as a truth value. 8 | 9 | a = [1, 3, 6] 10 | b = [] 11 | c = None 12 | 13 | print(bool(a)) # True 14 | print(bool(b)) # False 15 | print(bool(c)) # False 16 | 17 | # True 18 | # False 19 | # and 20 | # or 21 | # in 22 | # is 23 | # not 24 | # not ... or 25 | # not ... and 26 | # not in 27 | # is not 28 | # == 29 | # != 30 | # >= 31 | # <= 32 | 33 | # Any 'and' expression that has a False is immediately False 34 | # Any 'or' expression that has a True is immediately True 35 | 36 | print(not False) # True 37 | print(not True) # False 38 | print(True or False) # True 39 | print(True or True) # True 40 | print(False or True) # True 41 | print(False or False) # False 42 | print(True and False) # False 43 | print(True and True) # True 44 | print(False and True) # False 45 | print(False and False) # False 46 | print(not (True or False)) # False 47 | print(not (True or True)) # False 48 | print(not (False or True)) # False 49 | print(not (False or False)) # True 50 | print(not (True and False)) # True 51 | print(not (True and True)) # False 52 | print(not (False and True)) # True 53 | print(not (False and False)) # True 54 | print(1 != 0) # True 55 | print(1 != 1) # False 56 | print(0 != 1) # True 57 | print(0 != 0) # False 58 | print(1 == 0) # False 59 | print(1 == 1) # True 60 | print(0 == 1) # False 61 | print(0 == 0) # True 62 | -------------------------------------------------------------------------------- /composition.py: -------------------------------------------------------------------------------- 1 | '''Composition & Aggregation''' 2 | 3 | 4 | # Inheritance is a good technique (child is-a parent) but it can be tempting 5 | # to build elaborate inheritance hierarchies. Sometimes composition or 6 | # aggregation (x has-a y) makes more sense. There is actually a difference 7 | # between composition and aggregation though they often blur together. 8 | 9 | 10 | # Composition 11 | # ----------------------------------------------------------------------------- 12 | # In this example, the Room class creates a Floor object and a Windows object. 13 | # The arguments for these two objects are passed in when the Room is created. 14 | # If the Room is deleted, so are the Floor and Window instances. 15 | 16 | class Floor(): 17 | def __init__(self, material): 18 | self.material = material 19 | 20 | class Windows(): 21 | def __init__(self, quantity): 22 | self.quantity = quantity 23 | 24 | class Room(): 25 | def __init__(self, floor, windows): 26 | self.floor = Floor(floor) 27 | self.windows = Windows(windows) 28 | def about(self): 29 | print('This room has', self.floor.material, 'floors and', 30 | self.windows.quantity, 'windows') 31 | 32 | bathroom = Room('tiled', 0) 33 | bathroom.about() # This room has tiled floors and 0 windows 34 | 35 | 36 | # Aggregation 37 | # ----------------------------------------------------------------------------- 38 | # Unlike composition, aggregation uses existing instances of objects to build 39 | # up another object. The composed object does not actually own the objects that 40 | # it's composed of. If it's destroyed, those objects continue to exist. 41 | 42 | # This example creates Floor and a Window objects, then passes them as 43 | # arguments to a Room object. If the Room object is deleted, the other two 44 | # objects remain. 45 | 46 | class Floor(): 47 | def __init__(self, material): 48 | self.material = material 49 | 50 | class Windows(): 51 | def __init__(self, quantity): 52 | self.quantity = quantity 53 | 54 | class Room(): 55 | def __init__(self, floor, windows): 56 | self.floor = floor 57 | self.windows = windows 58 | def about(self): 59 | print('This room has', floor.material, 'floors and', 60 | windows.quantity, 'windows') 61 | 62 | floor = Floor('hardwood') 63 | windows = Windows('4') 64 | kitchen = Room(floor, windows) 65 | 66 | kitchen.about() # This room has hardwood floors and 4 windows 67 | 68 | 69 | # Composition vs Inheritance 70 | # ----------------------------------------------------------------------------- 71 | # There's always more than one solution. Consider the following where we want 72 | # to include address information for a class. The first example uses 73 | # composition, the second uses inheritance. Both are viable solutions but 74 | # composition (has a) feels like it makes more sense. 75 | 76 | # Composition (Customer has a Address): 77 | 78 | class Address(): 79 | def __init__(self, street, city, province, code): 80 | self.street = street 81 | self.city = city 82 | self.province = province 83 | self.code = code 84 | 85 | class Customer(): 86 | def __init__(self, name, email, **kwargs): 87 | self.name = name 88 | self.email = email 89 | self.address = Address(**kwargs) 90 | 91 | a = {'street': '1234 Main St.', 92 | 'city': 'Vancouver', 93 | 'province': 'BC', 94 | 'code': 'V5V1X1'} 95 | 96 | c = Customer('bob', 'bob@email.com', **a) 97 | 98 | print(c.address.city) 99 | # Vancouver 100 | 101 | 102 | # Inheritance (Customer is a AddressHolder): 103 | 104 | class AddressHolder(): 105 | def __init__(self, street, city, province, code): 106 | self.street = street 107 | self.city = city 108 | self.province = province 109 | self.code = code 110 | 111 | class Customer(AddressHolder): 112 | def __init__(self, name, email, **kwargs): 113 | self.name = name 114 | self.email = email 115 | super().__init__(**kwargs) 116 | 117 | a = {'street': '1234 Main St.', 118 | 'city': 'Victoria', 119 | 'province': 'BC', 120 | 'code': 'V5V1X1'} 121 | 122 | c = Customer('chuck', 'chuck@email.com', **a) 123 | 124 | print(c.city) 125 | # Victoria 126 | -------------------------------------------------------------------------------- /conditionals.py: -------------------------------------------------------------------------------- 1 | '''If Statements and Conditional Tests''' 2 | 3 | 4 | # At the heart of every if statement is an expression that can be evaluated as 5 | # True or False and this is called a conditional test (also called a boolean 6 | # expression). If the conditional test evaluates as True, python will execute 7 | # the code following the if statement, otherwise it will be ignored. 8 | 9 | cars = ['ford', 'bmw', 'honda', 'subaru'] 10 | 11 | # checking for equality: 12 | for car in cars: 13 | if car == 'bmw': 14 | print(car.upper()) 15 | else: 16 | print(car.title()) 17 | 18 | # checking for inequality: 19 | for car in cars: 20 | if car != 'bmw': 21 | print(car.title()) 22 | else: 23 | print(car.upper()) 24 | 25 | # Both produce the same result: 26 | # Ford 27 | # BMW 28 | # Honda 29 | # Subaru 30 | 31 | 32 | # if, elif, else chains 33 | # ----------------------------------------------------------------------------- 34 | 35 | age = 42 36 | 37 | if age < 5: 38 | admission = 0 39 | elif age < 19: 40 | admission = 5 41 | elif age >= 65: 42 | admission = 3 43 | else: 44 | admission = 10 45 | 46 | print(admission) # 10 47 | 48 | # Note you are not required to have a final else statement. It may be more 49 | # readable to use another elif. Keep in mind if you do it this way, there is no 50 | # catchall at the end, so the value must meet one of the elif conditions or it 51 | # may raise an exception. 52 | 53 | if age < 5: 54 | admission = 0 55 | elif age < 19: 56 | admission = 5 57 | elif age >= 65: 58 | admission = 3 59 | elif age < 65: 60 | admission = 10 61 | 62 | 63 | # Check for values with in, not in 64 | # ----------------------------------------------------------------------------- 65 | 66 | users = ['rick', 'morty', 'raja', 'bob'] 67 | user = 'morty' 68 | 69 | if user in users: 70 | print('Hello', user.title()) 71 | else: 72 | print('Create account') 73 | 74 | # same as: 75 | if user not in users: 76 | print('Create account') 77 | else: 78 | print('Hello', user.title()) 79 | 80 | 81 | # Check for multiple conditions 82 | # ----------------------------------------------------------------------------- 83 | 84 | import time 85 | 86 | now = time.localtime() # 87 | day = time.strftime('%A', now) # 88 | hour = int(time.strftime('%H', now)) # 89 | 90 | if (4 > hour or hour > 20) and (day != 'Friday') and (day != 'Saturday'): 91 | print('Time for bed!') 92 | else: 93 | print('Carry on.') 94 | 95 | 96 | # if, else assignments 97 | # ----------------------------------------------------------------------------- 98 | 99 | flag = True 100 | 101 | x = 100 if flag else 0 102 | 103 | print(f'{flag=}, {x=}') 104 | # flag=True, x=100 105 | -------------------------------------------------------------------------------- /context_managers.py: -------------------------------------------------------------------------------- 1 | '''Context managers: with expression as variable''' 2 | 3 | 4 | # Context managers allow the execution of initialization and finalization 5 | # code around another block of code. In simpler terms, its helpful for when you 6 | # have two related operations you'd like to execute as a pair, with a block of 7 | # code in between. 8 | 9 | print('set things up') # initialization code 10 | try: 11 | print('do something') # another block of code 12 | finally: 13 | print('tear things down') # finalization code 14 | 15 | # Here, "set things up" could be opening a file, or acquiring some external 16 | # resource, and "tear things down" would then be closing the file, or releasing 17 | # or removing the resource. The try-finally construct guarantees that the 18 | # "tear things down" part is always executed, even if the code that does the 19 | # work doesn’t finish. Perhaps the most common (and important) use of context 20 | # managers is to properly manage resources. The act of opening a file consumes 21 | # a resource (called a file descriptor), and this resource is limited by your 22 | # OS – there are a maximum number of files a process can have open at one time. 23 | # Using a context manager ensures these resources are released. 24 | 25 | with open('data/filename.txt', 'w') as fileobject: 26 | print('do something') 27 | 28 | # So the general translation is that there is a try and finally block running 29 | # behind the scenes. The example below demonstrates this method in another way. 30 | # It says try: __enter__() the block and when completed, finally: __exit__(). 31 | 32 | class controlled_execution: 33 | def __enter__(self): 34 | thing = 'set things up' 35 | print(thing) 36 | return thing 37 | def __exit__(self, type, value, traceback): 38 | print('tear things down') 39 | 40 | with controlled_execution() as thing: 41 | print('do something') 42 | 43 | # This is considered a context manager. A context manager is an object that 44 | # defines the runtime context to be established when executing a with statement. 45 | # The context manager handles the entry into, and the exit from, the desired 46 | # runtime context for the execution of the block of code. Context managers are 47 | # normally invoked using the with statement. Typical uses of context managers 48 | # include saving and restoring various kinds of global state, locking and 49 | # unlocking resources, closing opened files, etc. 50 | 51 | # As above, you can implement a context manager as a class. At the very least 52 | # a context manager has __enter__ and __exit__ methods defined. 53 | 54 | # Note the 3 arguments in __exit__(). In a normal situation, these are all 55 | # given a value of None. However, if an exception occurs inside the with block, 56 | # they will be set to values related to the type, value, and traceback for the 57 | # exception. This allows the __exit__ method to do any cleanup code that may 58 | # be required, even if an exception occurred. 59 | 60 | # Here's another example of a context manager that will take a sequence 61 | # of random ascii characters and convert it to string. 62 | 63 | import random 64 | import string 65 | 66 | class StringJoiner(list): 67 | def __enter__(self): 68 | return self 69 | def __exit__(self, type, value, traceback): 70 | self.result = ''.join(self) 71 | 72 | with StringJoiner() as j: 73 | for i in range(15): 74 | j.append(random.choice(string.ascii_letters + string.digits)) 75 | 76 | print(j) 77 | # ['T', 'h', '9', 'M', 'F', 'v', 'y', 'Q', 'A', 'b', '6', 'n', 'r', 'x', 'i'] 78 | 79 | print(j.result) 80 | # Th9MFvyQAb6nrxi 81 | 82 | # Here's an example of a file opening context manager: 83 | 84 | class File(): 85 | def __init__(self, filename, method): 86 | self.file_obj = open(filename, method) 87 | 88 | def __enter__(self): 89 | return self.file_obj 90 | 91 | def __exit__(self, type, value, traceback): 92 | self.file_obj.close() 93 | return True # if you want exceptions to be ignored 94 | return False # if you want to pass the exception up the line 95 | 96 | # By defining __enter__() and __exit__() methods we can use this object 97 | # in a with statement: 98 | 99 | with File('data/test.txt', 'w') as opened_file: 100 | opened_file.write('Hola!') 101 | 102 | 103 | # @contextmanager - example 1 104 | # ----------------------------------------------------------------------------- 105 | # You can also construct your own context managers using the contextmanager 106 | # decorator from contextlib. In this case it's the 'yield' keyword that 107 | # separates the enter and exit instructions. 108 | 109 | from contextlib import contextmanager 110 | 111 | @contextmanager 112 | def html_element(name): 113 | print("<{}>".format(name), end='') 114 | yield 115 | print("".format(name)) 116 | 117 | with html_element("h1"): 118 | print("Heading", end='') #

Heading

119 | 120 | 121 | # @contextmanager - example 2 122 | # ----------------------------------------------------------------------------- 123 | # This could be used when you have to change the current directory temporarily 124 | # and then return to where you were: 125 | 126 | from contextlib import contextmanager 127 | import os 128 | 129 | @contextmanager 130 | def working_directory(path): 131 | current_dir = os.getcwd() 132 | os.chdir(path) 133 | try: 134 | yield 135 | finally: 136 | os.chdir(current_dir) 137 | 138 | with working_directory("data") as wd: 139 | pass # do something within data 140 | 141 | # Then you'll be back again in the original working directory 142 | 143 | 144 | # @contextmanager - example 3 145 | # ----------------------------------------------------------------------------- 146 | # This example creates a temporary folder and cleans it up when leaving 147 | # the context: 148 | 149 | from contextlib import contextmanager 150 | from tempfile import mkdtemp 151 | import shutil 152 | import time 153 | 154 | @contextmanager 155 | def temporary_dir(*args, **kwargs): 156 | name = mkdtemp(*args, **kwargs) 157 | try: 158 | yield name 159 | finally: 160 | shutil.rmtree(name) 161 | 162 | with temporary_dir() as dirname: 163 | time.sleep(10) 164 | pass # do whatever 165 | -------------------------------------------------------------------------------- /data/NEO_historical.csv: -------------------------------------------------------------------------------- 1 | ,close,high,low,open,time 2 | 0,35.03,36.67,34.35,35.82,1512172800 3 | 1,36.61,39.6,34.84,35.13,1512259200 4 | 2,41.84,43.19,36.55,36.59,1512345600 5 | 3,38.43,42.82,38.25,41.86,1512432000 6 | 4,34.79,38.73,33.92,38.42,1512518400 7 | 5,34.02,36.87,31.75,34.78,1512604800 8 | 6,35.24,36.48,31.73,34.03,1512691200 9 | 7,35.12,38.11,33.82,35.23,1512777600 10 | 8,32.78,35.17,31.56,35.12,1512864000 11 | 9,35.6,36.2,32.76,32.76,1512950400 12 | 10,37.05,37.6,35.06,35.6,1513036800 13 | -------------------------------------------------------------------------------- /data/accounts1.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/accounts1.sqlite -------------------------------------------------------------------------------- /data/accounts2.sqlite: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/accounts2.sqlite -------------------------------------------------------------------------------- /data/backups/test.txt: -------------------------------------------------------------------------------- 1 | File created. 2 | -------------------------------------------------------------------------------- /data/bohemian_rhapsody_lyrics.txt: -------------------------------------------------------------------------------- 1 | Bohemian Rhapsody 2 | Queen 3 | 4 | Is this the real life? 5 | Is this just fantasy? 6 | Caught in a landslide 7 | No escape from reality 8 | Open your eyes 9 | Look up to the skies and see 10 | I'm just a poor boy, I need no sympathy 11 | Because I'm easy come, easy go 12 | A little high, little low 13 | Anyway the wind blows, doesn't really matter to me, to me 14 | Mama, just killed a man 15 | Put a gun against his head 16 | Pulled my trigger, now he's dead 17 | Mama, life had just begun 18 | But now I've gone and thrown it all away 19 | Mama, ooo 20 | Didn't mean to make you cry 21 | If I'm not back again this time tomorrow 22 | Carry on, carry on, as if nothing really matters 23 | Too late, my time has come 24 | Sends shivers down my spine 25 | Body's aching all the time 26 | Goodbye everybody I've got to go 27 | Gotta leave you all behind and face the truth 28 | Mama, ooo (anyway the wind blows) 29 | I don't want to die 30 | I sometimes wish I'd never been born at all 31 | I see a little silhouetto of a man 32 | Scaramouch, scaramouch will you do the fandango 33 | Thunderbolt and lightning very very frightening me 34 | Gallileo, Gallileo, 35 | Gallileo, Gallileo, 36 | Gallileo Figaro - magnifico 37 | But I'm just a poor boy and nobody loves me 38 | He's just a poor boy from a poor family 39 | Spare him his life from this monstrosity 40 | Easy come easy go will you let me go 41 | Bismillah! No we will not let you go - let him go 42 | Bismillah! We will not let you go - let him go 43 | Bismillah! We will not let you go let me go 44 | Will not let you go let me go (never) 45 | Never let you go let me go 46 | Never let me go ooo 47 | No, no, no, no, no, no, no 48 | Oh mama mia, mama mia, mama mia let me go 49 | Beelzebub has a devil put aside for me 50 | For me 51 | For me 52 | So you think you can stone me and spit in my eye 53 | So you think you can love me and leave me to die 54 | Oh baby, can't do this to me baby 55 | Just gotta get out just gotta get right outta here 56 | Ooh yeah, ooh yeah 57 | Nothing really matters 58 | Anyone can see 59 | Nothing really matters nothing really matters to me 60 | Anyway the wind blows 61 | -------------------------------------------------------------------------------- /data/cities.csv: -------------------------------------------------------------------------------- 1 | France, Paris 2 | venuzuela,caracas 3 | LithuniA,vilnius 4 | argentina,buenos aires 5 | bolivia,la paz 6 | brazil,brasilia 7 | chile,santiago 8 | colombia,Bogotá 9 | ecuador,quito 10 | falkland islands,stanley 11 | french guiana,cayenne 12 | guyana,georgetown 13 | paraguay,Asunción 14 | peru,lima 15 | suriname,paramaribo 16 | uruguay,montevideo 17 | venezuela,caracas 18 | quit 19 | -------------------------------------------------------------------------------- /data/definitions: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/definitions -------------------------------------------------------------------------------- /data/example.txt: -------------------------------------------------------------------------------- 1 | This is 2 | filler text 3 | for a demo of list and generator comprehensions 4 | see comprehensions.py 5 | ----- 6 | -------------------------------------------------------------------------------- /data/filename.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/filename.txt -------------------------------------------------------------------------------- /data/img/bokeh-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/bokeh-example.png -------------------------------------------------------------------------------- /data/img/folium-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/folium-example.png -------------------------------------------------------------------------------- /data/img/matplotlib-csv-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/matplotlib-csv-example.png -------------------------------------------------------------------------------- /data/img/matplotlib-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/matplotlib-example.png -------------------------------------------------------------------------------- /data/img/matplotlib-examples.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/matplotlib-examples.png -------------------------------------------------------------------------------- /data/img/pygal-example-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/pygal-example-1.png -------------------------------------------------------------------------------- /data/img/pygal-example-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/pygal-example-2.png -------------------------------------------------------------------------------- /data/img/pygal-intro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/pygal-intro.png -------------------------------------------------------------------------------- /data/img/pygal-json-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/pygal-json-example.png -------------------------------------------------------------------------------- /data/img/tkinter-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/tkinter-example.png -------------------------------------------------------------------------------- /data/img/tkinter-module.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/img/tkinter-module.png -------------------------------------------------------------------------------- /data/menu.json: -------------------------------------------------------------------------------- 1 | {"breakfast": {"hours": "7-11", "items": {"breakfast burritos": "$6.00", "pancakes": "$4.00", "bagel": "$2.50"}}, "lunch": {"hours": "11-3", "items": {"sandwich": "$8.00"}}, "dinner": {"hours": "3-10", "items": {"spaghetti": "$9.00"}}} -------------------------------------------------------------------------------- /data/music.pickle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/music.pickle -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/1 - Sgt. Pepper's Lonely Hearts Club Band.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/1 - Sgt. Pepper's Lonely Hearts Club Band. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/10 - Lovely Rita.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/10 - Lovely Rita. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/11 - Good Morning Good Morning.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/11 - Good Morning Good Morning. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/12 - Sgt. Pepper's Lonely Hearts Club Band (reprise).emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/12 - Sgt. Pepper's Lonely Hearts Club Band (reprise). -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/13 - A Day In The Life.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/13 - A Day In The Life. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/2 - With A Little Help From My Friends.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/2 - With A Little Help From My Friends. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/3 - Lucy In The Sky With Diamonds.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/3 - Lucy In The Sky With Diamonds. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/4 - Getting Better.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/4 - Getting Better. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/5 - Fixing A Hole.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/5 - Fixing A Hole. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/6 - She's Leaving Home.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/6 - She's Leaving Home. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/7 - Being For The Benefit Of Mr. Kite.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/7 - Being For The Benefit Of Mr. Kite. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/8 - Within You Without You.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/8 - Within You Without You. -------------------------------------------------------------------------------- /data/music/Beatles/Sgt. Pepper's Lonely Hearts Club Band/9 - When I'm Sixty-Four.emp3: -------------------------------------------------------------------------------- 1 | Beatles/Sgt. Pepper's Lonely Hearts Club Band/9 - When I'm Sixty-Four. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/1 - Fallen Angel.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/1 - Fallen Angel. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/10 - Draw The Line.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/10 - Draw The Line. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/11 - U.S. Maid.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/11 - U.S. Maid. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/12 - Let It Go.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/12 - Let It Go. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/13 - Walk It.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/13 - Walk It. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/14 - Man O'Means.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/14 - Man O'Means. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/15 - Monkey Business.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/15 - Monkey Business. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/16 - My Darlene.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/16 - My Darlene. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/2 - Black Sheep.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/2 - Black Sheep. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/3 - Action.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/3 - Action. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/4 - Ball & Chain.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/4 - Ball & Chain. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/5 - Slip Away.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/5 - Slip Away. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/6 - Long Time Coming.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/6 - Long Time Coming. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/7 - Spinnin' Your Wheels.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/7 - Spinnin' Your Wheels. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/8 - Don't Understand.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/8 - Don't Understand. -------------------------------------------------------------------------------- /data/music/Bernie Torme/Demolition Ball/9 - Industry.emp3: -------------------------------------------------------------------------------- 1 | Bernie Torme/Demolition Ball/9 - Industry. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/1 - She Cries Your Name.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/1 - She Cries Your Name. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/10 - I Wish I Never Saw The Sunshine.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/10 - I Wish I Never Saw The Sunshine. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/11 - Galaxy Of Emptiness.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/11 - Galaxy Of Emptiness. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/2 - Tangent.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/2 - Tangent. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/3 - Don't Need A Reason.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/3 - Don't Need A Reason. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/4 - Live As You Dream.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/4 - Live As You Dream. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/5 - Sugar Boy.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/5 - Sugar Boy. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/6 - Touch Me With Your Love.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/6 - Touch Me With Your Love. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/7 - Whenever.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/7 - Whenever. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/8 - How Far.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/8 - How Far. -------------------------------------------------------------------------------- /data/music/Beth Orton/Trailer Park/9 - Someone's Daughter.emp3: -------------------------------------------------------------------------------- 1 | Beth Orton/Trailer Park/9 - Someone's Daughter. -------------------------------------------------------------------------------- /data/my_files/test.txt: -------------------------------------------------------------------------------- 1 | File created. 2 | -------------------------------------------------------------------------------- /data/numbers.json: -------------------------------------------------------------------------------- 1 | [[4, 5, 7, 9, 11, 13], "j"] -------------------------------------------------------------------------------- /data/pizzas: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/pizzas -------------------------------------------------------------------------------- /data/plants_shelf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/plants_shelf -------------------------------------------------------------------------------- /data/practice.cfg: -------------------------------------------------------------------------------- 1 | [english] 2 | greeting = Hello 3 | [french] 4 | greeting = Bonjour 5 | [files] 6 | home = /usr/local 7 | # simple interpolation: 8 | bin = %(home)s/bin 9 | -------------------------------------------------------------------------------- /data/practice.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | breakfast burritos 5 | pancakes 6 | 7 | 8 | hamburger 9 | 10 | 11 | spaghetti 12 | 13 | 14 | -------------------------------------------------------------------------------- /data/practice.yaml: -------------------------------------------------------------------------------- 1 | name: 2 | first: James 3 | last: McIntyre 4 | dates: 5 | birth: 1828-05-25 6 | death: 1906-03-31 7 | details: 8 | bearded: true 9 | themes: [cheese, Canada] 10 | books: 11 | url: http://www.gutenberg.org/files/36068/36068-h/36068-h.htm 12 | poems: 13 | - title: 'Motto' 14 | text: | 15 | Politeness, perseverance and pluck, 16 | To their possessor will bring good luck. 17 | - title: 'Canadian Charms' 18 | text: | 19 | Here industry is not in vain, 20 | For we have bounteous crops of grain, 21 | And you behold on every field 22 | Of grass and roots abundant yield, 23 | But after all the greatest charm 24 | Is the snug home upon the farm, 25 | And stone walls now keep cattle warm. 26 | -------------------------------------------------------------------------------- /data/practice1.txt: -------------------------------------------------------------------------------- 1 | File created. 2 | -------------------------------------------------------------------------------- /data/practice2.txt: -------------------------------------------------------------------------------- 1 | File created. 2 | -------------------------------------------------------------------------------- /data/singers.csv: -------------------------------------------------------------------------------- 1 | first,last 2 | Jack,White 3 | Eddie,Vedder 4 | David,Bowie 5 | Josh,Homme 6 | Annie,Lennox 7 | -------------------------------------------------------------------------------- /data/supermarkets-commas.txt: -------------------------------------------------------------------------------- 1 | ID,Address,City,State,Country,Name,Employees 2 | 1,3666 21st St,San Francisco,CA 94114,USA, Madeira,8 3 | 2,735 Dolores St,San Francisco,CA 94119,USA,Bready Shop,15 4 | 3,332 Hill St,San Francisco,California 94114,USA,Super River,25 5 | 4,3995 23rd St,San Francisco,CA 94114,USA,Ben's Shop,10 6 | 5,1056 Sanchez St,San Francisco,California,USA,Sanchez,12 7 | 6,551 Alvarado St,San Francisco,CA 94114,USA,Richvalley,20 8 | -------------------------------------------------------------------------------- /data/supermarkets-semicolons.txt: -------------------------------------------------------------------------------- 1 | ID;Address;City;State;Country;Name;Employees 2 | 1;3666 21st St;San Francisco;CA 94114;USA;Madeira;8 3 | 2;735 Dolores St;San Francisco;CA 94119;USA;Bready Shop;15 4 | 3;332 Hill St;San Francisco;California 94114;USA; Super River;25 5 | 4;3995 23rd St;San Francisco;CA 94114;USA;Ben's Shop;10 6 | 5;1056 Sanchez St;San Francisco;California;USA;Sanchez;12 7 | 6;551 Alvarado St;San Francisco;CA 94114;USA;Richvalley;20 8 | -------------------------------------------------------------------------------- /data/supermarkets.csv: -------------------------------------------------------------------------------- 1 | ID,Address,City,State,Country,Name,Employees 2 | 1,3666 21st St,San Francisco,CA 94114,USA,Madeira,8 3 | 2,735 Dolores St,San Francisco,CA 94119,USA,Bready Shop,15 4 | 3,332 Hill St,San Francisco,California 94114,USA,Super River,25 5 | 4,3995 23rd St,San Francisco,CA 94114,USA,Ben's Shop,10 6 | 5,1056 Sanchez St,San Francisco,California,USA,Sanchez,12 7 | 6,551 Alvarado St,San Francisco,CA 94114,USA,Richvalley,20 8 | -------------------------------------------------------------------------------- /data/supermarkets.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "ID": 1, 4 | "Address": "3666 21st St", 5 | "City": "San Francisco", 6 | "State": "CA 94114", 7 | "Country": "USA", 8 | "Name": "Madeira", 9 | "Employees": 8 10 | }, 11 | { 12 | "ID": 2, 13 | "Address": "735 Dolores St", 14 | "City": "San Francisco", 15 | "State": "CA 94119", 16 | "Country": "USA", 17 | "Name": "Bready Shop", 18 | "Employees": 15 19 | }, 20 | { 21 | "ID": 3, 22 | "Address": "332 Hill St", 23 | "City": "San Francisco", 24 | "State": "California 94114", 25 | "Country": "USA", 26 | "Name": "Super River", 27 | "Employees": 25 28 | }, 29 | { 30 | "ID": 4, 31 | "Address": "3995 23rd St", 32 | "City": "San Francisco", 33 | "State": "CA 94114", 34 | "Country": "USA", 35 | "Name": "Ben's Shop", 36 | "Employees": 10 37 | }, 38 | { 39 | "ID": 5, 40 | "Address": "1056 Sanchez St", 41 | "City": "San Francisco", 42 | "State": "California", 43 | "Country": "USA", 44 | "Name": "Sanchez", 45 | "Employees": 12 46 | }, 47 | { 48 | "ID": 6, 49 | "Address": "551 Alvarado St", 50 | "City": "San Francisco", 51 | "State": "CA 94114", 52 | "Country": "USA", 53 | "Name": "Richvalley", 54 | "Employees": 20 55 | } 56 | ] -------------------------------------------------------------------------------- /data/supermarkets.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/supermarkets.xlsx -------------------------------------------------------------------------------- /data/test.log: -------------------------------------------------------------------------------- 1 | 2019-07-16 12:05:51,303 DEBUG 23 debug message 2 | 2019-07-16 12:05:51,303 INFO 24 info message 3 | 2019-07-16 12:05:51,304 WARNING 25 warning message 4 | 2019-07-16 12:05:51,304 ERROR 26 error message 5 | 2019-07-16 12:05:51,304 CRITICAL 27 critical message 6 | 2019-07-16 12:05:51,304 DEBUG 48 here is my debug message 7 | 2019-07-16 12:38:03,511 DEBUG 23 debug message 8 | 2019-07-16 12:38:03,511 INFO 24 info message 9 | 2019-07-16 12:38:03,511 WARNING 25 warning message 10 | 2019-07-16 12:38:03,512 ERROR 26 error message 11 | 2019-07-16 12:38:03,512 CRITICAL 27 critical message 12 | 2019-07-16 12:38:03,512 DEBUG 48 here is my debug message 13 | 2019-07-16 12:45:12,277 DEBUG 23 debug message 14 | 2019-07-16 12:45:12,277 INFO 24 info message 15 | 2019-07-16 12:45:12,277 WARNING 25 warning message 16 | 2019-07-16 12:45:12,277 ERROR 26 error message 17 | 2019-07-16 12:45:12,277 CRITICAL 27 critical message 18 | 2019-07-16 12:45:12,278 DEBUG 48 here is my debug message 19 | 2019-07-16 12:45:12,278 SPECIAL_INFO 83 Hey, this is special 20 | -------------------------------------------------------------------------------- /data/test.txt: -------------------------------------------------------------------------------- 1 | Hola! -------------------------------------------------------------------------------- /data/verlegenhuken.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/data/verlegenhuken.xlsx -------------------------------------------------------------------------------- /data_types.py: -------------------------------------------------------------------------------- 1 | '''Functions related to data types & structures''' 2 | 3 | 4 | bool(1) # -> True 5 | float(True) # -> 1.0 6 | int(True) # -> 1 7 | str(True) # -> True 8 | list((1,2,3)) # -> [1, 2, 3] 9 | dict((('a', 'A'), ('b', 'B'), ('c', 'C'))) # -> {'c':'C', 'a':'A', 'b':'B'} 10 | set(['a', 'b', 'c']) # -> {'b', 'c', 'a'} 11 | frozenset(['a', 'b', 'c']) # -> {'c', 'b', 'a'} 12 | tuple(['a', 'b', 'c']) # -> ('a', 'b', 'c') 13 | 14 | -------------------------------------------------------------------------------- /data_visualization.md: -------------------------------------------------------------------------------- 1 | # Python Data Visualization 2 | 3 | ## Table of Contents 4 | 5 | 6 | 7 | - [pandas](#pandas) 8 | - [Matplotlib](#matplotlib) 9 | - [Pygal](#pygal) 10 | - [Bokeh](#bokeh) 11 | - [Folium](#folium) 12 | - [Plotly](#plotly) 13 | - [ggplot](#ggplot) 14 | - [VisPy](#vispy) 15 | - [Altair](#altair) 16 | - [Data Sources](#data-sources) 17 | 18 | 19 | 20 | ## pandas 21 | 22 | > [pandas](https://pandas.pydata.org/) is a fast, powerful, flexible and easy to use open source data analysis and manipulation tool, built on top of the Python programming language. 23 | 24 | See [pandas_data_analysis.py](pandas_data_analysis.py) 25 | 26 | 27 | ## Matplotlib 28 | 29 | > [Matplotlib](https://matplotlib.org/) is a comprehensive library for creating static, animated, and interactive visualizations in Python. 30 | 31 | See [matplotlib_intro.py](matplotlib_intro.py) 32 | See [matplotlib_example.py](matplotlib_example.py) 33 | See [matplotlib_csv_example.py](matplotlib_csv_example.py) 34 | 35 | 36 | ## Pygal 37 | 38 | [Pygal](http://www.pygal.org/en/stable/) creates interactive SVG charts using python. 39 | 40 | See [pygal_intro.py](pygal_intro.py) 41 | See [pygal_json_example.py](pygal_json_example.py) 42 | See [pygal_github_api_example.py]([pygal_github_api_example.py]) 43 | See [pygal_hn_api_example.py](pygal_hn_api_example.py) 44 | 45 | 46 | ## Bokeh 47 | 48 | > [Bokeh](https://docs.bokeh.org/en/latest/index.html) is an interactive visualization library for modern web browsers. It provides elegant, concise construction of versatile graphics, and affords high-performance interactivity over large or streaming datasets. Bokeh can help anyone who would like to quickly and easily make interactive plots, dashboards, and data applications. 49 | 50 | See [bokeh_example.py](bokeh_example.py) 51 | 52 | 53 | ## Folium 54 | 55 | > [folium](https://python-visualization.github.io/folium/) builds on the data wrangling strengths of the Python ecosystem and the mapping strengths of the leaflet.js library. Manipulate your data in Python, then visualize it in on a Leaflet map via folium. 56 | 57 | See [folium_webmaps.py](folium_webmaps.py) 58 | 59 | 60 | ## Plotly 61 | 62 | [Plotly](https://plotly.com/python/) is an open source python graphing library 63 | 64 | 65 | ## ggplot 66 | 67 | > [ggplot](http://ggplot.yhathq.com/) is a plotting system for Python based on R's ggplot2 and the Grammar of Graphics. It is built for making professional looking, plots quickly with minimal code. 68 | 69 | 70 | ## VisPy 71 | 72 | > [VisPy](http://vispy.org/) VisPy is a Python library for interactive scientific visualization that is designed to be fast, scalable, and easy to use. 73 | 74 | ## Altair 75 | 76 | > [Altair](https://altair-viz.github.io/) is a declarative statistical visualization library for Python, based on Vega and Vega-Lite, and the source is available on GitHub. 77 | 78 | 79 | ## Data Sources 80 | 81 | - [Top 8 Weather APIs](https://www.climacell.co/blog/top-8-weather-apis-for-2020/) 82 | - [Historical weather data - Weather Underground](https://www.wunderground.com/history/) 83 | - [National Centres for Environmental Information](https://www.ncdc.noaa.gov/data-access/quick-links) 84 | - [Historical Climate Data - Government of Canada](http://climate.weather.gc.ca/index_e.html) 85 | -------------------------------------------------------------------------------- /dataclasses_module.py: -------------------------------------------------------------------------------- 1 | '''Data Classes''' 2 | 3 | 4 | # https://docs.python.org/3.7/library/dataclasses.html#module-dataclasses 5 | 6 | # Python 3.7 introduced a new module: dataclasses.py. The new dataclass() 7 | # decorator provides a way to declare data classes. A data class is a class 8 | # that typically contains mainly data. When you use it, the class constructor 9 | # (__init__ method) and other magic methods, such as __repr__(), __eq__(), and 10 | # __hash__() are generated automatically. 11 | 12 | # When using this decorator, you need to describe the attributes using 13 | # class variable annotations - type hints (introduced in Python 3.6). 14 | 15 | # Example: 16 | 17 | from dataclasses import dataclass 18 | 19 | @dataclass 20 | class Point(): 21 | x: float 22 | y: float 23 | z: float = 0.0 24 | 25 | p = Point(1.5, 2.5) 26 | print(p) # Point(x=1.5, y=2.5, z=0.0) 27 | 28 | 29 | # Annotationg varibales 30 | # ------------------------------------------------------------------------------ 31 | # Python 3.6 introduced a syntax for annotating variables in PEP 526. 32 | # Here's a quick review. For more see documenting_naming.py 33 | 34 | # For simple built-in types: 35 | x: int = 1 36 | x: float = 1.0 37 | x: bool = True 38 | x: str = 'test' 39 | x: bytes = b'test' 40 | 41 | from typing import List, Set, Dict, Tuple, Optional 42 | 43 | x: List[int] = [1] 44 | x: Set[int] = {6, 7} 45 | x: Dict[str, float] = {'field': 2.0} 46 | x: Tuple[int, str, float] = (3, "yes", 7.5) 47 | # x: Optional[str] = some_function() 48 | 49 | 50 | # dataclass() parameters 51 | # ------------------------------------------------------------------------------ 52 | # The dataclass decorator has some default parameters: 53 | 54 | @dataclass(init=True, repr=True, eq=True, order=False, unsafe_hash=False, frozen=False) 55 | class Example(): 56 | pass 57 | 58 | # init: If true (the default), a __init__() method will be generated. If the 59 | # class already defines __init__(), this parameter is ignored. 60 | 61 | # repr: If true (the default), a __repr__() method will be generated. The 62 | # generated string will have the class name and the name and repr of each field, 63 | # in the order they are defined in the class. Fields can be marked to be excluded 64 | # from the repr. If the class defines __repr__(), this parameter is ignored. 65 | 66 | # eq: If true (the default), an __eq__() method will be generated. This method 67 | # compares the class as if it were a tuple of its fields, in order. Both 68 | # instances in the comparison must be of the identical type. If the class 69 | # already defines __eq__(), this parameter is ignored. 70 | 71 | # order: If true (the default is False), __lt__(), __le__(), __gt__(), and 72 | # __ge__() methods will be generated. Works the same as eq. Note if order is 73 | # true and eq is false, a ValueError is raised. 74 | 75 | # unsafe_hash: If False (the default), a __hash__() method is generated 76 | # according to how eq and frozen are set. __hash__() is used by built-in hash(), 77 | # and when objects are added to hashed collections such as dictionaries and sets. 78 | # Having a __hash__() implies that instances of the class are immutable. For an 79 | # in depth explanation see the docs. 80 | 81 | # frozen: If true (the default is False), assigning to fields will generate an 82 | # exception. This emulates read-only frozen instances. If __setattr__() or 83 | # __delattr__() is defined in the class, then a TypeError is raised. 84 | 85 | 86 | # field() 87 | # ------------------------------------------------------------------------------ 88 | # This method allows us to add more information to a particular attribute. 89 | # The parameters to field() are: 90 | 91 | # default: If provided, this will be the default value for this field. This is 92 | # needed because the field() call itself replaces the normal position where the 93 | # default value would go. 94 | 95 | # default_factory: If provided, it must be a zero-argument callable that will 96 | # be called when a default value is needed for this field. Among other purposes, 97 | # this can be used to specify fields with mutable default values. 98 | 99 | # init: If true (the default), this field is included as a parameter to the 100 | # generated __init__() method. 101 | 102 | # repr: If true (the default), this field is included in the string returned 103 | # by the generated __repr__() method. 104 | 105 | # compare: If true (the default), this field is included in the generated 106 | # equality and comparison methods (__eq__(), __gt__(), et al.). 107 | 108 | from dataclasses import field 109 | 110 | @dataclass 111 | class InventoryItem(): 112 | '''Class representing an item in inventory.''' 113 | name: str 114 | price: float 115 | rating: int = field(repr=False) 116 | quantity: int = 0 117 | min: int = field(repr=False, default=10) 118 | mylist: List[int] = field(default_factory=list) 119 | 120 | def total_value(self): 121 | return self.price * self.quantity 122 | 123 | 124 | i = InventoryItem('pencils', 0.5, 5, 100) 125 | i.mylist += [10] 126 | i.mylist += [20, 2] 127 | 128 | print(i) 129 | # InventoryItem(name='pencils', price=0.5, quantity=100, mylist=[10, 20, 2]) 130 | print(i.total_value()) 131 | # 50.0 132 | print(i.rating) 133 | # 5 134 | 135 | 136 | # fields(), asdict(), astuple(), is_dataclass() 137 | # ------------------------------------------------------------------------------ 138 | 139 | from dataclasses import fields, asdict, astuple, is_dataclass 140 | from pprint import pprint 141 | 142 | # This method returns a tuple of field objects for a given dataclass. 143 | pprint(fields(i)) 144 | #TL;DR 145 | 146 | # This method returns a dict of fields for a given dataclass. 147 | pprint(asdict(i)) 148 | # {'min': 10, 149 | # 'mylist': [10, 20, 2], 150 | # 'name': 'pencils', 151 | # 'price': 0.5, 152 | # 'quantity': 100, 153 | # 'rating': 5} 154 | 155 | # This method returns a tuple of fields for a given dataclass. 156 | pprint(astuple(i)) 157 | # ('pencils', 0.5, 5, 100, 10, [10, 20, 2]) 158 | 159 | # This method returns True if the given arg is a dataclass or instance of one. 160 | pprint(is_dataclass(i)) 161 | # True 162 | -------------------------------------------------------------------------------- /date_time_examples.py: -------------------------------------------------------------------------------- 1 | '''datetime Examples''' 2 | 3 | 4 | # Compare usages 5 | # ----------------------------------------------------------------------------- 6 | 7 | import datetime 8 | import pytz 9 | 10 | now = datetime.datetime.now() 11 | # 2017-10-02 16:03:01.558993 12 | 13 | utc = datetime.datetime.utcnow() 14 | # 2017-10-02 23:03:01.559071 15 | 16 | the_time = pytz.utc.localize(datetime.datetime.utcnow()) 17 | # 2017-10-02 23:03:01.559084+00:00 18 | 19 | the_local_time = the_time.astimezone() 20 | # 2017-10-02 16:03:01.559084-07:00 21 | 22 | the_local_time = pytz.utc.localize(datetime.datetime.utcnow()).astimezone() 23 | # 2017-10-02 16:03:01.559084-07:00 24 | 25 | timezone = the_local_time.tzinfo 26 | # PDT 27 | 28 | formatted = datetime.datetime.now().strftime('%m-%d-%Y, %H:%M %p') 29 | # 12-31-2017, 10:47 AM 30 | 31 | timestamp = datetime.datetime.now().strftime('%a %x') 32 | # Mon 10/02/17 33 | 34 | backdated = datetime.date(2017, 8, 7).strftime('%a %x') 35 | # Mon 08/07/17 36 | 37 | print(f'{now.month}/{now.day}/{now.year}') 38 | # 1/5/2022 39 | 40 | 41 | # timedelta example 42 | # ----------------------------------------------------------------------------- 43 | # The following function will output a plant watering schedule using the 44 | # datetime timedelta method. 45 | 46 | from datetime import date 47 | from datetime import timedelta 48 | 49 | def watering_schedule(x): 50 | '''Calculate a plant watering schedule for the next 'x' waterings''' 51 | # categorize each month into seasons: 52 | winter_months = [1, 2, 3, 12] 53 | summer_months = [6, 7, 8, 9] 54 | # watering frequency every n days: 55 | winter_freq = 6 56 | spring_fall_freq = 5 57 | summer_freq = 4 58 | # check the month and generate schedule: 59 | now = date.today() 60 | schedule = [] 61 | while len(schedule) < x: 62 | if now.month in summer_months: 63 | now += timedelta(days=summer_freq) 64 | schedule.append(now) 65 | elif now.month in winter_months: 66 | now += timedelta(days=winter_freq) 67 | schedule.append(now) 68 | else: 69 | now += timedelta(days=spring_fall_freq) 70 | schedule.append(now) 71 | print('Watering Schedule:') 72 | for i in schedule: 73 | print(i) 74 | 75 | watering_schedule(25) 76 | -------------------------------------------------------------------------------- /debugging.py: -------------------------------------------------------------------------------- 1 | '''Debugging''' 2 | 3 | 4 | # General Tips 5 | # ----------------------------------------------------------------------------- 6 | # Though not very advanced, dropping print() statements and other reflection 7 | # related functions can tell you a lot about what's going on. Beyond that: 8 | 9 | # print the values of your local arguments with: 10 | 11 | print(vars()) 12 | 13 | # If you run your program with an -i flag, it will drop you into the 14 | # interactive interpreter if the program fails: 15 | 16 | # $ python3 -i myfile.py 17 | 18 | 19 | # Standard Library: pdb module 20 | # ----------------------------------------------------------------------------- 21 | # Bug test: read a file of countries and their capital cities, separated by a 22 | # comma, and write them out as capital, country. In addition: 23 | # – Make sure they capitalized incorrectly 24 | # – Remove any extra spaces extra spaces 25 | # – Stop the program if we find the word quit (uppercase or lowercase) 26 | 27 | # Here's a sample data file data/cities.csv: 28 | ''' 29 | France, Paris 30 | venuzuela,caracas 31 | LithuniA,vilnius 32 | argentina,buenos aires 33 | bolivia,la paz 34 | brazil,brasilia 35 | chile,santiago 36 | colombia,Bogotá 37 | ecuador,quito 38 | falkland islands,stanley 39 | french guiana,cayenne 40 | guyana,georgetown 41 | paraguay,Asunción 42 | peru,lima 43 | suriname,paramaribo 44 | uruguay,montevideo 45 | venezuela,caracas 46 | quit 47 | ''' 48 | 49 | # Here's pseudocode: 50 | ''' 51 | for each line in the text file: 52 | read the line 53 | strip leading and trailing spaces 54 | if 'quit' occurs in a lowercase copy of the line: 55 | stop 56 | else: 57 | split the country and capital by the comma character 58 | trim any leading and trailing spaces 59 | convert the country and capital to titlecase 60 | print the capital, a comma, and the country 61 | ''' 62 | 63 | 64 | # Here's the actual code: 65 | def process_cities(filename): 66 | with open(filename, 'rt') as file: 67 | for line in file: 68 | line = line.strip() 69 | if 'quit' in line.lower(): # should be if 'quit' == line.lower(): 70 | return 71 | country, city = line.split(',') 72 | city = city.strip() 73 | country = country.strip() 74 | print(city.title(), country.title(), sep=',') 75 | 76 | 77 | # import pdb; pdb.set_trace() # this will launch the debugger 78 | breakpoint() # this will launch the debugger as of Python 3.7 79 | 80 | process_cities('data/cities.csv') 81 | 82 | 83 | # pdb Commands 84 | # ----------------------------------------------------------------------------- 85 | # Documented commands (type help ): 86 | 87 | # EOF c d h list q rv undisplay 88 | # a cl debug help ll quit s unt 89 | # alias clear disable ignore longlist r source until 90 | # args commands display interact n restart step up 91 | # b condition down j next return tbreak w 92 | # break cont enable jump p retval u whatis 93 | # bt continue exit l pp run unalias where 94 | 95 | # type 'c' to continue (the program will run until it ends normally or via 96 | # an Error/Exception). If it ends normally, we now it's logic error 97 | # rather than a syntax error. 98 | # type 's' single step through lines and into functions/methods 99 | # (this will include any modules you might be using like sys) 100 | # type 'n' to step past a function/method (execute and move onto the next). 101 | # Generally speaking you would 'n' to step past any library code. 102 | # type 'l' to see the next few lines at once 103 | # type 'll' to see even more of the next few lines at once 104 | # type 'l' and a line number to see lines from that point onward 105 | # type 'b' and a line number to insert a breakpoint 106 | # type 'b' alone to see all your breakpoints 107 | # type 'cl' and a number to (clear) a breakpoint 108 | 109 | # NOTE: Your program will ONLY stop at a breakpoint if it has a chance to 110 | # actually get to that spot in the code. 111 | 112 | # type 'u' to go up (back) 113 | # type 'p' to print the value of something. i.e. p line 114 | # type a variable name and it will output its current value 115 | # type '?' or help when in the pdb to see all the commands. 116 | # type help command to get help on that one. 117 | 118 | # NOTE: when viewing lines, 'B' indicates a breakpoint you've inserted and 119 | # '->' indicates your current line 120 | 121 | # To use the debugger from the command line, import the pdb module by typing: 122 | # $ python3 -m pdb myfile.py 123 | 124 | # For the example above if we drop a breakpoint at line 68 and continue our 125 | # program, when it stops if we type line to see the current value of line we 126 | # see our problem: ecuador,quito. 127 | 128 | 129 | # breakpoint(*args, **kws) 130 | # ------------------------------------------------------------------------------ 131 | # New to Python 3.7, this function drops you into the debugger where ever you 132 | # call it in your code. Specifically, it calls sys.breakpointhook(), passing 133 | # *args and **kws straight through. By default, sys.breakpointhook() calls 134 | # pdb.set_trace() expecting no arguments. It is purely a convenience function 135 | # so you don’t have to explicitly import pdb or type as much code to enter the 136 | # debugger. 137 | 138 | images = ['pickle.png', 'dog.jpg', 'car.png', 'apple.gif', 'baloon.psd'] 139 | todo = [] 140 | 141 | for i in images: 142 | if not i.endswith('.png'): 143 | breakpoint() 144 | todo.append(i) 145 | 146 | print(todo) 147 | -------------------------------------------------------------------------------- /decorators.py: -------------------------------------------------------------------------------- 1 | '''Decorators''' 2 | 3 | 4 | # A decorator is a function that takes one function as an input and returns 5 | # another function. Here is a simplified example: 6 | 7 | def my_decorator(func): 8 | def wrapper(arg): 9 | print('Before {}() is called.'.format(func.__name__)) 10 | func(arg) 11 | print('After {}() is called.'.format(func.__name__)) 12 | return wrapper 13 | 14 | @my_decorator 15 | def squares(n): 16 | '''returns the square of a number''' 17 | print(n ** 2) 18 | 19 | squares(6) 20 | # Before squares() is called. 21 | # 36 22 | # After squares() is called. 23 | 24 | 25 | # @functools.wraps(): 26 | # ----------------------------------------------------------------------------- 27 | # When you use a decorator, you're replacing one function (squares) with 28 | # another (wrapper). While inside the decorator we can see the original 29 | # function being used but now when outside, look what happens: 30 | 31 | print(squares.__name__) # wrapper 32 | print(squares.__doc__) # None 33 | 34 | # That's why we have functools.wraps. This takes a function used in a decorator 35 | # and adds the functionality of copying over the functions name, docstring, 36 | # arguments list, etc. See the difference: 37 | 38 | from functools import wraps 39 | 40 | def my_decorator(func): 41 | @wraps(func) # Add this, everything else is the same 42 | def wrapper(arg): 43 | print('Before {}() is called.'.format(func.__name__)) 44 | func(arg) 45 | print('After {}() is called.'.format(func.__name__)) 46 | return wrapper 47 | 48 | @my_decorator 49 | def squares(n): 50 | '''returns the square of a number''' 51 | print(n ** 2) 52 | 53 | print(squares.__name__) # squares 54 | print(squares.__doc__) # returns the square of a number 55 | 56 | 57 | # Returning results 58 | # ----------------------------------------------------------------------------- 59 | # The function document_it() below defines a decorator that will: 60 | # - print the functions name and values of its arguments 61 | # - run the function with the arguments 62 | # - print the result 63 | # - return the modified function for use 64 | 65 | def document_it(func): 66 | @wraps(func) 67 | def wrapper(*args, **kwargs): 68 | print('Running function:', func.__name__) 69 | print('Positional arguments:', args) 70 | print('Keyword arguments:', kwargs) 71 | result = func(*args, **kwargs) 72 | print('Result:', result) 73 | return result 74 | return wrapper 75 | 76 | # Here's our simple test function, first we'll run it without a decorator: 77 | def add_multiply(a, b, multiplyer=1): 78 | return (a + b) * multiplyer 79 | 80 | decorated_add_ints = document_it(add_multiply) 81 | decorated_add_ints(3, 5, multiplyer=100) 82 | # Running function: add_multiply 83 | # Positional arguments: (3, 5) 84 | # Keyword arguments: {'multiplyer': 100} 85 | # Result: 800 86 | 87 | # Now with a decorator... a little less code: 88 | @document_it 89 | def add_multiply(a, b, multiplyer=1): 90 | return (a + b) * multiplyer 91 | 92 | add_multiply(4, 5, multiplyer=100) 93 | # Running function: add_multiply 94 | # Positional arguments: (4, 5) 95 | # Keyword arguments: {'multiplyer': 100} 96 | # Result: 900 97 | 98 | 99 | # Multiple decorators 100 | # ----------------------------------------------------------------------------- 101 | 102 | def square_it(func): 103 | @wraps(func) 104 | def wrapper(*args, **kwargs): 105 | result = func(*args, **kwargs) 106 | return result * result 107 | return wrapper 108 | 109 | # The decorator that's closest to the actual function (above the def) runs 110 | # first and then the one above that will run. The results will be the same no 111 | # matter what order but the intermediate steps are different. Depending on what 112 | # your decorators do, the order may be important. 113 | 114 | @document_it 115 | @square_it 116 | def add_ints(a, b): 117 | return a + b 118 | 119 | print(add_ints(4, 3)) 120 | # Running function: add_ints 121 | # Positional arguments: (4, 3) 122 | # Keyword arguments: {} 123 | # Result: 49 124 | # 49 125 | 126 | @square_it 127 | @document_it 128 | def add_ints(a, b): 129 | return a + b 130 | 131 | print(add_ints(4, 3)) 132 | # Running function: add_ints 133 | # Positional arguments: (4, 3) 134 | # Keyword arguments: {} 135 | # Result: 7 136 | # 49 137 | 138 | 139 | # @property 140 | # ----------------------------------------------------------------------------- 141 | # see decorators used as getter and setter methods in classes.py 142 | 143 | 144 | # decorators with arguments 145 | # ----------------------------------------------------------------------------- 146 | # Consider this example. Imagine a number of functions that allow access to 147 | # different things, but you only want to allow access if some criteria is met. 148 | # Instead of repeating code in each function, you can set this up in a 149 | # decorator. The only problem is, for some reason you can't add parameters to 150 | # the decorator that has (func) passed in. So in order to do this, we have to 151 | # create another wrapping decorator :( 152 | 153 | def bouncer(flag): 154 | def my_decorator(func): 155 | @wraps(func) 156 | def wrapper(*args, **kwargs): 157 | if flag: 158 | func(*args, **kwargs) 159 | else: 160 | print('Not running the function') 161 | return wrapper 162 | return my_decorator 163 | 164 | flag = True 165 | 166 | @bouncer(flag) 167 | def my_function(): 168 | print('The function runs...') 169 | 170 | my_function() 171 | # The function runs... 172 | 173 | flag = False 174 | 175 | @bouncer(flag) 176 | def my_function(): 177 | print('The function runs...') 178 | 179 | my_function() 180 | # Not running the function 181 | 182 | 183 | # one last example: 184 | # ----------------------------------------------------------------------------- 185 | 186 | import time 187 | 188 | def timing(func): 189 | '''Outputs the time a function takes to execute.''' 190 | @wraps(func) 191 | def wrapper(*args, **kwargs): 192 | t1 = time.time() 193 | func(*args, **kwargs) 194 | t2 = time.time() 195 | return 'Time it took to run the function: ' + str((t2 - t1)) 196 | return wrapper 197 | 198 | @timing 199 | def my_function(): 200 | num_list = [] 201 | for num in (range(0, 10000)): 202 | num_list.append(num) 203 | 204 | print(my_function()) 205 | # Time it took to run the function: 0.0011439323425292969 206 | -------------------------------------------------------------------------------- /demos/redis_dryer.py: -------------------------------------------------------------------------------- 1 | # import redis 2 | # 3 | # conn = redis.Redis() 4 | # print('Dryer is starting') 5 | # 6 | # while True: 7 | # msg = conn.blpop('dishes') 8 | # if not msg: 9 | # break 10 | # val = msg[1].decode('utf-8') 11 | # if val == 'quit': 12 | # break 13 | # print('dried', val) 14 | # 15 | # print('Dryer is done') 16 | 17 | 18 | 19 | def dryer(): 20 | import redis 21 | import os 22 | import time 23 | conn = redis.Redis() 24 | pid = os.getpid() 25 | timeout = 20 26 | print('Dryer process {} is starting'.format(pid)) 27 | 28 | while True: 29 | msg = conn.blpop('dishes', timeout) 30 | if not msg: 31 | break 32 | val = msg[1].decode('utf-8') 33 | if val == 'quit': 34 | break 35 | print('{} dried {}'.format(pid, val)) 36 | time.sleep(0.1) 37 | print('dryer process {} is done'.format(pid)) 38 | 39 | import multiprocessing 40 | DRYERS = 3 41 | for num in range(DRYERS): 42 | p = multiprocessing.Process(target=dryer) 43 | p.start() 44 | -------------------------------------------------------------------------------- /demos/redis_pub.py: -------------------------------------------------------------------------------- 1 | import random 2 | import redis 3 | 4 | conn = redis.Redis() 5 | cats = ['siamese', 'black', 'persian', 'main coon', 'tabby', 'norwegian'] 6 | hats = ['bowler', 'fedora', 'top hat', 'poor boy', 'cowboy', 'stovepipe'] 7 | 8 | for i in range(10): 9 | cat = random.choice(cats) 10 | hat = random.choice(hats) 11 | print('Publish: {} cat wears a {}'.format(cat, hat)) 12 | conn.publish(cat, hat) 13 | -------------------------------------------------------------------------------- /demos/redis_sub.py: -------------------------------------------------------------------------------- 1 | import redis 2 | 3 | conn = redis.Redis() 4 | topics = ['siamese', 'black'] 5 | sub = conn.pubsub() 6 | sub.subscribe(topics) 7 | 8 | for msg in sub.listen(): 9 | if msg['type'] == 'message': 10 | cat = msg['channel'] 11 | hat = msg['data'] 12 | print('Subscribe: {} cat wears a {}'.format(cat, hat)) 13 | -------------------------------------------------------------------------------- /demos/redis_washer.py: -------------------------------------------------------------------------------- 1 | import redis 2 | 3 | conn = redis.Redis() 4 | print('Washer is starting') 5 | dishes = ['salad', 'bread', 'main', 'side', 'dessert'] 6 | 7 | for dish in dishes: 8 | msg = dish.encode('utf-8') 9 | conn.rpush('dishes', msg) 10 | print('washed', dish) 11 | 12 | conn.rpush('dishes', 'quit') 13 | print('Washer is done') 14 | -------------------------------------------------------------------------------- /demos/sample_plot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jessicarush/python-notes/124f21c82f2a249e0413a3c3e3c01ff8d7b46481/demos/sample_plot.png -------------------------------------------------------------------------------- /demos/tasks.py: -------------------------------------------------------------------------------- 1 | # This demo file goes with the instructions in queues.py 2 | 3 | import time 4 | 5 | def example(seconds): 6 | print('Starting task...') 7 | for i in range(seconds): 8 | print(i) 9 | time.sleep(1) 10 | print('Task completed.') -------------------------------------------------------------------------------- /demos/tasks_with_info.py: -------------------------------------------------------------------------------- 1 | # This demo file goes with the instructions in queues.py 2 | 3 | import time 4 | from rq import get_current_job 5 | 6 | def example(seconds): 7 | job = get_current_job() 8 | print('Starting task...') 9 | for i in range(seconds): 10 | job.meta['progress'] = 100.0 * i / seconds 11 | job.save_meta() 12 | print(i) 13 | time.sleep(1) 14 | job.meta['progress'] = 100 15 | job.save_meta() 16 | print('Task completed.') -------------------------------------------------------------------------------- /demos/tcp_client.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import socket 3 | 4 | 5 | address = ('localhost', 4544) 6 | max_size = 1000 7 | print('Starting the client at', datetime.datetime.now()) 8 | 9 | client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 10 | 11 | # The connect() call sets up a stream 12 | client.connect(address) 13 | client.sendall(b'Hi there') 14 | data = client.recv(max_size) 15 | print('Message:', datetime.datetime.now(), 'someone replied', data) 16 | 17 | client.close() 18 | -------------------------------------------------------------------------------- /demos/tcp_server.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import socket 3 | 4 | 5 | address = ('localhost', 4544) 6 | max_size = 1000 # bytes 7 | print('Starting the server at', datetime.datetime.now()) 8 | print('waiting for client to call') 9 | 10 | # SOCK_DGRAM is replaced with SOCK_STREAM to use the streaming TCP protocol: 11 | server = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 12 | server.bind(address) 13 | 14 | # This queues up to 5 client connections before refusing new ones: 15 | server.listen(5) 16 | 17 | # TCP gets the first available message as it arrives: 18 | client, addr = server.accept() 19 | data = client.recv(max_size) 20 | print('Message:', datetime.datetime.now(), client, 'said', data) 21 | 22 | client.sendall(b'Are you talking to me?') 23 | client.close() 24 | server.close() 25 | -------------------------------------------------------------------------------- /demos/udp_client.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import socket 3 | 4 | 5 | server_address = ('localhost', 4544) 6 | max_size = 4096 7 | print('Starting the client at', datetime.datetime.now()) 8 | 9 | client = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 10 | 11 | client.sendto(b'Hello', server_address) 12 | 13 | data, server = client.recvfrom(max_size) 14 | print('Message:', datetime.datetime.now(), server, 'said', data) 15 | 16 | client.close() 17 | -------------------------------------------------------------------------------- /demos/udp_server.py: -------------------------------------------------------------------------------- 1 | import datetime 2 | import socket 3 | 4 | 5 | server_address = ('localhost', 4544) 6 | max_size = 4096 7 | print('Starting the server at', datetime.datetime.now()) 8 | print('waiting for client to call') 9 | 10 | # This line creates a socket (AF_INET means we'll create an IP socket, 11 | # SOCK_DGRAM means we'll send and receive datagrams - UDP): 12 | server = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) 13 | 14 | # This listens for any data arriving at the IP, port. The programs sits and 15 | # waits here until there's some action: 16 | server.bind(server_address) 17 | 18 | # Any data that comes in will be received here: 19 | data, client = server.recvfrom(max_size) 20 | print('Message:', datetime.datetime.now(), client, 'said', data) 21 | 22 | # The server sends a reply: 23 | server.sendto(b'Are you talking to me?', client) 24 | 25 | # and closes the connection: 26 | server.close() 27 | -------------------------------------------------------------------------------- /escape_characters.py: -------------------------------------------------------------------------------- 1 | '''Escape Characters''' 2 | 3 | 4 | # Backslash allows you to escape characters or run an escape sequence 5 | # for example, escape the single-quote inside the string: 6 | 7 | print('I am 6\'2" tall.') 8 | 9 | # using triple quotes also works """ 10 | 11 | # \\ = backslash 12 | # \' = single quote 13 | # \" = double quote 14 | # \a = ASCII bell 15 | # \b = ASCII backspace 16 | # \f = ASCII formfeed 17 | # \n = ASCII line break 18 | # \r = ASCII full return 19 | # \t = ASCII tab 20 | # \v = ASCII vertical tab 21 | # \N{name} = unicode character name in the unicode database 22 | # \uxxxx = unicode character with 16-bit hex value xxxx 23 | # \Uxxxxxxxx = unicode character with 32-bit hex value xxxxxxxx 24 | # \ooo = character with octal value ooo 25 | # \xhh... = character with hex value hh.. 26 | 27 | # Example: 28 | 29 | print('Escape characters:\n\t-\u272D') 30 | # Escape characters: 31 | # -✭ 32 | -------------------------------------------------------------------------------- /flask_mqtt.md: -------------------------------------------------------------------------------- 1 | # Flask-mqtt 2 | 3 | See the [flask-mqtt documentation](https://flask-mqtt.readthedocs.io/en/latest/api/index.html) 4 | See also: [MQTT Essentials](https://www.hivemq.com/blog/mqtt-essentials-part-4-mqtt-publish-subscribe-unsubscribe/) 5 | 6 | ## Table of contents 7 | 8 | 9 | 10 | - [Introduction](#introduction) 11 | - [Install](#install) 12 | - [Instantiate and include](#instantiate-and-include) 13 | - [Client side](#client-side) 14 | - [Server side](#server-side) 15 | - [Notes](#notes) 16 | - [Mosquitto broker](#mosquitto-broker) 17 | 18 | 19 | 20 | ## Introduction 21 | 22 | MQTT is a lightweight, publish-subscribe network protocol that transports messages between devices using an MQTT broker. 23 | 24 | A client can publish messages as soon as it connects to a broker. Each message contains a topic that the broker uses to forward the message to interested clients. To receive messages on topics, a client sends a subscribe message to the MQTT broker. 25 | 26 | When a client sends a message to an MQTT broker for publication, the broker reads the message, acknowledges it (according to the Quality of Service [QoS] Level), and sends it off to the clients who have subscribed to the topic. 27 | 28 | The client that initially publishes the message is only concerned about delivering the message to the broker. Once the broker receives the message, it is the responsibility of the broker to deliver the message to all subscribers. The publishing client does not get any feedback about whether anyone is interested in the published message or how many clients received the message from the broker. 29 | 30 | ## Install 31 | 32 | ``` 33 | pip install flask-mqtt 34 | ``` 35 | 36 | ## Instantiate and include 37 | 38 | `__init__.py` 39 | ```python 40 | from flask_mqtt import Mqtt 41 | 42 | app = Flask(__name__) 43 | mqtt = Mqtt(app, connect_async=True) # To address flask-mqtt issue #73 44 | ``` 45 | 46 | `run.py` 47 | ```python 48 | socketio.run(app, 49 | host='0.0.0.0', 50 | port=80, 51 | debug=True, 52 | use_reloader=False, # mqtt requires this 53 | certfile='cert.pem', 54 | keyfile='key.pem') 55 | ``` 56 | 57 | `routes.py` 58 | ```python 59 | from app import mqtt 60 | ``` 61 | 62 | `config.py` 63 | ```python 64 | # MQTT configuration variables 65 | MQTT_BROKER_URL = 'localhost' 66 | MQTT_BROKER_PORT = 1883 67 | MQTT_USERNAME = '' 68 | MQTT_PASSWORD = '' 69 | MQTT_KEEPALIVE = 10 70 | MQTT_TLS_ENABLED = False 71 | ``` 72 | 73 | See the [flask-mqtt docs](https://flask-mqtt.readthedocs.io/en/latest/configuration.html#configuration-keys) for a complete list of the config keys. 74 | 75 | ## Client side 76 | 77 | All socketio 78 | 79 | ## Server side 80 | 81 | ```python 82 | # subscribe to a topic as soon as mqtt is connected 83 | @mqtt.on_connect() 84 | def handle_connect(client, userdata, flags, rc): 85 | mqtt.subscribe('home/mytopic') 86 | 87 | # handle the subscribed messages 88 | @mqtt.on_message() 89 | def handle_mqtt_message(client, userdata, message): 90 | data = dict( 91 | topic=message.topic, 92 | payload=message.payload.decode() 93 | ) 94 | 95 | # unsubscribe 96 | mqtt.unsubscribe('home/mytopic') 97 | mqtt.unsubscribe_all() 98 | 99 | # publish a message 100 | mqtt.publish('home/mytopic', 'this is my message') 101 | ``` 102 | 103 | ## Notes 104 | 105 | - A subscribe message can contain multiple subscriptions for a client. Each subscription is made up of a topic and a QoS level. 106 | 107 | - The topic in the subscribe message can contain wildcards that make it possible to subscribe to a topic pattern rather than a specific topic. 108 | 109 | - If there are overlapping subscriptions for one client, the broker delivers the message that has the highest QoS level for that topic. 110 | 111 | - The broker holds the session data of all clients that have persistent sessions, including subscriptions and missed messages. 112 | 113 | - Published messages have a Retain Flag (true/false). This flag defines whether the message is saved by the broker as the last known good value for a specified topic. When a new client subscribes to a topic, they receive the last message that is retained on that topic. 114 | 115 | - $ topics are reserved for internal statistics of the MQTT broker, for example: 116 | ``` 117 | $SYS/broker/clients/connected 118 | $SYS/broker/clients/disconnected 119 | $SYS/broker/clients/total 120 | $SYS/broker/messages/sent 121 | $SYS/broker/uptime 122 | ``` 123 | 124 | - A retained message is a normal MQTT message with the retained flag set to true. Using retained messages helps provide the last good value to a connecting client immediately. 125 | 126 | - The clean session flag tells the broker whether the client wants to establish a persistent session or not. In a persistent session (CleanSession = false), the broker stores all subscriptions for the client and all missed messages for the client that subscribed with a Quality of Service (QoS) level 1 or 2. If the session is not persistent (CleanSession = true), the broker does not store anything for the client and purges all information from any previous persistent session. Note in the Flask-MQTT github readme example, they have a config: `app.config['MQTT_CLEAN_SESSION'] = True` but this config is missing from the documentation. 127 | 128 | - In MQTT, you use the Last Will and Testament (LWT) feature to notify other clients about an ungracefully disconnected client. Each client can specify its last will message when it connects to a broker. The last will message is a normal MQTT message with a topic, retained message flag, QoS, and payload. The broker stores the message until it detects that the client has disconnected ungracefully. In response to the ungraceful disconnect, the broker sends the last-will message to all subscribed clients of the last-will message topic. If the client disconnects gracefully with a correct DISCONNECT message, the broker discards the stored LWT message. Clients can specify an LWT message in the CONNECT message that initiates the connection between the client and the broker. 129 | 130 | 131 | ## Mosquitto broker 132 | 133 | To restart the broker: 134 | ``` 135 | sudo systemctl restart mosquitto 136 | ``` 137 | 138 | To clear out (reset) the broker 139 | ``` 140 | sudo systemctl stop mosquitto 141 | sudo rm /var/lib/mosquitto/mosquitto.db 142 | sudo systemctl start mosquitto 143 | ``` 144 | 145 | To publish via the command line: 146 | ``` 147 | mosquitto_pub -t "xnet/sts/scc1/status" -m "offline" 148 | ``` 149 | 150 | To subscribe via the command line: 151 | ``` 152 | mosquitto_sub -t "xnet/sts/scc1/status" 153 | ``` 154 | -------------------------------------------------------------------------------- /flask_user_roles.md: -------------------------------------------------------------------------------- 1 | # User Roles in Flask 2 | 3 | There are a number of ways of implementing user roles in a Flask app. The best method will largely depend on how many different roles need to be supported and how elaborate they are. For example, a simple app may need only two roles, regular users and admin. In this case, having a simple `is_admin` boolean in the `User` model may be all that is necessary. 4 | 5 | In more complex apps, you may need several roles with varying levels of access. In some cases it may make more sense to discard the idea of individual roles and instead give users a set of individual permissions. 6 | 7 | ## Table of contents 8 | 9 | 10 | 11 | - [Simple Solution 1 - limit access in templates](#simple-solution-1---limit-access-in-templates) 12 | - [Simple Solution 2 - limit access in routes using redirects](#simple-solution-2---limit-access-in-routes-using-redirects) 13 | - [Roles & permissions - Chapter 9 Flask Web directive](#roles--permissions---chapter-9-flask-web-directive) 14 | - [User roles - Flask-User library](#user-roles---flask-user-library) 15 | 16 | 17 | 18 | ## Simple Solution 1 - limit access in templates 19 | 20 | In any Jinja template, you could pass the user data and show different things depending on the access level. 21 | 22 | For example: 23 | 24 | ```Jinja 25 | {% if current_user.is_admin %} 26 | ...Admin only content... 27 | {% endif %} 28 | ``` 29 | 30 | This is safe because the templates are rendered in the server. The user cannot modify their access level or show and hide elements through the Developer Tools in the browser. 31 | 32 | ## Simple Solution 2 - limit access in routes using redirects 33 | 34 | In any endpoint, you could check for a condition and simply redirect if it is not met. 35 | 36 | For example: 37 | 38 | ```python 39 | @app.route('/admin') 40 | def admin(): 41 | '''View function for the admins only.''' 42 | if not current_user.is_admin: 43 | flash('Sorry, only admins can access that page.', category='auth-fail') 44 | return redirect(url_for('index')) 45 | return render_template('admin.html') 46 | ``` 47 | 48 | ## Roles & permissions - Chapter 9 Flask Web directive 49 | 50 | ## User roles - Flask-User library 51 | -------------------------------------------------------------------------------- /geopy_module.py: -------------------------------------------------------------------------------- 1 | '''A Brief look at Geopy''' 2 | 3 | 4 | # https://geopy.readthedocs.io/en/stable/ 5 | 6 | # geopy is a Python client for several popular geocoding web services. geopy 7 | # makes it easy to locate the coordinates of addresses, cities, countries, and 8 | # landmarks across the globe using third-party geocoders and other data sources. 9 | 10 | # geopy includes geocoder classes for: ArcGIS, AzureMaps, Baidu, BANFrance, 11 | # Bing, DataBC, GeocodeEarth, GeocodeFarm, Geolake, GeoNames, GoogleV3, HERE, 12 | # IGNFrance, MapBox, OpenCage, OpenMapQuest, Nominatim, Pelias, Photon, 13 | # PickPoint, LiveAddress, TomTom, What3Words, Yandex. The majority of these 14 | # require a (not free) API key or usernamename/password paramter. The various 15 | # geocoder classes are located in geopy.geocoders. 16 | 17 | # As of July 2018 Google requires each request to have a paid API key. 18 | # https://developers.google.com/maps/documentation/geocoding/usage-and-billing 19 | 20 | # $ pip install geopy 21 | 22 | # from geopy.geocoders import GoogleV3 23 | # from geopy.geocoders import Yandex 24 | # from geopy.geocoders import DataBC 25 | # from geopy.geocoders import Nominatim 26 | from geopy.geocoders import Photon 27 | 28 | # geolocator = GoogleV3(scheme='http') 29 | # geolocator = Yandex(lang='en', scheme='http') 30 | # geolocator = DataBC(scheme='http') 31 | # geolocator = Nominatim(scheme='http', user_agent='my-application') 32 | geolocator = Photon(scheme='http') 33 | 34 | 35 | address1 = '3995 23rd St, San Francisco, CA 94114, USA' 36 | address2 = '3730 Cambie St, Vancouver, BC V5Z2X5, Canada' 37 | 38 | location = geolocator.geocode(address1) 39 | 40 | print(type(location)) 41 | # 42 | 43 | print(dir(location)) 44 | # [... 'address', 'altitude', 'latitude', 'longitude', 'point', 'raw'] 45 | 46 | print(location) 47 | # 3995 23rd St, San Francisco, CA 94114, USA 48 | 49 | print(location.address) 50 | # 3995 23rd St, San Francisco, CA 94114, USA 51 | 52 | print(location.altitude) 53 | # 0.0 54 | 55 | print((location.latitude, location.longitude)) 56 | # (37.7529648, -122.4317141) 57 | 58 | print(location.point) 59 | # 37 45m 10.6733s N, 122 25m 54.1708s W 60 | 61 | print(location.raw) 62 | # {'address_components': [ 63 | # {'long_name': '3995', 'short_name': '3995', 'types': ['street_number']}, 64 | # {'long_name': '23rd Street', 'short_name': '23rd St', 'types': ['route']}, 65 | # {'long_name': 'Noe Valley', 'short_name': 'Noe Valley', 'types': 66 | # ['neighborhood', 'political']}, 67 | # {'long_name': 'San Francisco', 'short_name': 'SF', 'types': 68 | # ['locality', 'political']}, 69 | # {'long_name': 'San Francisco County', 'short_name': 'San Francisco County', 70 | # 'types': ['administrative_area_level_2', 'political']}, 71 | # {'long_name': 'California', 'short_name': 'CA', 'types': 72 | # ['administrative_area_level_1', 'political']}, 73 | # {'long_name': 'United States', 'short_name': 'US', 'types': 74 | # ['country', 'political']}, 75 | # {'long_name': '94114', 'short_name': '94114', 'types': ['postal_code']}, 76 | # {'long_name': '3302', 'short_name': '3302', 'types': ['postal_code_suffix']}], 77 | # 'formatted_address': '3995 23rd St, San Francisco, CA 94114, USA', 78 | # 'geometry': { 79 | # 'location': {'lat': 37.7529353, 'lng': -122.4317224}, 80 | # 'location_type': 'ROOFTOP', 81 | # 'viewport': { 82 | # 'northeast': {'lat': 37.75428428029149, 'lng': -122.4303734197085}, 83 | # 'southwest': {'lat': 37.75158631970849, 'lng': -122.4330713802915}}}, 84 | # 'place_id': 'ChIJp4irxxN-j4ARlJs-6IsSQGk', 'types': ['street_address']} 85 | -------------------------------------------------------------------------------- /helper_functions.py: -------------------------------------------------------------------------------- 1 | '''Demo: Helper functions can be more readable than complex expressions.''' 2 | 3 | 4 | # Consider we're receiving some rgba values from a query string of a url. 5 | # The output we want is an integer for each r, g, b, and a. Let's say if the 6 | # values are missing, we want them to be 0. 7 | 8 | # Let's first see what type of data we get: 9 | 10 | from urllib.parse import parse_qs 11 | 12 | values = parse_qs('red=52&blue=0&green=', keep_blank_values=True) 13 | 14 | print(values) 15 | print(type(values)) 16 | # {'red': ['52'], 'blue': ['0'], 'green': ['']} 17 | # 18 | 19 | # The get method will allow us to check for values even if they don't exist: 20 | 21 | red = values.get('red') 22 | green = values.get('green') 23 | blue = values.get('blue') 24 | opacity = values.get('opacity') 25 | 26 | print('red:', red) 27 | print('green:', green) 28 | print('blue:', blue) 29 | print('opacity:', opacity) 30 | # red: ['52'] 31 | # green: [''] 32 | # blue: ['0'] 33 | # opacity: None 34 | 35 | # So, if I just want the number, I would have to specfify the first item in the 36 | # list[0]. And if the list is empty I'd have to say 'or 0'. And if the value 37 | # didn't exist at all (None), then I'd have to pass an optional value to the 38 | # get method (an empty list): 39 | 40 | red = values.get('red', [''])[0] or 0 41 | green = values.get('green', [''])[0] or 0 42 | blue = values.get('blue', [''])[0] or 0 43 | opacity = values.get('opacity', [''])[0] or 0 44 | 45 | print('red:', red) 46 | print('green:', green) 47 | print('blue:', blue) 48 | print('opacity:', opacity) 49 | # red: 52 50 | # green: 0 51 | # blue: 0 52 | # opacity: 0 53 | 54 | # But I also have to make sure the values are integers: 55 | 56 | red = int(values.get('red', [''])[0] or 0) 57 | green = int(values.get('green', [''])[0] or 0) 58 | blue = int(values.get('blue', [''])[0] or 0) 59 | opacity = int(values.get('opacity', [''])[0] or 0) 60 | 61 | # Now these expressions have become unreadable and repetitive. 62 | # Here's where a helper function would be useful: 63 | 64 | def get_value(values, key, default=0): 65 | result = values.get(key, ['']) 66 | if result[0]: 67 | result = int(result[0]) 68 | else: 69 | result = default 70 | return result 71 | 72 | red = get_value(values, 'red') 73 | green = get_value(values, 'green') 74 | blue = get_value(values, 'blue') 75 | opacity = get_value(values, 'opacity') 76 | 77 | # Though it means more lines of code, in the end its more readable. 78 | -------------------------------------------------------------------------------- /import_modules.py: -------------------------------------------------------------------------------- 1 | '''Importing Modules''' 2 | 3 | 4 | # You can import your own python file as a module or a module from the 5 | # python standard library in the same way: 6 | 7 | import random 8 | import myfile 9 | 10 | # when you import this way, you need to include the module name as a prefix 11 | # to any functions you want to use from that module. This is to avoid any 12 | # possible naming conflicts between modules. 13 | 14 | possibilities = ['red', 'orange', 'yellow', 'green', 'blue', 'cyan'] 15 | 16 | random.choice(possibilities) 17 | 18 | # Import a function from a module: 19 | 20 | from random import choice 21 | 22 | # when you import this way, you can reference the function name directly: 23 | 24 | choice(possibilities) 25 | 26 | # Import a module and give it another name using 'as': 27 | 28 | import random as ran 29 | 30 | ran.choice(possibilities) 31 | 32 | # Import a function from a module and give it another name: 33 | 34 | from random import choice as mix 35 | 36 | mix(possibilities) 37 | 38 | # Import a class from a module: 39 | 40 | from myclasses import Person 41 | 42 | # When importing, Python looks in the current directory first (so if you have a 43 | # file named random.py in your current directory, it will use that over the one 44 | # in the standard library. The standard library on a mac: 45 | 46 | # Library/Frameworks/Python.framework/Versions/3.5/lib/python3.6 47 | 48 | 49 | # import ordering 50 | # ----------------------------------------------------------------------------- 51 | # According to PEP8, imports should be listed in the following order. 52 | # Some also put a space between each group and some separate the regular 53 | # import statements from the 'from' import statements. Up to you. 54 | 55 | # 1. Standard library imports 56 | # 2. Third-party imports 57 | # 3. Applications imports 58 | 59 | 60 | # __name__ == '__main__' 61 | # ----------------------------------------------------------------------------- 62 | # If you intend for your module to be imported, remember that your program 63 | # should most likely run by calling a function, not just start running as soon 64 | # as its imported. When you want to test and run your code from within module, 65 | # itself use this: 66 | 67 | if __name__ == '__main__': 68 | pass # call the function that starts the script 69 | 70 | 71 | # see also packages.py 72 | -------------------------------------------------------------------------------- /iterables_summary.py: -------------------------------------------------------------------------------- 1 | '''Review of Lists, Tuples, Dictionaries & Sets''' 2 | 3 | 4 | # Compare data structures 5 | # ----------------------------------------------------------------------------- 6 | # List: mutable, ordered, values don't need to be unique 7 | colours_list = ['red', 'orange', 'black', 'black'] 8 | 9 | # Tuple: immutable, ordered, values don't need to be unique 10 | colours_tuple = ('red', 'orange', 'black', 'black') 11 | 12 | # Sets: mutable, unordered, values must be unique 13 | colours_set = {'red', 'orange', 'black'} 14 | 15 | # Frozensets : immutable, unordered, values must be unique 16 | colours_fset = frozenset(('red', 'orange', 'black')) 17 | 18 | # Dict keys: immutable, ordered (as of Python 3.6), values must be unique 19 | # Dict values: mutable, ordered (as of Python 3.6), values don't need to be unique 20 | colours_dict = { 21 | 'red': 'Pantone 185C', 22 | 'orange': 'Pantone 021C', 23 | 'black': 'Pantone 6C' 24 | } 25 | 26 | print(type(colours_list)) # 27 | print(type(colours_tuple)) # 28 | print(type(colours_set)) # 29 | print(type(colours_fset)) # 30 | print(type(colours_dict)) # 31 | 32 | # In each case use [] brackets to access a single element 33 | # (except sets which don't support indexing): 34 | 35 | print(colours_list[2]) # black 36 | print(colours_tuple[2]) # black 37 | print(colours_dict['black']) # Pantone 6C 38 | 39 | # You can combine data structures, for example, you can make: 40 | # - a tuple of lists 41 | # - a list of lists 42 | # - a dictionary of lists (only the values van be lists) 43 | 44 | colors = ['magenta', 'red', 'cyan'] 45 | moods = ['happy', 'sad', 'confused'] 46 | senses = ['smell', 'touch', 'taste'] 47 | 48 | dict_of_lists = { 49 | 'Colors': colors, 50 | 'Moods': moods, 51 | 'Senses': senses 52 | } 53 | 54 | # Reminder: Dictionary keys are immutable, therefor you cannot use a list 55 | # or another dictionary as a key, but you CAN use a tuple or a frozenset 56 | # because those are immutable too. A good example of this is with mapping – 57 | # the GPS coordinates may be the key: 58 | 59 | places = { 60 | (44, -93, 344): 'home', 61 | (27, -80, 200): 'work' 62 | } 63 | 64 | # Note that these aren't the only iterables in Python. Other iterables include: 65 | # - strings 66 | # - generators 67 | # - pandas series objects 68 | -------------------------------------------------------------------------------- /iterating_with_for.py: -------------------------------------------------------------------------------- 1 | '''Iterating with for''' 2 | 3 | 4 | # Iterating over lists, tuples and sets returns individual items: 5 | # ----------------------------------------------------------------------------- 6 | 7 | list1 = ['one', 'two', 'three'] 8 | tuple1 = ('four', 'five', 'six') 9 | set1 = {'seven', 'eight', 'nine'} 10 | 11 | for item in list1: 12 | print(item) 13 | # one 14 | # two 15 | # three 16 | 17 | for item in tuple1: 18 | print(item) 19 | # four 20 | # five 21 | # six 22 | 23 | for item in set1: 24 | print(item) 25 | # eight 26 | # nine 27 | # seven 28 | 29 | # You cab also iterate over a slice. 30 | for item in list1[2:]: 31 | print(item) 32 | # three 33 | 34 | 35 | # Iterating over strings returns characters 36 | # ----------------------------------------------------------------------------- 37 | 38 | string1 = "ten" 39 | 40 | for char in string1: 41 | print(char) 42 | # t 43 | # e 44 | # n 45 | 46 | 47 | # Iterating over dictionaries 48 | # ----------------------------------------------------------------------------- 49 | 50 | dict1 = {'eleven': '11', 'twelve': '12', 'thirteen': '13'} 51 | 52 | for item in dict1: # will choose keys 53 | print(item) 54 | # eleven 55 | # twelve 56 | # thirteen 57 | 58 | for item in dict1.keys(): # works the same as above 59 | print(item) 60 | # eleven 61 | # twelve 62 | # thirteen 63 | 64 | # Therefor, to iterate over a dictionary and get values: 65 | for item in dict1.values(): 66 | print(item) 67 | # 11 68 | # 12 69 | # 13 70 | 71 | # To iterate over both keys and values: 72 | for item in dict1.items(): 73 | print(item) 74 | # ('eleven', '11') 75 | # ('twelve', '12') 76 | # ('thirteen', '13') 77 | 78 | # Break apart the resulting tuple by doing this: 79 | for key, value in dict1.items(): 80 | print(key, '–', value) 81 | # eleven – 11 82 | # twelve – 12 83 | # thirteen – 13 84 | 85 | 86 | # Iterating over multiple sequences with zip() 87 | # ----------------------------------------------------------------------------- 88 | 89 | days = ['Monday', 'Tuesday', 'Wednesday'] 90 | fruits = ['Apple', 'Banana', 'Pear'] 91 | drinks = ['Tea', 'Juice', 'Wine'] 92 | desserts = ['Ice cream', 'Cookies', 'Cake', 'Candy'] 93 | 94 | for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts): 95 | print(day.upper()) 96 | print('–', fruit) 97 | print('–', drink) 98 | print('–', dessert) 99 | 100 | 101 | # Using range(start, stop, step) 102 | # ----------------------------------------------------------------------------- 103 | 104 | for x in range(5, -1, -1): 105 | print(x, end='...') # 5...4...3...2...1...0.. 106 | 107 | 108 | # using multiple for loops 109 | # ----------------------------------------------------------------------------- 110 | 111 | for i in range(1, 13): 112 | for j in range(1, 13): 113 | print(i, 'x', j, '=', i * j) 114 | print('------------------') 115 | 116 | 117 | # break and continue 118 | # ----------------------------------------------------------------------------- 119 | # break is useful when you want to terminate a loop early if some condition 120 | # is met. Continue skips past to the next iteration. 121 | 122 | cheeses = ['brie', 'cheddar', 'feta', 'gorgonzola'] 123 | 124 | for cheese in cheeses: 125 | if cheese == 'feta': 126 | break 127 | print('We have', cheese) 128 | 129 | # We have brie 130 | # We have cheddar 131 | 132 | for cheese in cheeses: 133 | if cheese == 'feta': 134 | continue 135 | print('We have', cheese) 136 | 137 | # We have brie 138 | # We have cheddar 139 | # We have gorgonzola 140 | 141 | 142 | # Create your own iterator with iter() and next() 143 | # ----------------------------------------------------------------------------- 144 | # A for loop actually creates an iterator object that will return each 145 | # item that it's iterating over. When there are no more items, the iterator 146 | # returns an error. The for loop is built to handle the error and terminates. 147 | 148 | string = '123' 149 | my_iterator = iter(string) 150 | print(my_iterator) # 151 | print(next(my_iterator)) # 1 152 | print(next(my_iterator)) # 2 153 | print(next(my_iterator)) # 3 154 | 155 | # To confirm, the following two are the same thing, we don't need to explicitly 156 | # add the iter() as the for loop will do it for us: 157 | 158 | for char in string: 159 | print(char, end='...') # 1...2...3... 160 | 161 | for char in iter(string): 162 | print(char, end='...') # 1...2...3... 163 | 164 | 165 | # Important Note: 166 | # ----------------------------------------------------------------------------- 167 | # A for loop is effective for iterating through a list but apparently, "You 168 | # shouldn't modify a list inside a for loop because Python will have trouble 169 | # keeping track of the items in the list. To modify a list as you work through 170 | # it, use a while loop." Using a while loop allows you to better collect, 171 | # store and organize. 172 | -------------------------------------------------------------------------------- /json_example.py: -------------------------------------------------------------------------------- 1 | '''JSON Example''' 2 | 3 | 4 | # see also structured_file_formats.py 5 | 6 | # The JSON (JavaScript Object Notation) module allows you to dump simple python 7 | # data structures into a file and load it back in next time the programs runs. 8 | # You can also use JSON to share data between different python programs or 9 | # other programming languages. It's a useful, portable, easy-to-learn file 10 | # format. The pythons data types it can store are: strings, numbers, booleans, 11 | # lists and dictionaries with string keys. 12 | 13 | # There are two sets of functions for working with JSON: 14 | 15 | # json.dump() and json.load() – for storing and retrieving JSON from a file 16 | # json.dumps() and json.loads() – for creating and parsing JSON strings 17 | 18 | 19 | # JSON to file 20 | # ----------------------------------------------------------------------------- 21 | 22 | import json 23 | 24 | # The json.dump() function takes two arguments: a piece of data to store and 25 | # the file object to store the data in. 26 | 27 | numbers = [4, 5, 7, 9, 11, 13] 28 | username = input("What is your name? ") 29 | filename = 'data/numbers.json' 30 | 31 | with open(filename, 'w') as fob: 32 | json.dump((numbers, username), fob) 33 | 34 | # Read the data back in with json.load(): 35 | 36 | with open(filename) as fob: 37 | data = json.load(fob) 38 | 39 | print(type(data)) # 40 | 41 | numbers2, username2 = data 42 | 43 | print(type(numbers2)) # 44 | print(type(username2)) # 45 | print('Hello', username2) # Hello Raja 46 | print(numbers2) # [4, 5, 7, 9, 11, 13] 47 | 48 | 49 | # JSON to strings 50 | # ----------------------------------------------------------------------------- 51 | # JSON strings work well for when you want to share data structures between 52 | # programs and languages (e.g. python dictionary to Javscript). 53 | 54 | data = { 55 | 'date': '2019-04-10', 56 | 'count': 6, 57 | 'session_id': 102 58 | } 59 | 60 | # create a JSON string with json.dumps(): 61 | 62 | data_json = json.dumps(data) 63 | 64 | print(type(data_json), data_json) 65 | # {"date": "2019-04-10", "count": 6, "session_id": 102} 66 | 67 | # parse a JSON string into a python data structure with json.loads(): 68 | 69 | json_string = '["hello", 100, [2019, 4, 10]]' 70 | 71 | data = json.loads(json_string) 72 | 73 | print(type(data), data) 74 | # ['hello', 100, [2019, 4, 10]] 75 | 76 | 77 | # Encoders and Decoders (json.JSONDecoder, json.JSONEncoder) 78 | # ----------------------------------------------------------------------------- 79 | # When you convert from Python to JSON, Python objects are encoded to the 80 | # following JavaScript equivalents: 81 | 82 | # Python JSON 83 | # ------ ------ 84 | # dict Object 85 | # list Array 86 | # tuple Array 87 | # str String 88 | # int Number 89 | # float Number 90 | # True true 91 | # False false 92 | # None null 93 | 94 | 95 | # Extending the JSONEncoder 96 | # ----------------------------------------------------------------------------- 97 | # see structured_file_formats.py 98 | -------------------------------------------------------------------------------- /jupyter_notebook.md: -------------------------------------------------------------------------------- 1 | # Jupyter Notebook 2 | 3 | 4 | Jupyter Notebook is a hybrid editor and interactive shell that runs in the browser. It works really well for exploring large sets of data. It does not replace your regular editor or IDE (which is more useful for working with multiple files and projects) but definitely worth considering if you need to explore your datasets and test your code before implementing. 5 | 6 | ``` 7 | $ pip install jupyter 8 | ``` 9 | 10 | If you intend on working with excel files, you should also install: 11 | 12 | ``` 13 | $ pip install xlrd 14 | ``` 15 | 16 | Once installed, start a jupyter session by navigating to your working directory in the command line, then launch: 17 | 18 | ``` 19 | $ jupyter notebook 20 | ``` 21 | 22 | This will launch a jupyter session in a new browser window. You will see all your files listed from your current working directory. 23 | 24 | From the dropdown in the top right corner, choose to create a new python notebook. Python is listed as the only type of notebook because we installed jupyter via python with pip. This opens a new untitled tab. When you give it a name and you'll see a new .ipynb file and folder show up in your working directory. The .ipynb file allows you to return to your previous saved session (follow the same steps to launch jupyter, but instead of creating a new session, select the .ipynb file). 25 | 26 | The editable field here is the shell. You can enter in as many lines of code as you want. Hitting the return key will not execute the code. When you are ready to execute hit `ctrl + return`. 27 | 28 | At this point you can continue to edit the code in the first shell 'cell', or you can create a new 'cell' by hitting `option/alt + return`. You can execute your code and immediately create a new 'cell' with `shift + return`. 29 | 30 | - To remove a 'cell', press `esc`, then `dd` 31 | - To add a cell without executing press `esc`, then `b` 32 | 33 | You can find more shortcuts under the help menu. 34 | 35 | To view data try this; in a new jupyter 'cell': 36 | ```python 37 | import pandas 38 | ``` 39 | 40 | In a second cell: 41 | ```python 42 | df = pandas.read_csv('data/supermarkets.csv') 43 | df 44 | ``` 45 | 46 | When you execute the second cell, you should see the data displayed in a friendly, easy-to-read table. Note the header rows in CSV files are shown in bold text. Try setting the header arg to None to see the difference: 47 | 48 | ```python 49 | df1 = pandas.read_csv('data/supermarkets.csv', header=None) 50 | ``` 51 | 52 | Another nice feature of jupyter is the ability to get help on methods or classes by typing a question mark: 53 | ```python 54 | df.set_index? 55 | ``` 56 | 57 | This will open a window with detailed help... really nice. 58 | 59 | When you are finished working, logout of the browser windows. Back in the command line window, `CTRL-c`, then enter `y` to quit: 60 | ``` 61 | The Jupyter Notebook is running at: 62 | http://localhost:8888/?token=d1ac3... 63 | Shutdown this notebook server (y/[n])? y 64 | [C 10:16:08.918 NotebookApp] Shutdown confirmed 65 | ``` 66 | -------------------------------------------------------------------------------- /keywords.py: -------------------------------------------------------------------------------- 1 | '''Python Keywords''' 2 | 3 | 4 | # The following identifiers are used as reserved words, or keywords of the 5 | # language, and cannot be used as ordinary identifiers. If you absolutely 6 | # feel for clarity that you must use one of these names for your own variable, 7 | # an accepted format to follow the name with an underscore. For example: from_ 8 | 9 | import keyword 10 | 11 | for word in keyword.kwlist: 12 | print(word) 13 | 14 | # False 15 | # None 16 | # True 17 | # and 18 | # as 19 | # assert 20 | # async 21 | # await 22 | # break 23 | # class 24 | # continue 25 | # def 26 | # del 27 | # elif 28 | # else 29 | # except 30 | # finally 31 | # for 32 | # from 33 | # global 34 | # if 35 | # import 36 | # in 37 | # is 38 | # lambda 39 | # nonlocal 40 | # not 41 | # or 42 | # pass 43 | # raise 44 | # return 45 | # try 46 | # while 47 | # with 48 | # yield 49 | -------------------------------------------------------------------------------- /logging_errors.py: -------------------------------------------------------------------------------- 1 | '''Logging Error Messages''' 2 | 3 | 4 | # The Python standard library module for logging is logging. 5 | # https://docs.python.org/3/library/logging.html 6 | 7 | # The logging module includes: 8 | # – The message that you want to save to the log 9 | # – Ranked priority levels as functions: 10 | # debug(), info(), warning(), error(), and critical() 11 | # – One or more logger objects as the main connection with the module 12 | # – Handlers that direct the message to your terminal, a file, a database, 13 | # or somewhere else 14 | # – Formatters that create the output 15 | # – Filters that make decisions based on the input 16 | 17 | import logging 18 | 19 | fmt = '%(asctime)s %(levelname)s %(lineno)s %(message)s' 20 | logging.basicConfig(level=logging.DEBUG, filename='data/test.log', format=fmt) 21 | 22 | # priority levels: 23 | logging.debug('debug message') 24 | logging.info('info message') 25 | logging.warning('warning message') 26 | logging.error('error message') 27 | logging.critical('critical message') 28 | 29 | 30 | # Breakdown 31 | # ----------------------------------------------------------------------------- 32 | # The default priority level is warn. Set the default by using basicConfig(). 33 | # This config should appear before any other logging functions as above. 34 | 35 | logging.basicConfig(level=logging.DEBUG) 36 | 37 | # handlers direct messages to different places, for example, a log file: 38 | 39 | logging.basicConfig(level=logging.DEBUG, filename='data/test.log') 40 | 41 | # you can format your logged messages: 42 | 43 | fmt = '%(asctime)s %(levelname)s %(lineno)s %(message)s' 44 | logging.basicConfig(level=logging.DEBUG, filename='data/test.log', format=fmt) 45 | 46 | # here's a logger object: 47 | 48 | logger = logging.getLogger('MyLogger') 49 | logger.debug('here is my debug message') 50 | 51 | # Calling basicConfig() with a filename argument created a FileHandler and made 52 | # it available to the logger. The logging module includes at least 15 handlers 53 | # to send messages to places such as the screen, email, web servers and files. 54 | 55 | # The information here is pretty scant. For more information on this topic, go 56 | # through the following in Doug Hellman's Python 3 Standard Library by example: 57 | # p.980–986, p.563–564, p.644–647, p.650–653, p.671–674 58 | 59 | 60 | # Log Record attributes 61 | # ----------------------------------------------------------------------------- 62 | # There are a number of attributes (bits of information) that can be included 63 | # in your logged message format. For a full list see: 64 | # https://docs.python.org/3/library/logging.html#logrecord-attributes 65 | 66 | 67 | # Logging Levels 68 | # ----------------------------------------------------------------------------- 69 | # The predifined logging levels all have a numeric value associated to them. 70 | # This is mostly of interest because you can define your own levels. 71 | 72 | # Level | Numeric value 73 | # ----------|-------------- 74 | # CRITICAL | 50 75 | # ERROR | 40 76 | # WARNING | 30 77 | # INFO | 20 78 | # DEBUG | 10 79 | # NOTSET | 0 80 | 81 | # To define your own level: 82 | 83 | SPECIAL_INFO_LEVEL_NUM = 21 84 | logging.addLevelName(SPECIAL_INFO_LEVEL_NUM, 'SPECIAL_INFO') 85 | 86 | def special_info(self, message, *args, **kws): 87 | if self.isEnabledFor(SPECIAL_INFO_LEVEL_NUM): 88 | self._log(SPECIAL_INFO_LEVEL_NUM, message, args, **kws) 89 | 90 | logging.Logger.special_info = special_info 91 | 92 | logger.special_info('Hey, this is special') 93 | 94 | # That being said, while defining your own levels is possible, it shouldn't 95 | # really be necessary, as the existing levels have been chosen on the basis of 96 | # practical experience. It's also a very bad idea if you are developing a 97 | # library as your custom level could conflict with others. 98 | -------------------------------------------------------------------------------- /match_statements.py: -------------------------------------------------------------------------------- 1 | '''Match Statements''' 2 | 3 | 4 | # New to Python 3.10, structural pattern matching has been added in the form of 5 | # a match statement and case statements of patterns with associated actions. It 6 | # seems similar to JavaScript switch statements but better. 7 | 8 | # https://peps.python.org/pep-0634/ 9 | # https://peps.python.org/pep-0636/ 10 | 11 | # Patterns consist of sequences, mappings, primitive data types as well as class 12 | # instances. Pattern matching enables programs to extract information from 13 | # complex data types, branch on the structure of data, and apply specific 14 | # actions based on different forms of data. 15 | 16 | subject = 'b' 17 | result = None 18 | 19 | match subject: 20 | case 'a': 21 | result = 'one' 22 | case 'b': 23 | result = 'two' 24 | case 'c': 25 | result = 'three' 26 | case _: 27 | result = 'wildcard' 28 | 29 | print(result) # two 30 | 31 | # A match statement takes an expression and compares its value to successive 32 | # patterns given as one or more case blocks. Specifically, pattern matching 33 | # operates by: 34 | 35 | # - using data with type and shape (the subject) 36 | # - evaluating the subject in the match statement 37 | # - comparing the subject with each pattern in a case statement from top to 38 | # bottom until a match is confirmed. 39 | # - executing the action associated with the pattern of the confirmed match 40 | # - If an exact match is not confirmed, the last case, a wildcard _, if provided, 41 | # will be used as the matching case. If an exact match is not confirmed and a 42 | # wildcard case does not exist, the entire match block is a no-op. 43 | 44 | 45 | # Match to a literal 46 | # ----------------------------------------------------------------------------- 47 | 48 | def http_status(code): 49 | match code: 50 | case 200: 51 | return 'ok' 52 | case 400: 53 | return 'bad request' 54 | case 401 | 403 | 405: 55 | return 'not allowed' 56 | case 404: 57 | return 'not found' 58 | case _: 59 | return 'something else broke' 60 | 61 | print(http_status(500)) # something else broke 62 | 63 | 64 | # Patterns with a variable 65 | # ---------------------------------------------------------------------------- 66 | 67 | def show_point(point): 68 | match point: 69 | case (0, 0): 70 | print('origin') 71 | case (x, 0): 72 | print(f'x={x}') 73 | case (0, y): 74 | print(f'y={y}') 75 | case (x, y): 76 | print(f'x={x}, y={y}') 77 | case _: 78 | raise ValueError('Not a point') 79 | 80 | show_point((15, 4)) # x=15, y=4 81 | show_point((0, 5)) # y=5 82 | 83 | 84 | # Patterns with classes 85 | # ---------------------------------------------------------------------------- 86 | 87 | class Point: 88 | def __init__(self, x, y): 89 | self.x = x 90 | self.y = y 91 | 92 | def location(point): 93 | match point: 94 | case Point(x=0, y=0): 95 | print('origin') 96 | case Point(x=x, y=y): 97 | print(f'x={x}, y={y}') 98 | case _: 99 | raise ValueError('Not a point') 100 | 101 | 102 | location(Point(x=0, y=0)) # origin 103 | location(Point(x=12, y=66)) # x=12, y=66 104 | 105 | 106 | # Add an if clause as a 'guard' 107 | # ---------------------------------------------------------------------------- 108 | 109 | def location(point): 110 | match point: 111 | case Point(x=0, y=0): 112 | print('origin') 113 | case Point(x=x, y=y) if x == y: 114 | print(f'the point is on the diagonal: x={x}, y={y}') 115 | case Point(x=x, y=y): 116 | print(f'x={x}, y={y}') 117 | case _: 118 | raise ValueError('Not a point') 119 | 120 | 121 | location(Point(x=12, y=12)) # the point is on the diagonal: x=12, y=12 122 | 123 | 124 | # Complex patterns and the wildcard 125 | # ---------------------------------------------------------------------------- 126 | # The _ wildcard can be used for items within a pattern, for example: 127 | 128 | message = ('error', 100, ['foo']) 129 | 130 | match message: 131 | case ('warning', code, 50): 132 | print('a warning has been received') 133 | case ('error', code, _): 134 | print(f'an error {code} has occurred') 135 | 136 | # an error 100 has occurred 137 | 138 | -------------------------------------------------------------------------------- /matplotlib_csv_example.py: -------------------------------------------------------------------------------- 1 | '''Matplotlib – Weather Data''' 2 | 3 | 4 | # The weather data for this example was originally obtained from: 5 | # https://www.wunderground.com/history/ 6 | # Some other good data sources for historical weather data: 7 | # https://www.ncdc.noaa.gov/data-access/quick-links 8 | # http://climate.weather.gc.ca/index_e.html 9 | 10 | import csv 11 | from datetime import datetime 12 | from matplotlib import pyplot as plt 13 | 14 | 15 | filename = 'data/death_valley_2014.csv' 16 | placename = 'Death Valley, CA' 17 | 18 | 19 | # Exploring the data: 20 | # ----------------------------------------------------------------------------- 21 | 22 | with open(filename) as fob: 23 | reader = csv.reader(fob) 24 | header_row = next(reader) 25 | for index, column_header in enumerate(header_row): 26 | print(index, column_header) 27 | # 0 PST 28 | # 1 Max TemperatureF 29 | # 2 Mean TemperatureF 30 | # 3 Min TemperatureF 31 | # 4 Max Dew PointF 32 | # 5 MeanDew PointF 33 | # 6 Min DewpointF 34 | # 7 Max Humidity 35 | # 8 Mean Humidity 36 | # 9 Min Humidity 37 | # 10 Max Sea Level PressureIn 38 | # 11 Mean Sea Level PressureIn 39 | # 12 Min Sea Level PressureIn 40 | # 13 Max VisibilityMiles 41 | # 14 Mean VisibilityMiles 42 | # 15 Min VisibilityMiles 43 | # 16 Max Wind SpeedMPH 44 | # 17 Mean Wind SpeedMPH 45 | # 18 Max Gust SpeedMPH 46 | # 19 PrecipitationIn 47 | # 20 CloudCover 48 | # 21 Events 49 | # 22 WindDirDegrees 50 | 51 | 52 | # Extracting and reading data: 53 | # ----------------------------------------------------------------------------- 54 | 55 | with open(filename) as fob: 56 | reader = csv.reader(fob) 57 | header_row = next(reader) 58 | dates, highs, lows = [], [], [] 59 | for row in reader: 60 | try: 61 | current_date = datetime.strptime(row[0], '%Y-%m-%d') 62 | high = int(row[1]) 63 | low = int(row[3]) 64 | except ValueError: 65 | print(current_date, 'missing data') 66 | else: 67 | dates.append(current_date) 68 | highs.append(high) 69 | lows.append(low) 70 | 71 | 72 | # Plotting the data: 73 | # ----------------------------------------------------------------------------- 74 | 75 | # creates a figure and one subplot 76 | fig, ax = plt.subplots(figsize=(10, 5)) 77 | 78 | # plot the data: 79 | plt.plot(dates, highs, c='tomato', alpha=0.6) 80 | plt.plot(dates, lows, c='darkturquoise', alpha=0.6) 81 | 82 | # format the plot: 83 | plt.fill_between(dates, highs, lows, facecolor='papayawhip', alpha=0.6) 84 | title = 'Daily high and low temperatures, 2014\n{}'.format(placename) 85 | plt.title(title, fontsize=10) 86 | plt.xlabel('', fontsize=9) 87 | fig.autofmt_xdate() 88 | plt.xticks(rotation=25) 89 | plt.ylabel('Termperature (F)', fontsize=9, fontweight='bold') 90 | plt.tick_params(axis='both', which='major', labelsize=7) 91 | 92 | # tells x, y axis to use this range 93 | # plt.axis([datetime(2014, 1, 1), datetime(2014, 12, 1), 0, 120]) 94 | # if you just want to set one axis: 95 | ax.set_xlim([datetime(2014, 1, 1), datetime(2014, 12, 1)]) 96 | # ax.set_ylim([20, 120]) 97 | 98 | # display the plot: 99 | plt.show() 100 | -------------------------------------------------------------------------------- /matplotlib_example.py: -------------------------------------------------------------------------------- 1 | '''Matplotlib – Random Walk''' 2 | 3 | 4 | # A random walk is a path that has no clear direction but is determined by a 5 | # series of random decisions. For example, a grain of pollen floating on a drop 6 | # of water moves across the surface in a random walk. It is pushed around by 7 | # water molecules and molecular motion in a water drop happens to be random. 8 | 9 | # See also: matplotlib_example.py 10 | 11 | # This example uses the matplotlib.pyplot.scatter plot: 12 | # https://matplotlib.org/stable/api/_as_gen/matplotlib.pyplot.scatter.html?highlight=scatter#matplotlib.pyplot.scatter 13 | 14 | 15 | # Ideally the following class would be saved as a module: random_walk.py 16 | # ----------------------------------------------------------------------------- 17 | 18 | from random import choice 19 | 20 | class RandomWalk(): 21 | '''A class to generate random walks.''' 22 | 23 | def __init__(self, num_points=5000): 24 | '''Initialize attributes of walk.''' 25 | self.num_points = num_points 26 | 27 | # all walks start at (0,0): 28 | self.x_values = [0] 29 | self.y_values = [0] 30 | 31 | def fill_walk(self): 32 | '''Calculate all the points in the walk.''' 33 | # keep tracking the steps until the walk reaches the desired length: 34 | while len(self.x_values) < self.num_points: 35 | # decide on which direction to go and how far to go: 36 | x_direction = choice([1, -1]) 37 | x_distance = choice([0, 1, 2, 3, 4]) 38 | x_step = x_direction * x_distance 39 | 40 | y_direction = choice([1, -1]) 41 | y_distance = choice([0, 1, 2, 3, 4]) 42 | y_step = y_direction * y_distance 43 | # reject moves that go nowhere: 44 | if x_step == 0 and y_step == 0: 45 | continue 46 | # calculate the next x and y values: 47 | next_x = self.x_values[-1] + x_step 48 | next_y = self.y_values[-1] + y_step 49 | 50 | self.x_values.append(next_x) 51 | self.y_values.append(next_y) 52 | 53 | 54 | # Ideally the following would be in it's own file and we'd import the class 55 | # ----------------------------------------------------------------------------- 56 | 57 | import matplotlib.pyplot as plt 58 | from matplotlib.colors import LinearSegmentedColormap 59 | # from random_walk import RandomWalk 60 | 61 | # colours: 62 | a = '#69F4BD' 63 | b = '#319589' 64 | c = '#344D6c' 65 | d = '#372560' 66 | e = '#3E1B3C' 67 | 68 | rw1 = RandomWalk(50000) 69 | rw1.fill_walk() 70 | 71 | # optional setting the window size: 72 | plt.figure(figsize=(9, 5)) 73 | 74 | # make your own colour map: 75 | cmap = LinearSegmentedColormap.from_list('mycmap', [a, b, c, d, e]) 76 | 77 | # create a list for the range needed for the colour map: 78 | point_numbers = list(range(rw1.num_points)) 79 | 80 | # the main plot: 81 | plt.scatter(rw1.x_values, rw1.y_values, s=1, c=point_numbers, cmap=cmap) 82 | 83 | # emphasize the start and end points: 84 | plt.scatter(0, 0, c='gold', s=25) 85 | plt.scatter(rw1.x_values[-1], rw1.y_values[-1], c='red', s=25) 86 | 87 | # title and axis size: 88 | plt.title('Random Walk', fontsize=9) 89 | plt.tick_params(axis='both', which='major', labelsize=7) 90 | 91 | plt.show() 92 | -------------------------------------------------------------------------------- /namespaces.py: -------------------------------------------------------------------------------- 1 | '''Namespaces''' 2 | 3 | 4 | # Local and global variables 5 | # ----------------------------------------------------------------------------- 6 | # You can get the value of a global variable from within a function 7 | 8 | animal = 'fruitbat' 9 | 10 | def print_global(): 11 | print('inside print_global:', animal) 12 | 13 | print_global() 14 | # inside print_global: fruitbat 15 | 16 | # But if you get the value of a global variable and and try to change it within 17 | # a function, you get an error because as soon as you start assigning to a 18 | # variable, python wants to make it a local variable, and since no local 19 | # variable has been initialized... error: 20 | 21 | def change_and_print_global(): 22 | print('inside change_and_print_global:', animal) 23 | animal = 'wombat' 24 | print('after the change:', animal) 25 | 26 | # change_and_print_global() 27 | # UnboundLocalError: local variable 'animal' referenced before assignment 28 | 29 | # So, if you reassign the variable you are actually making a new local variable 30 | # (also named animal) inside of the function. Use ID to see they're different: 31 | 32 | def change_local(): 33 | animal = 'wombat' 34 | print('local animal:', animal, id(animal)) 35 | 36 | change_local() 37 | print('global animal:', animal, id(animal)) 38 | # local animal: wombat 4316476056 39 | # global animal: fruitbat 4316756144 40 | 41 | # To access and change a global variable from within a function you need to be 42 | # explicit by using the global keyword: 43 | 44 | def change_and_print_global(): 45 | global animal 46 | print('inside change_and_print_global:', animal) 47 | animal = 'wombat' 48 | print('after the change:', animal) 49 | 50 | change_and_print_global() 51 | # inside change_and_print_global: fruitbat 52 | # after the change: wombat 53 | 54 | print(f'global animal: {animal}') 55 | # global animal: wombat 56 | 57 | 58 | # nonlocal 59 | # ----------------------------------------------------------------------------- 60 | # Python 3 introduced the nonlocal keyword that allows you to assign to 61 | # variables in an outer, but non-global, scope. 62 | 63 | x = 'orange' 64 | 65 | def testing_nonlocal(): 66 | x = 'lemon' 67 | def inside(): 68 | nonlocal x # if this statement is removed, the printed output 69 | x = 'lime' # would be 'lime', then 'lemon' because we would 70 | print(x) # have 3 unique 'x' variables. 71 | inside() 72 | print(x) 73 | 74 | print(x) 75 | testing_nonlocal() 76 | # orange 77 | # lime 78 | # lime 79 | 80 | # NOTE: when you reference a variable name, python will start by looking for 81 | # the variable definition locally, and then move outward until if finds it. 82 | # The order of scopes is as follows: 83 | 84 | # – local 85 | # – enclosing 86 | # – global 87 | # – built-ins 88 | 89 | 90 | # locals() and globals() 91 | # ----------------------------------------------------------------------------- 92 | # Python provides two functions to access the contents of your Namespaces 93 | # locals() and globals(): 94 | 95 | fruit = 'orange' 96 | 97 | def testing_local(): 98 | fruit = 'apple' 99 | print('locals:', locals()) 100 | 101 | testing_local() 102 | # locals: {'fruit': 'apple'} 103 | 104 | print('globals:') 105 | g = sorted(globals()) 106 | for i in g: 107 | print(i) 108 | # globals: 109 | # __annotations__ 110 | # __builtins__ 111 | # __cached__ 112 | # __doc__ 113 | # __file__ 114 | # __loader__ 115 | # __name__ 116 | # __package__ 117 | # __spec__ 118 | # animal 119 | # change_and_print_global 120 | # change_local 121 | # fruit 122 | # print_global 123 | # testing_local 124 | # testing_nonlocal 125 | # x 126 | 127 | 128 | # __name__, __doc__ 129 | # ----------------------------------------------------------------------------- 130 | # The name of a function is in the system variable: function.__name__ 131 | # The functions document string in in the variable: function.__doc__ 132 | 133 | def amazing(): 134 | '''This is the functions description''' 135 | print('This function is named:', amazing.__name__) 136 | print('Its docstring says:', amazing.__doc__) 137 | print('The main program running is assigned a special name:', __name__) 138 | 139 | amazing() 140 | # This function is named: amazing 141 | # Its docstring says: This is the functions description 142 | # The main program running is assigned a special name: __main__ 143 | -------------------------------------------------------------------------------- /numpy_modules.py: -------------------------------------------------------------------------------- 1 | '''A brief look at NumPy (Numerical Python).''' 2 | 3 | 4 | # http://www.numpy.org/ 5 | 6 | # NumPy is a library for Python that adds support for "large, multi-dimensional 7 | # arrays and matrices, along with a large collection of high-level mathematical 8 | # functions to operate on these arrays." 9 | 10 | # numpy arrays look similar to python lists but they are much more efficient, 11 | # especially when it comes to iterating over them, running other functions. 12 | 13 | # numpy is a base library for many other libraries such as pandas, matplotlib, 14 | # and OpenCV (image processing library). 15 | 16 | import numpy 17 | 18 | 19 | # One Dimensional Arrays 20 | # ----------------------------------------------------------------------------- 21 | n = numpy.arange(27) 22 | 23 | print(n) # [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 ...] 24 | print(type(n)) # 25 | 26 | n_start_stop_step = numpy.arange(0, 100, 10) 27 | print(n_start_stop_step) # [ 0 10 20 30 40 50 60 70 80 90] 28 | 29 | 30 | # Two Dimensional Arrays 31 | # ----------------------------------------------------------------------------- 32 | n = n.reshape(3, 9) 33 | 34 | print(n) 35 | # [[ 0 1 2 3 4 5 6 7 8] 36 | # [ 9 10 11 12 13 14 15 16 17] 37 | # [18 19 20 21 22 23 24 25 26]] 38 | 39 | # This is how image files can be stored... each row is a row of pixels. 40 | 41 | 42 | # Three Dimensional Arrays 43 | # ----------------------------------------------------------------------------- 44 | n = n.reshape(3, 3, 3) 45 | 46 | print(n) 47 | # [[[ 0 1 2] 48 | # [ 3 4 5] 49 | # [ 6 7 8]] 50 | # 51 | # [[ 9 10 11] 52 | # [12 13 14] 53 | # [15 16 17]] 54 | # 55 | # [[18 19 20] 56 | # [21 22 23] 57 | # [24 25 26]]] 58 | 59 | 60 | # Create numpy arrays from Python lists 61 | # ----------------------------------------------------------------------------- 62 | 63 | a = [12, 45, 255] 64 | b = [34, 45, 101] 65 | c = [122, 60, 10] 66 | 67 | n = numpy.asarray([a, b, c]) 68 | 69 | print(type(n)) # 70 | print(n) 71 | # [[ 12 45 255] 72 | # [ 34 45 101] 73 | # [122 60 10]] 74 | 75 | 76 | # Indexing, slicing, iterating arrays 77 | # ----------------------------------------------------------------------------- 78 | print(n.shape) 79 | # (3, 3) 80 | 81 | n_slice = n[1:3, 1:3] 82 | print(n_slice) 83 | # [[ 45 101] 84 | # [ 60 10]] 85 | 86 | single_value = n[1, 2] 87 | print(single_value) 88 | # 101 89 | 90 | # iterate through an array: 91 | for i in n: 92 | print(i) 93 | # [ 12 45 255] 94 | # [ 34 45 101] 95 | # [122 60 10] 96 | 97 | # iterate through columns instead for rows by using the transpose method: 98 | for i in n.T: 99 | print(i) 100 | # [ 12 34 122] 101 | # [45 45 60] 102 | # [255 101 10] 103 | 104 | # iterate through each value by using the flat method: 105 | for i in n.flat: 106 | print(i, end=', ') 107 | print('') 108 | # 12, 45, 255, 34, 45, 101, 122, 60, 10, 109 | 110 | 111 | # Stacking and splitting arrays 112 | # ----------------------------------------------------------------------------- 113 | stacked_array = numpy.hstack((n, n)) 114 | print(stacked_array) 115 | # [[ 12 45 255 12 45 255] 116 | # [ 34 45 101 34 45 101] 117 | # [122 60 10 122 60 10]] 118 | 119 | stacked_array = numpy.vstack((n, n)) 120 | print(stacked_array) 121 | # [[ 12 45 255] 122 | # [ 34 45 101] 123 | # [122 60 10] 124 | # [ 12 45 255] 125 | # [ 34 45 101] 126 | # [122 60 10]] 127 | 128 | # Note you can stack together (concatenate) as many arrays as you want 129 | # (provided they have the same dimensions): 130 | 131 | stacked_array = numpy.hstack((n, n, n.T, n.T)) 132 | print(stacked_array) 133 | # [[ 12 45 255 12 45 255 12 34 122 12 34 122] 134 | # [ 34 45 101 34 45 101 45 45 60 45 45 60] 135 | # [122 60 10 122 60 10 255 101 10 255 101 10]] 136 | 137 | split_array = numpy.hsplit(stacked_array, 2) 138 | for i in split_array: 139 | print(i) 140 | # [[ 12 45 255 12 45 255] 141 | # [ 34 45 101 34 45 101] 142 | # [122 60 10 122 60 10]] 143 | # [[ 12 34 122 12 34 122] 144 | # [ 45 45 60 45 45 60] 145 | # [255 101 10 255 101 10]] 146 | 147 | split_array = numpy.vsplit(stacked_array, 3) 148 | for i in split_array: 149 | print(i) 150 | # [[ 12 45 255 12 45 255 12 34 122 12 34 122]] 151 | # [[ 34 45 101 34 45 101 45 45 60 45 45 60]] 152 | # [[122 60 10 122 60 10 255 101 10 255 101 10]] 153 | -------------------------------------------------------------------------------- /operators.py: -------------------------------------------------------------------------------- 1 | '''Operators''' 2 | 3 | 4 | a = 4 5 | b = 5 6 | 7 | 8 | # Arithmetic operators 9 | # ----------------------------------------------------------------------------- 10 | 11 | print(a + b) # Add 12 | print(a - b) # Subtract 13 | print(a * b) # Multiply 14 | print(a / b) # Divide 15 | print(a // b) # Floor division: digits after the decimal are dropped 16 | print(a % b) # Modulus: divides and returns the remainder of the first number 17 | print(a ** b) # Exponent calculation 18 | 19 | 20 | # Comparison operators 21 | # ----------------------------------------------------------------------------- 22 | 23 | print(a > b) # Greater than 24 | print(a < b) # Less than 25 | print(a >= b) # Greater or equal to 26 | print(a <= b) # Less or equal to 27 | print(a == b) # Equal to 28 | print(a != b) # Not equal to 29 | 30 | 31 | # Assignment operators 32 | # ----------------------------------------------------------------------------- 33 | 34 | a = b # Assigns the values from the right to the left 35 | print(a) # 5 36 | a += b # Equivalent to a = a + b 37 | print(a) # 10 38 | a -= b # Equivalent to a = a - b 39 | print(a) # 5 40 | a *= b # Equivalent to a = a * b 41 | print(a) # 25 42 | a /= b # Equivalent to a = a / b 43 | print(a) # 5.0 44 | a //= b # Equivalent to a = a // b 45 | print(a) # 1.0 46 | a %= b # Equivalent to a = a % b 47 | print(a) # 1.0 48 | a **= b # Equivalent to a = a ** b 49 | print(a) # 1.0 50 | 51 | 52 | # Get // and % with divmod() 53 | # ----------------------------------------------------------------------------- 54 | 55 | a = 4 56 | b = 5 57 | 58 | c = divmod(a, b) # will return the quotient and the remainder as a tuple 59 | print(c) # (0, 4) 60 | print(type(c)) # 61 | 62 | # NOTE: when working with modulus or divmod, keep in mind that when dividing 63 | # a larger number into a smaller number, the quotient will be always be zero 64 | # and the remainder will be the first number. This may seem obvious but still 65 | # worth noting. 66 | 67 | 68 | # Assignment-expression (walrus) operator := 69 | # ----------------------------------------------------------------------------- 70 | # New to Python 3.8 and one of the things we lost Guido over, the 71 | # assignment-expression operator (walrus) allows you to make an assignment and 72 | # use the result at the same time. 73 | 74 | # For example: 75 | 76 | a = [1, 2, 3, 4, 5, 6, 7] 77 | 78 | # Without walrus: 79 | 80 | if len(a) > 5: 81 | print(f'List too long (expected 5, got {len(a)}).') 82 | # List too long (expected 5, got 7). 83 | 84 | # With walrus: 85 | 86 | if (n := len(a)) > 5: 87 | print(f'List too long (expected 5, got {n}).') 88 | # List too long (expected 5, got 7). 89 | 90 | # Another example use case could be a list comprehensions where 91 | # a value computed in the filtering condition is also needed in 92 | # the expression body. In other words, share a subexpression 93 | # between a comprehension filter clause and its output. 94 | 95 | # For example: 96 | 97 | data = ['one', 'two', 'cheese', 'xxxxxxxxxx'] 98 | 99 | def func(arg): 100 | # pretend a bunch of processing happening here 101 | if len(arg) < 10: 102 | result = arg 103 | else: 104 | result = None 105 | return result 106 | 107 | 108 | filtered = [result.title() for d in data if (result := func(d)) is not None] 109 | 110 | print(filtered) 111 | # ['One', 'Two', 'Cheese'] 112 | 113 | # The operator is also useful with while-loops that compute a value 114 | # to test loop termination and then need that same value again in the 115 | # body of the loop. 116 | 117 | # For example: 118 | 119 | def process(block): 120 | # pretend a bunch of processing happening here 121 | print(block) 122 | 123 | with open('data/example.txt', 'r') as file: 124 | while (block := file.read(5)) != '': 125 | process(block) 126 | 127 | 128 | # Dictionary merge and update operators 129 | # ----------------------------------------------------------------------------- 130 | # Python 3.9 introduced the '|' and '|=' operators for dictionaries to 131 | # "complement" existing dict.update and {**d1, **d2} methods of merging 132 | # dictionaries. 133 | 134 | d = {'spam': 1, 'eggs': 2, 'cheese': 3} 135 | e = {'cheese': 'cheddar', 'fruit': 'apple'} 136 | 137 | f = d | e 138 | # {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'fruit': 'apple'} 139 | g = e | d 140 | # {'cheese': 3, 'fruit': 'apple', 'spam': 1, 'eggs': 2} 141 | d |= e 142 | # {'spam': 1, 'eggs': 2, 'cheese': 'cheddar', 'fruit': 'apple'} 143 | -------------------------------------------------------------------------------- /package_management.md: -------------------------------------------------------------------------------- 1 | # Package Management 2 | 3 | ## Table of contents 4 | 5 | 6 | 7 | - [pip](#pip) 8 | - [Installing & upgrading packages](#installing--upgrading-packages) 9 | - [pip help](#pip-help) 10 | - [Requirements files](#requirements-files) 11 | * [pipreqs](#pipreqs) 12 | - [Dependencies](#dependencies) 13 | * [pipdeptree](#pipdeptree) 14 | 15 | 16 | 17 | ## pip 18 | 19 | *`pip`* - you can install most python packages this way 20 | *package managers* - (ie brew) work like pip but not restricted to python 21 | 22 | NOTE: Usually, pip is automatically installed if you installed Python from python.org or you're working in virtual environment. You can see where the pip command is looking, for example: 23 | 24 | A Python installation on mac os might look like: 25 | 26 | ```bash 27 | $ which pip 28 | /usr/local/bin/pip 29 | ``` 30 | 31 | If using [pyenv](https://github.com/pyenv/pyenv), the path may look like this: 32 | 33 | ```bash 34 | $ which pip 35 | /Users/jessicarush/.pyenv/shims/pip 36 | ``` 37 | 38 | If activating a virtual environment, it should point to the `venv` directory in our project folder: 39 | 40 | ```bash 41 | $ source venv/bin/activate 42 | $ which pip 43 | /Users/jessicarush/Documents/Coding/Python/github_python/venv/bin/pip 44 | ``` 45 | 46 | You can check the path and the version with `pip --version`: 47 | 48 | ```bash 49 | $ pip --version 50 | pip 21.2.4 from /Users/jessicarush/.pyenv/versions/3.10.2/lib/python3.10/site-packages/pip (python 3.10) 51 | ``` 52 | 53 | to upgrade pip (the order matters): 54 | 55 | ```bash 56 | $ pip install --upgrade pip 57 | ``` 58 | 59 | 60 | ## Installing & upgrading packages 61 | 62 | Install the most recent version of something: 63 | 64 | ```bash 65 | $ pip install flask 66 | ``` 67 | 68 | Upgrade an already installed package: 69 | 70 | ```bash 71 | $ pip install --upgrade flask 72 | ``` 73 | 74 | Install a particular version: 75 | 76 | ```bash 77 | $ pip install flask==2.0.3 78 | ``` 79 | 80 | Install a minimum version: 81 | 82 | ```bash 83 | $ pip install flask>==0.9.0 84 | ``` 85 | 86 | To see where a package is installed: 87 | 88 | ```bash 89 | $ pip show flask 90 | ``` 91 | 92 | Run the following command to check if any of your packages can be updated. 93 | 94 | ```bash 95 | $ pip list --outdated 96 | ``` 97 | 98 | There's another library that does this way better. It shows which packages have a minor vs major updates and formats the output really nice: 99 | 100 | ```bash 101 | $ pip install pip-check 102 | $ pip-check 103 | ``` 104 | 105 | 106 | ## pip help 107 | 108 | To access pip's help documentation you can type `pip help` in the command line. You can also follow that with a specific command you want help with, for example: 109 | 110 | ```bash 111 | pip help install 112 | ``` 113 | 114 | This will give you all sorts of useful stuff including what all the various flags mean. 115 | 116 | 117 | ## Requirements files 118 | 119 | [Requirements files](https://pip.pypa.io/en/latest/user_guide/#requirements-files) are files containing a list of packages to be installed using pip install. Logically, a requirements file is just a list of pip install arguments placed in a file. Note that you should not rely on the items in the file being installed by pip in any particular order. 120 | 121 | Requirements files are used to hold the result from `pip freeze` for the purpose of achieving repeatable installations. Your requirement file contains a pinned version of everything that was installed when pip freeze was run. 122 | 123 | To list all the installed packages: 124 | 125 | ```bash 126 | $ pip freeze 127 | ``` 128 | 129 | To output the list to a file: 130 | 131 | ```bash 132 | $ pip freeze > requirements.txt 133 | ``` 134 | 135 | To install from the file: 136 | 137 | ```bash 138 | $ pip install -r requirements.txt 139 | ``` 140 | 141 | NOTE: `pip freeze` saves all packages that are currently installed in the environment. 142 | 143 | NOTE: `pip freeze` only lists the packages that were installed using pip in your environment. If you installed other packages in a different way, or you need to add something that's required for the production server, just add it manually to the `requirements.txt` file. 144 | 145 | ### pipreqs 146 | 147 | [pipreqs](https://github.com/bndr/pipreqs) is a package used to generate a `requirements.txt` file for any project based on imports. 148 | 149 | ```bash 150 | $ pip install pipreqs 151 | ``` 152 | 153 | Once pipreqs is installed: 154 | 155 | ```bash 156 | $ pipreqs path/to/project/directory 157 | ``` 158 | 159 | That being said, the github page reports quite a few open issues. You're better off starting with a clean virtual environment and using pip to install everything you need as you go, thereby ensuring your pip freeze will be accurate and up-to-date. 160 | 161 | 162 | ## Dependencies 163 | 164 | This will list all package dependencies: 165 | 166 | ```bash 167 | $ pip show flask | grep Requires 168 | Requires: click, Jinja2, Werkzeug, itsdangerous 169 | ``` 170 | 171 | ### pipdeptree 172 | 173 | The [pipdeptree](https://github.com/naiquevin/pipdeptree) package will show the whole family tree of dependencies in your environment. 174 | 175 | ```bash 176 | $ pip install pipdeptree 177 | ``` 178 | 179 | Just run the command: 180 | 181 | ```bash 182 | $ pipdeptree 183 | ``` 184 | 185 | Requirements files are used to force pip to properly resolve dependencies. As it is now, pip doesn't have true dependency resolution, but instead simply uses the first specification it finds for a project. For example, if pkg1 requires pkg3 >=1.0 and pkg2 requires pkg3 >=1.0, <=2.0, and if pkg1 is resolved first, pip will only use pkg3 >=1.0, and could easily end up installing a version of pkg3 that conflicts with the needs of pkg2. To solve this problem, you can place pkg3 >=1.0, <=2.0 (the correct specification) into your requirements file directly along with the other top level requirements. Like so: 186 | 187 | ```text 188 | pkg1 189 | pkg2 190 | pkg3>=1.0,<=2.0 191 | ``` 192 | -------------------------------------------------------------------------------- /postgreSQL_example.py: -------------------------------------------------------------------------------- 1 | '''PostgreSQL''' 2 | 3 | 4 | # To work with this database server, first you'll need to download the 5 | # installer: https://www.postgresql.org/ 6 | 7 | # Launch the installer and it will take you into the 'setup wizard'. 8 | # Choose your installation directory, data directory, (by default these are 9 | # /Library/PostgreSQL/10/data). Then you'll need to create a superuser 10 | # password. The superuser is 'postgres' by default but you select the password. 11 | # It's a good idea to record all this in a credentials file including the 12 | # next item which is port. By default is: 5432. 13 | 14 | # The next question 'Set the locale to be used by the new database cluster'. 15 | # There's a good description of database clusters here: 16 | # https://www.postgresql.org/docs/current/static/creating-cluster.html 17 | # Locale refers to an application respecting cultural preferences regarding 18 | # alphabets, sorting, number formatting, etc. If your system is already set to 19 | # use the locale that you want in your database cluster then there is nothing 20 | # else you need to do. If you want to use a different locale you can select 21 | # something else from the list. Note, you can't change this afterwards. 22 | 23 | # After the main install completes, it will ask you if you want some additional 24 | # tools (Stack Builder). There's bunch of add-ons available here. You can 25 | # access this current list of add-ons at any time by launching stackbuilder: 26 | # /Library/PostgreSQL/10/stackbuilder 27 | 28 | # Next, you'll need to use the pgAdmin tool to create a database. Once the 29 | # dashboard tool opens, right click on the PostgreSQL server in the left 30 | # panel and choose 'Connect Server'. Type your password. Once connected, 31 | # pop open the Databases group and you'll see one default database called 32 | # 'postgres'. To create a new one, right click on the 'Databases' group and 33 | # choose 'Create' > database. 34 | 35 | # You could use the included tools in this directory to interact with the 36 | # PostgreSQL database, or you could use python with the help of an external 37 | # library psycopg2: http://initd.org/psycopg/ 38 | 39 | # NOTE: once you've created the database, you can check out the table in 40 | # the pgAdmin tool. Navigate to your database > Schemas > Public > Tables 41 | # There's also a query tool that allows you to query your database. 42 | 43 | # NOTE: for some reason PostgreSQL doesn't like the syntax of using ? as 44 | # placeholder values. It prefers %s instead! 45 | 46 | # NOTE: do not use the name 'user' for a table name as is is a reserved word 47 | # in postgresql. 'users' seems to work fine. 48 | 49 | import psycopg2 50 | 51 | 52 | db = ("dbname='test1'" 53 | "user='postgres'" 54 | "password='your-password'" 55 | "host='localhost'" 56 | "port='5432'") 57 | 58 | 59 | def create(): 60 | conn = psycopg2.connect(db) 61 | curs = conn.cursor() 62 | curs.execute('''CREATE TABLE IF NOT EXISTS inventory 63 | (item TEXT, quantity INT, cost FLOAT)''') 64 | conn.commit() 65 | curs.close() 66 | conn.close() 67 | 68 | 69 | def insert(item, quantity, cost): 70 | conn = psycopg2.connect(db) 71 | curs = conn.cursor() 72 | curs.execute("INSERT INTO inventory VALUES (%s, %s, %s)", 73 | (item, quantity, cost)) 74 | curs.connection.commit() 75 | curs.close() 76 | conn.close() 77 | 78 | 79 | def display(): 80 | conn = psycopg2.connect(db) 81 | curs = conn.cursor() 82 | curs.execute('SELECT * FROM inventory') 83 | rows = curs.fetchall() 84 | curs.close() 85 | conn.close() 86 | return rows 87 | 88 | 89 | def delete(item): 90 | conn = psycopg2.connect(db) 91 | curs = conn.cursor() 92 | curs.execute('DELETE FROM inventory WHERE item=%s', (item,)) 93 | curs.connection.commit() 94 | curs.close() 95 | conn.close() 96 | 97 | 98 | def update(quantity, cost, item): 99 | conn = psycopg2.connect(db) 100 | curs = conn.cursor() 101 | curs.execute('UPDATE inventory SET quantity=%s, cost=%s WHERE item=%s', 102 | (quantity, cost, item)) 103 | curs.connection.commit() 104 | curs.close() 105 | conn.close() 106 | 107 | 108 | create() 109 | insert('Dolphin', 5, 1000) 110 | # insert('Rocks', 5, 2) 111 | # insert('Dice', 100, 0.5) 112 | # delete('Dice') 113 | update(5000, 0.5, 'Giraffe') 114 | print(display()) 115 | 116 | 117 | 118 | # To compare: sqlite3 119 | # ----------------------------------------------------------------------------- 120 | # The following is the same code but written for sqlite3: 121 | 122 | import sqlite3 123 | 124 | db = 'data/test.db' 125 | 126 | def create(): 127 | conn = sqlite3.connect(db) 128 | curs = conn.cursor() 129 | curs.execute('''CREATE TABLE IF NOT EXISTS inventory 130 | (item TEXT, quantity INT, cost FLOAT)''') 131 | conn.commit() 132 | curs.close() 133 | conn.close() 134 | 135 | 136 | def insert(item, quantity, cost): 137 | conn = sqlite3.connect(db) 138 | curs = conn.cursor() 139 | curs.execute('INSERT INTO inventory VALUES (?, ?, ? )', 140 | (item, quantity, cost)) 141 | curs.connection.commit() 142 | curs.close() 143 | conn.close() 144 | 145 | 146 | def display(): 147 | conn = sqlite3.connect(db) 148 | curs = conn.cursor() 149 | curs.execute('SELECT * FROM inventory') 150 | rows = curs.fetchall() 151 | curs.close() 152 | conn.close() 153 | return rows 154 | 155 | 156 | def delete(item): 157 | conn = sqlite3.connect(db) 158 | curs = conn.cursor() 159 | curs.execute('DELETE FROM inventory WHERE item=?', (item,)) 160 | curs.connection.commit() 161 | curs.close() 162 | conn.close() 163 | 164 | 165 | def update(quantity, cost, item): 166 | conn = sqlite3.connect(db) 167 | curs = conn.cursor() 168 | curs.execute('UPDATE inventory SET quantity=?, cost=? WHERE item=?', 169 | (quantity, cost, item)) 170 | curs.connection.commit() 171 | curs.close() 172 | conn.close() 173 | 174 | 175 | create() 176 | # insert('Coffee', 25, 10.5) 177 | # insert('Rocks', 5, 2) 178 | # insert('Dice', 100, 0.5) 179 | # delete('Rocks') 180 | update(100, 0.5, 'Dice') 181 | print(display()) 182 | -------------------------------------------------------------------------------- /postman.md: -------------------------------------------------------------------------------- 1 | # Postman App for API testing 2 | 3 | 4 | https://www.getpostman.com/ 5 | 6 | Postman is an API development application that makes it easy to create, save and test your API requests during development. Once you've downloaded the software for your OS, launch it. You don't have to create an account but you should create a project folder in collections. Note you can also create sub folders. Once you're ready to test: 7 | 8 | - Make sure your web server is running. 9 | - Choose the http verb you want to test (GET, POST, etc). 10 | - Enter the address (ie http://localhost:5000/store). 11 | - Press the send button to test out your request. 12 | - Save your requests to the appropriate collection/sub-collection so you don't need to type it out again. 13 | - Once saved, select it in the sidebar to edit or duplicate it to create more variations. 14 | 15 | Testing GET requests is pretty straightforward but testing POST requests requires a little more information. Go to the headers tab (the first thing flask does when it gets a request, is look at the headers to see what sort of data it's getting). Here you can add key-value pairs like: 16 | 17 | ``` 18 | Content-Type application/json 19 | ``` 20 | 21 | With this information in, we can now go to the body tab and enter in the data that we want to send with the POST request. For example, choose the 'raw' option and type something like: 22 | 23 | ``` 24 | {"name": "Flying Potato"} 25 | ``` 26 | 27 | > :warning: JSON always uses double quotes. 28 | 29 | If you're working with JWT for authorization, you will need to add the token by clicking on the headers tab, type Authorization for the key and for the value type JWT followed by one space, then the token (no quotes). 30 | 31 | ``` 32 | JWT eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9... 33 | ``` 34 | 35 | > :bookmark: If you're sick of seeing/typing the main part of the url in your endpoints, you can always set up an environment variable. When you do this, all your endpoints can be changed from the first example to the second: 36 | 37 | ``` 38 | http://localhost:5000/item/ 39 | {{url}}/item/ 40 | ``` 41 | 42 | To set up an environment variable in postman, first you'll need to create a new environment. Click the 'eye' icon in the top right corner. Add an environment, give it a name. You can switch environments using the select menu beside the eye icon. 43 | 44 | In the same menu, you can add global variables to your active environment. For example, with the url above we'd set `url` as they variable and `http://localhost:5000` as the value. Don't forget to hit the **save** button. 45 | 46 | You can set up something similar for JWT tokens so you're not copying and pasting all the time. Where you would normally enter the token value, enter instead: 47 | 48 | ``` 49 | JWT {{token}} 50 | ``` 51 | 52 | Go to the end point that produces the token {{url}}/auth and click on the Tests tab. This area is for Javascript code. It will run after the request has been processed by the server and sent back to us. What we can do in the test area is use the token thats just been issued and use it to change the environment variable {{token}}. In the test area: 53 | 54 | ```Javascript 55 | pm.test("Access Token Test", function () { 56 | var jsonData = pm.response.json(); 57 | pm.expect(jsonData.access_token).to.not.eql(null); 58 | pm.environment.set("token", jsonData.access_token); 59 | }); 60 | ``` 61 | 62 | This comes from the snippets: "Response Body:JSON value check" and "Set an environment variable". 63 | 64 | Now when you send the /auth endpoint, you should see "Test Results 1/1" passed at the bottom where the return data is displayed. And, when you send your endpoint that requires the token, it should now pick up the current environment variable. Note you can always see your variables by clicking the eye icon. 65 | -------------------------------------------------------------------------------- /pyenv.md: -------------------------------------------------------------------------------- 1 | # pyenv 2 | 3 | See first: [pyenv docs](https://github.com/pyenv/pyenv) 4 | 5 | These instructions come from this [intro to pyenv article](https://realpython.com/intro-to-pyenv/), but [this blog post](https://switowski.com/blog/pyenv) also looks good. 6 | 7 | ## Table of Contents 8 | 9 | 10 | 11 | - [Install pyenv](#install-pyenv) 12 | - [Install python versions](#install-python-versions) 13 | - [Remove a version](#remove-a-version) 14 | - [Using your versions](#using-your-versions) 15 | - [Other commands](#other-commands) 16 | 17 | 18 | 19 | ## Install pyenv 20 | 21 | Install dependencies: 22 | 23 | ```bash 24 | brew install openssl readline sqlite3 xz zlib 25 | ``` 26 | 27 | Install pyenv: 28 | 29 | ```bash 30 | curl https://pyenv.run | bash 31 | ``` 32 | 33 | Note: the most recent instruction in the pyenv docs say you can just do this: 34 | 35 | ```bash 36 | brew update 37 | brew install pyenv 38 | ``` 39 | 40 | There will be a message in the console saying that you need to update your PATH. Be sure to read because the instructions seem to change as they update pyenv. 41 | 42 | Basically, I have to add to my `.bash_profile`: 43 | 44 | ```bash 45 | export PATH="$HOME/.pyenv/shims:$PATH" 46 | ``` 47 | 48 | However, they have a longer, roundabout way of doing this: 49 | 50 | ```bash 51 | export PYENV_ROOT="$HOME/.pyenv" 52 | export PATH="$PYENV_ROOT/bin:$PATH" 53 | eval "$(pyenv init --path)" 54 | eval "$(pyenv init -)" 55 | ``` 56 | 57 | The reasons are explained in the [pyenv docs](https://github.com/pyenv/pyenv#advanced-configuration). 58 | 59 | Be sure to restart `.bash_profile`: 60 | 61 | ```bash 62 | source ~/.bash_profile 63 | ``` 64 | 65 | ## Install python versions 66 | 67 | List all versions (warning: there's a lot): 68 | 69 | ```bash 70 | pyenv install --list 71 | ``` 72 | 73 | List specific versions: 74 | 75 | ```bash 76 | pyenv install --list | grep " 3\.[678]" 77 | ``` 78 | 79 | Install a version: 80 | 81 | ```bash 82 | pyenv install -v 3.7.8 83 | ``` 84 | 85 | Each version installed with pyenv is located in your pyenv root directory: 86 | 87 | ```bash 88 | ls ~/.pyenv/versions/ 89 | ``` 90 | 91 | ## Remove a version 92 | 93 | Check the versions you have first: 94 | 95 | ```bash 96 | ls ~/.pyenv/versions/ 97 | ``` 98 | 99 | Then remove one like this: 100 | 101 | ```bash 102 | rm -rf ~/.pyenv/versions/2.7.15 103 | ``` 104 | 105 | or like this: 106 | 107 | ```bash 108 | pyenv uninstall 2.7.15 109 | ``` 110 | 111 | ## Using your versions 112 | 113 | First check what you have: 114 | 115 | ```bash 116 | pyenv versions 117 | * system (set by /Users/jessicarush/.pyenv/version) 118 | 3.5.9 119 | 3.6.11 120 | 3.7.8 121 | 3.8.5 122 | ``` 123 | 124 | The asterisk indicates which version is currently running. In the above output it's the system os version (2.7.16). 125 | 126 | To switch you can use the `global` command: 127 | 128 | ```bash 129 | pyenv global 3.7.8 130 | ``` 131 | 132 | now check: 133 | 134 | ```bash 135 | pyenv versions 136 | system 137 | 3.5.9 138 | 3.6.11 139 | * 3.7.8 (set by /Users/jessicarush/.pyenv/version) 140 | 3.8.5 141 | ``` 142 | 143 | A great way to get peace of mind that the version of Python you just installed is working properly is to run the built-in test suite: 144 | 145 | ```bash 146 | python -m test 147 | ``` 148 | 149 | If you ever want to go back to the system version: 150 | 151 | ```bash 152 | pyenv global system 153 | ``` 154 | 155 | ## Other commands 156 | 157 | Note, you will need to update pyenv to see new python versions to install with `pyenv install --list`. To update pyenv: 158 | 159 | ```bash 160 | pyenv update 161 | ``` 162 | 163 | Note that if you want to see `which python`, there will be a pyenv shim in place: 164 | 165 | ```bash 166 | which python 167 | /Users/jessicarush/.pyenv/shims/python 168 | ``` 169 | 170 | To see the actual path, you can run the following: 171 | 172 | ```bash 173 | pyenv which python 174 | /usr/bin/python 175 | ``` 176 | 177 | To see a complete list of pyenv commands: 178 | 179 | ```bash 180 | pyenv commands 181 | ``` 182 | 183 | Each command has a `--help` flag that will give you more detailed information. 184 | 185 | See also: [pyenv commands reference](https://github.com/pyenv/pyenv/blob/master/COMMANDS.md#pyenv-global) 186 | -------------------------------------------------------------------------------- /pygal_github_api_example.py: -------------------------------------------------------------------------------- 1 | '''Data visualization using a web API''' 2 | 3 | 4 | # A program that downloads current information about the most-starred 5 | # Python projects in GitHub. Some helpful links: 6 | 7 | # https://api.github.com 8 | # https://developer.github.com/v3/search/ 9 | # https://help.github.com/articles/searching-repositories/ 10 | # https://help.github.com/articles/understanding-the-search-syntax/ 11 | # http://docs.python-requests.org 12 | 13 | import requests 14 | import pygal 15 | from pygal.style import LightColorizedStyle as LCS, LightenStyle as LS 16 | 17 | 18 | # Processing an API response 19 | # ----------------------------------------------------------------------------- 20 | 21 | # make an API call and store the requests: 22 | url = 'https://api.github.com/search/repositories?q=language:python&sort=stars' 23 | r = requests.get(url) 24 | 25 | # It's always go to check the status - use these to make some asserts. 26 | print('Status code:', r.status_code) 27 | print('Headers:', r.headers['content-type']) 28 | print('Encoding:', r.encoding) 29 | # Status code: 200 (a status code of 200 indicates a successful response) 30 | # Headers: application/json; charset=utf-8 31 | # Encoding: utf-8 32 | 33 | # Store API response in a variable. The API returns info in JSON format so we 34 | # use the json() method to convert to a python dict. 35 | response_dict = r.json() 36 | print(type(response_dict)) # -> 37 | 38 | # process results: 39 | print(response_dict.keys()) 40 | print('Total repositories:', response_dict['total_count']) 41 | print('Incomplete results:', response_dict['incomplete_results']) 42 | print('Repositories returned:', len(response_dict['items'])) 43 | # dict_keys(['total_count', 'incomplete_results', 'items']) 44 | # Incomplete results: False 45 | # Total repositories: 2077471 46 | # Repositories returned: 30 47 | 48 | repo_dicts = response_dict['items'] 49 | 50 | # examine the first repo: 51 | # repo_dict1 = repo_dicts[0] 52 | # print('\nKeys:', len(repo_dict1)) 53 | # for key in sorted(repo_dict1.keys()): 54 | # print(key, '–', repo_dict1[key]) 55 | 56 | 57 | # Summarizing the top repos 58 | # ----------------------------------------------------------------------------- 59 | 60 | print('\nSome information about the top 30 repository:') 61 | for repo_dict in repo_dicts: 62 | print('\nName:', repo_dict['name']) 63 | print('Owner:', repo_dict['owner']['login']) 64 | print('Stars:', repo_dict['stargazers_count']) 65 | print('Repository:', repo_dict['html_url']) 66 | print('Created:', repo_dict['created_at']) 67 | print('Updated:', repo_dict['updated_at']) 68 | print('Description:', repo_dict['description']) 69 | 70 | 71 | # Monitoring API rate limits 72 | # ----------------------------------------------------------------------------- 73 | 74 | # most APIs are rate-limited, which means there's a limit to how many requests 75 | # you can make in a certain amount of time. To see if you're approaching 76 | # GitHub's limits: https://api.github.com/rate_limit 77 | 78 | 79 | # Prep the data for plotting: 80 | # ----------------------------------------------------------------------------- 81 | # 82 | # names, stars = [], [] 83 | # for repo_dict in repo_dicts: 84 | # names.append(repo_dict['name']) 85 | # stars.append(repo_dict['stargazers_count']) 86 | 87 | # NOTE: You can add custom tooltips by passing a list of dictionaries instead 88 | # of a list of values to chart.add() like so: 89 | # plot_dict = [ 90 | # {'value': 1314, 'label': 'Description...'}, 91 | # {'value': 5670, 'label': 'Description...'}, 92 | # {'value': 3224, 'label': 'Description...'},] 93 | 94 | names, plot_dicts = [], [] 95 | for repo_dict in repo_dicts: 96 | names.append(repo_dict['name']) 97 | # get the description if one is available: 98 | description = repo_dict['description'] 99 | if not description: 100 | description = 'No description provided' 101 | if len(description) > 118: 102 | description = description[:118] + '...' 103 | 104 | # NOTE: pygal uses the value given for 'xlink' to turn each bar into an active 105 | # link to the given url. You can also add links to the legend. See the docs: 106 | # http://pygal.org/en/stable/documentation/configuration/value.html#legend 107 | 108 | plot_dict = { 109 | 'value': repo_dict['stargazers_count'], 110 | 'label': description, 111 | 'xlink' : repo_dict['html_url'] 112 | } 113 | plot_dicts.append(plot_dict) 114 | 115 | 116 | # Visualizing the repos with pygal 117 | # ----------------------------------------------------------------------------- 118 | 119 | my_style = LS('#25bec4', base_style=LCS) 120 | my_style.title_font_family = 'Helvetica' 121 | 122 | # NOTE: google fonts will only render when the svg is embedded because the 123 | # google stylesheet is added in the XML processing. 124 | # my_style.title_font_family = 'googlefont:Slabo' 125 | 126 | my_style.title_font_size = 14 127 | my_style.foreground = '#586e75' 128 | my_style.foreground_strong = '#334145' 129 | my_style.foreground_subtle = '#fdca32' 130 | my_style.label_font_size = 9 131 | my_style.major_label_font_size = 12 132 | my_style.tooltip_font_size = 11 133 | 134 | my_config = pygal.Config() 135 | my_config.show_legend = False 136 | my_config.truncate_label = 15 137 | my_config.show_y_guides = True 138 | my_config.width = 1000 139 | 140 | my_config.y_labels = list(range(0, 42000, 2000)) 141 | my_config.y_labels_major_count = 3 142 | my_config.x_labels = names 143 | my_config.x_label_rotation = 45 144 | 145 | chart = pygal.Bar(my_config, style=my_style) 146 | chart.title = "Most-Starred Python Projects on GitHub" 147 | 148 | # chart.add('', stars) 149 | chart.add('', plot_dicts) 150 | chart.render_to_file('api_pygal_example.svg') 151 | -------------------------------------------------------------------------------- /pygal_hn_api_example.py: -------------------------------------------------------------------------------- 1 | '''Exploring API Calls''' 2 | 3 | 4 | # The following call returns information about an article on hacker news: 5 | # https://hacker-news.firebaseio.com/v0/item/9884165.json 6 | 7 | # The dictionary contains of number of useful keys like title, url, score. 8 | # The 'descendants' key contains the number of comments an article has. 9 | 10 | import json 11 | from operator import itemgetter 12 | 13 | import requests 14 | import pygal 15 | from pygal.style import DefaultStyle as DS, LightenStyle as LS 16 | 17 | 18 | # Make an API call and store the response: 19 | # ----------------------------------------------------------------------------- 20 | 21 | # This API returns a list containing the IDs of top 500 articles on HN: 22 | url = 'https://hacker-news.firebaseio.com/v0/topstories.json' 23 | r = requests.get(url) 24 | 25 | print('Status code:', r.status_code) 26 | print('Headers:', r.headers['content-type']) 27 | print('Encoding:', r.encoding) 28 | 29 | 30 | # Process information about each submission: 31 | # ----------------------------------------------------------------------------- 32 | 33 | submission_ids = r.json() 34 | submission_dicts = [] 35 | for submission_id in submission_ids[:100]: 36 | # make a separate API call for each submission: 37 | url = ('https://hacker-news.firebaseio.com/v0/item/' 38 | + str(submission_id) + '.json') 39 | submission_r = requests.get(url) 40 | response_dict = submission_r.json() 41 | 42 | submission_dict = { 43 | 'title' : response_dict['title'], 44 | 'link': 'http://news.ycombinator.com/item?id=' + str(submission_id), 45 | 'comments': response_dict.get('descendants', 0), 46 | 'id' : str(submission_id) 47 | } 48 | submission_dicts.append(submission_dict) 49 | 50 | 51 | # Store the dict as a JSON to reuse while testing: 52 | # ----------------------------------------------------------------------------- 53 | 54 | with open('hacker_news_api.json', 'w') as fob: 55 | json.dump(submission_dicts, fob) 56 | 57 | with open('hacker_news_api.json') as fob: 58 | submission_dicts = json.load(fob) 59 | 60 | 61 | # Sort the information: 62 | # ----------------------------------------------------------------------------- 63 | 64 | submission_dicts = sorted(submission_dicts, key=itemgetter('comments'), 65 | reverse=True) 66 | 67 | submission_dicts = list(submission_dicts[15:0:-1]) 68 | 69 | titles, plot_dicts = [], [] 70 | for submission_dict in submission_dicts: 71 | title = submission_dict['title'] 72 | if len(title) > 100: 73 | title = title[:100] + '...' 74 | titles.append(title) 75 | 76 | plot_dict = { 77 | 'value': submission_dict['comments'], 78 | 'label': submission_dict['id'], 79 | 'xlink' : submission_dict['link'], 80 | 'title' : title 81 | } 82 | plot_dicts.append(plot_dict) 83 | 84 | 85 | 86 | # Style info: 87 | # ----------------------------------------------------------------------------- 88 | 89 | style = LS('#25bec4', base_style=DS) 90 | 91 | # style.plot_background = '#586e75' 92 | # style.background = '#586e75' 93 | style.foreground = '#586e75' # all labels, guides 94 | style.foreground_strong = '#13434f' # major labels and title 95 | # style.foreground_subtle = '#fdca32' # minor guides 96 | 97 | # style.font_family = 'Helvetica' 98 | style.title_font_family = 'Helvetica' 99 | style.label_font_family = 'Helvetica' 100 | # style.major_label_font_family = 'Helvetica' 101 | # style.value_font_family = 'Helvetica' 102 | # style.value_label_font_family = 'Helvetica' 103 | # style.tooltip_font_family = 'Helvetica' 104 | # style.legend_font_family = 'Helvetica' 105 | # style.no_data_font_family = 'Helvetica' 106 | 107 | style.title_font_size = 18 108 | style.label_font_size = 13 109 | style.major_label_font_size = 10 110 | # style.value_font_size = 8 111 | # style.value_label_font_size = 8 112 | # style.tooltip_font_size = 11 113 | # style.legend_font_size = 10 114 | # style.no_data_font_size = 48 115 | 116 | # style.guide_stroke_dasharray = '2, 4' 117 | style.major_guide_stroke_dasharray = '2, 4' 118 | style.opacity = '.6' 119 | style.opacity_hover = '.9' 120 | # style.transition = '150ms' 121 | # style.colors = ('#E853A0', '#E8537A', '#E95355', '#E87653', '#E89B53') 122 | # style.value_colors = () 123 | 124 | 125 | # Config info: 126 | # ----------------------------------------------------------------------------- 127 | 128 | config = pygal.Config() 129 | config.width = 1200 130 | # config.height = 500 131 | # config.spacing = 50 132 | # config.x_title = 'x-axis' 133 | # config.y_title = 'y-axis' 134 | # config.human_readable = True # for complex or large numbers 135 | # config.stroke = False # for line graphs 136 | # config.fill = True # for line graphs 137 | # config.show_dots = False # for line graphs 138 | # config.dots_size = 5 # for line graphs 139 | # config.rounded_bars = 20 # for bar graphs 140 | # config.half_pie = True # for pie charts 141 | # config.inner_radius = .6 # for pie charts 142 | 143 | # config.margin = 0 144 | # config.margin_top = 10 145 | config.margin_right = 100 146 | # config.margin_bottom = 10 147 | config.margin_left = -40 148 | 149 | config.show_legend = False 150 | # config.truncate_legend = -1 151 | # config.legend_box_size = 15 152 | # config.legend_at_bottom = True 153 | # config.legend_at_bottom_columns = 4 154 | 155 | config.show_x_labels = True 156 | config.x_labels = titles 157 | # config.x_label_rotation = 45 158 | # config.x_labels_major = [0, 5, 10] 159 | # config.x_labels_major_every = 3 160 | # config.x_labels_major_count = 5 161 | # config.show_minor_x_labels = True 162 | 163 | config.show_y_labels = True 164 | # config.y_labels = names 165 | # config.y_label_rotation = 45 166 | # config.y_labels_major = [0, 50, 100] 167 | config.y_labels_major_every = 1 168 | # config.y_labels_major_count = 2 169 | config.show_minor_y_labels = True 170 | 171 | # config.truncate_label = 15 172 | 173 | # config.inverse_y_axis = True 174 | # config.inverse_x_axis = True 175 | # config.print_values = True 176 | # config.dynamic_print_values = True 177 | # config.tooltip_border_radius = 3 178 | # config.show_y_guides = True 179 | # config.show_x_guides = True 180 | 181 | 182 | # Create and populate the plot: 183 | # ----------------------------------------------------------------------------- 184 | 185 | chart = pygal.HorizontalBar(config, style=style) 186 | chart.title = "Most-commented articles on Hacker News top 100" 187 | 188 | chart.add('', plot_dicts) 189 | 190 | chart.render_to_file('hacker_news_api.svg') 191 | -------------------------------------------------------------------------------- /pygal_json_example.py: -------------------------------------------------------------------------------- 1 | '''Pygal - Mapping Global Data Sets: JSON format''' 2 | 3 | 4 | # The data in this example comes from: 5 | # http://data.okfn.org/ 6 | 7 | # To use the pygal world maps, you need to install it: 8 | # pip install pygal_maps_world 9 | 10 | import json 11 | import pygal 12 | from pygal.maps.world import COUNTRIES 13 | from pygal.maps.world import World 14 | from pygal.style import Style 15 | 16 | 17 | # Explore/Analyze the data 18 | # ----------------------------------------------------------------------------- 19 | 20 | # load the data into a list: 21 | filename = 'data/population_data.json' 22 | with open(filename) as fob: 23 | pop_data = json.load(fob) 24 | 25 | # print one year for each country: 26 | for pop_dict in pop_data: 27 | if pop_dict['Year'] == '2010': 28 | country_name = pop_dict['Country Name'] 29 | # some of the populations strings have decimals. In order to 30 | # convert them all to ints, we need to convert to floats first. 31 | population = int(float(pop_dict['Value'])) 32 | print(country_name, '–', population) 33 | 34 | 35 | # Extract the data 36 | # ----------------------------------------------------------------------------- 37 | # To use pygals mapping tools, countries need to be provided as country codes. 38 | # Pygals country codes are stored in a module called pygal_maps_world is a 39 | # dictionary called COUNTRIES. You have to do a separate pip install for this: 40 | # $ pip install pygal_maps_world. Then: pygal.maps.world import COUNTRIES 41 | 42 | for country_code in sorted(COUNTRIES.keys()): 43 | print(country_code, COUNTRIES[country_code]) 44 | 45 | def get_country_code(country_name): 46 | '''Return the Pygal 2-digit country code for a given country.''' 47 | for code, name in COUNTRIES.items(): 48 | if name == country_name: 49 | return code 50 | # if the name isn't found, return None: 51 | return None 52 | 53 | # Test it out: 54 | cc_populations = {} 55 | for pop_dict in pop_data: 56 | if pop_dict['Year'] == '2010': 57 | country_name = pop_dict['Country Name'] 58 | population = int(float(pop_dict['Value'])) 59 | code = get_country_code(country_name) 60 | if code: 61 | cc_populations[code] = population 62 | else: 63 | print('Error –', country_name) 64 | 65 | # There are a quite a few Errors (obv) which fall mainly into two categories: 66 | # – some country names don't match exactly (ie Yemen, Rep vs Yemen) 67 | # – some lines in the dataset aren't by country at all but by region 68 | # (ie Arab World) or by economic group (ie all income levels). 69 | 70 | # For the moment, we'll just plot the stuff that matched. 71 | 72 | 73 | # Plot the data 74 | # ----------------------------------------------------------------------------- 75 | # group the countries into 3 population levels to have the colouring be more 76 | # informative (otherwise it just shows China and India a contrastig colour). 77 | 78 | cc_pop1, cc_pop2, cc_pop3 = {}, {}, {} 79 | for cc, pop in cc_populations.items(): 80 | if pop < 10000000: # 10 million 81 | cc_pop1[cc] = pop 82 | elif pop < 1000000000: # 1 billion 83 | cc_pop2[cc] = pop 84 | else: 85 | cc_pop3[cc] = pop 86 | 87 | # see how many countries are in each group: 88 | print(len(cc_pop1), len(cc_pop2), len(cc_pop3)) 89 | # 85, 69, 2 90 | 91 | my_style = Style( 92 | legend_font_size=9, 93 | colors=('#509aff', '#01e7d4', '#ff6e67')) 94 | 95 | wm = World(style=my_style) 96 | wm.title = 'World Population in 2010' 97 | 98 | # wm.add('North America',['ca', 'mx', 'us']) 99 | # wm.add('North America',{'ca': 34126000, 'mx': 113423000, 'us': 309349000}) 100 | # wm.add('2010', cc_populations) 101 | 102 | wm.add('0-10m', cc_pop1) 103 | wm.add('10m-1b', cc_pop2) 104 | wm.add('>1b', cc_pop3) 105 | 106 | wm.render_to_file('demos/americas.svg') 107 | -------------------------------------------------------------------------------- /recursion.py: -------------------------------------------------------------------------------- 1 | '''Recursion''' 2 | 3 | 4 | # Recursion is a way of programming or coding a problem, in which a function 5 | # calls itself one or more times in its body. Usually, it is returning the 6 | # return value of this function call. Put simply, a recursive function is a 7 | # function that calls itself. 8 | 9 | 10 | # Factorial 11 | # ----------------------------------------------------------------------------- 12 | 13 | def factorial_i(n): 14 | '''calculates n! iteratively''' 15 | # for example 5! = 5 * 4 * 3 * 2 * 1 = 120 16 | result = 1 17 | if n > 1: 18 | for f in range(2, n + 1): 19 | result *= f 20 | return result 21 | 22 | 23 | # With factorials, you can also say that 6! = 6 * 5!, which is to say you can 24 | # get the factorial of 6 by multiplying 6 * 5's factorial (120). 25 | 26 | def factorial_r(n): 27 | '''calculates n! recursively''' 28 | # n! can also be defined as n * (n-1)! 29 | if n <= 1: 30 | return 1 31 | else: 32 | return n * factorial_r(n - 1) 33 | 34 | # Termination condition: A recursive function has to terminate to be useable 35 | # in a program. A recursive function terminates, if with every recursive call 36 | # the solution of the problem is downsized and moves towards a base case. 37 | # A base case is, where the problem can be solved without further recursion. 38 | # A recursion can lead to an infinite loop, if the base case is not met. In 39 | # the example above, the base case it met by if n <= 1, return 1 40 | 41 | # testing: 42 | for i in range(6): 43 | print(i, factorial_i(i)) 44 | # 0 1 45 | # 1 1 46 | # 2 2 47 | # 3 6 48 | # 4 24 49 | # 5 120 50 | 51 | for i in range(6): 52 | print(i, factorial_r(i)) 53 | # 0 1 54 | # 1 1 55 | # 2 2 56 | # 3 6 57 | # 4 24 58 | # 5 120 59 | 60 | 61 | # Fibonacci 62 | # ----------------------------------------------------------------------------- 63 | 64 | def fib_i(n): 65 | '''Calculates fibonacci iteratively''' 66 | a, b = 0, 1 67 | for i in range(n): 68 | a, b = b, a + b 69 | return a 70 | 71 | def fib_r(n): 72 | '''Calculates fibonacci recursively''' 73 | if n < 2: 74 | return n 75 | else: 76 | return fib_r(n - 1) + fib_r(n - 2) 77 | 78 | def fib_g(n): 79 | '''Calculates fibonacci with a generator''' 80 | a, b = 0, 1 81 | counter = 0 82 | while counter < n: 83 | yield a 84 | a, b = b, a + b 85 | counter += 1 86 | 87 | # testing: 88 | for i in range(10): 89 | print(fib_i(i)) 90 | # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 91 | 92 | for i in range(10): 93 | print(fib_r(i)) 94 | # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 95 | 96 | fib_generator = fib_g(10) 97 | for i in fib_generator: 98 | print(i) 99 | # 0, 1, 1, 2, 3, 5, 8, 13, 21, 34 100 | 101 | 102 | # Directory Listings 103 | # ----------------------------------------------------------------------------- 104 | 105 | import os 106 | 107 | # os.walk() creates a generator object that yields a 3-item tuple 108 | # (dirpath, dirnames, filenames). dirpath is a string of the path to the 109 | # directory. dirnames is a list of the names of the subdirectories in dirpath 110 | # (excluding '.' and '..'). filenames is a list of the names of the 111 | # non-directory files in dirpath. 112 | 113 | # This is a non-recursive example: 114 | 115 | listing = os.walk('.') 116 | 117 | print(next(listing)) 118 | 119 | for root, directories, files in listing: 120 | print('Root: ', root) 121 | for d in directories: 122 | print('Directories: ', d) 123 | for f in files: 124 | print('Files: ', f) 125 | 126 | # here we use os.listdir and os.path to create a recursive example: 127 | 128 | def list_directory(s): 129 | def dir_list(d): 130 | nonlocal tab_stop 131 | files = os.listdir(d) 132 | for f in files: 133 | current_dir = os.path.join(d, f) 134 | if os.path.isdir(current_dir): 135 | print('\t' * tab_stop + 'Directory: ' + f) 136 | tab_stop += 1 137 | dir_list(current_dir) 138 | tab_stop -= 1 139 | else: 140 | print('\t' * tab_stop + f) 141 | 142 | tab_stop = 0 143 | if os.path.exists(s): 144 | print('Directory listing of ' + s) 145 | dir_list(s) 146 | else: 147 | print(s + ' does not exist') 148 | 149 | 150 | list_directory('./data') 151 | 152 | 153 | # Tips: 154 | # ----------------------------------------------------------------------------- 155 | # When coding/testing a recursive function, it's often helpful to insert print 156 | # statements to see what's going on. When you do this, add a tab before the 157 | # print so you can visually see your levels of recursion. 158 | -------------------------------------------------------------------------------- /resources.py: -------------------------------------------------------------------------------- 1 | '''Resources''' 2 | 3 | 4 | # Python philosophy 5 | # ----------------------------------------------------------------------------- 6 | 7 | import this 8 | 9 | 10 | # Good explanations 11 | # ----------------------------------------------------------------------------- 12 | # Python Docs- https://docs.python.org/3/ 13 | # Built-ins: https://www.programiz.com/python-programming/methods/built-in 14 | # http://simeonfranklin.com/blog/2012/jul/1/python-decorators-in-12-steps/ 15 | 16 | 17 | # When looking for code 18 | # ----------------------------------------------------------------------------- 19 | # PyPI - https://pypi.python.org/pypi 20 | # Github - https://github.com/trending?l=python 21 | # Active State - http://code.activestate.com/recipes/langs/python/ 22 | 23 | 24 | # help() 25 | # ----------------------------------------------------------------------------- 26 | # In the python interpreter, use the help function to get information on a 27 | # module or built-in function, or run the help utility to get information on 28 | # keywords, symbols, topics, modules and built-in functions. For example: 29 | 30 | help() # Welcome to Python 3.6's help utility! 31 | 32 | keywords # lists all keywords 33 | symbols # lists all symbols 34 | topics # lists all help topics 35 | modules # list all modules 36 | builtins # lists all built-in functions and exceptions 37 | 38 | yield # describes the yield statement 39 | @ # describes decorators 40 | DEBUGGING # provides information on 'pdb' the python debugger 41 | random # provides information on the random standard library module 42 | print # provides information in the print built-in function 43 | ValueError # provides information on the ValueError exception 44 | 45 | # you don't have to enter the help utility to get help either, you can pass 46 | # the name of a function or module directly (this method doesn't work for 47 | # keywords, symbols or topics). 48 | 49 | help(print) 50 | help(ValueError) 51 | import random 52 | help(random) 53 | 54 | # you can also ask for specific help on a method or class such as: 55 | 56 | help(list.extend) 57 | help(str.translate) 58 | help(set.difference) 59 | help(datetime.datetime) 60 | 61 | 62 | # dir() 63 | # ----------------------------------------------------------------------------- 64 | # The built-in dir function is an easy way to grab a list of all the attributes 65 | # available inside an object (i.e., its methods and simpler data items). It can 66 | # be called on any object that has attributes, including imported modules and 67 | # built-in types, as well as the name of a data type. 68 | 69 | import random 70 | dir(random) 71 | # [..., 'betavariate', 'choice', 'choices', 'expovariate', 'gammavariate', 72 | # 'gauss', 'getrandbits', 'getstate', 'lognormvariate', 'normalvariate', 73 | # 'paretovariate', 'randint', 'random', 'randrange', 'sample', 'seed', 74 | # 'setstate', 'shuffle', 'triangular', 'uniform', 'vonmisesvariate', 75 | # 'weibullvariate'] 76 | 77 | # To find out what attributes are provided in objects of built-in types, run 78 | # dir on a literal or an existing instance of the desired type. For example, 79 | # to see list, string, dict attributes, you can pass empty objects: 80 | 81 | dir(list) 82 | # [..., 'append', 'clear', 'copy', 'count', 'extend', 'index', 'insert', 'pop', 83 | # 'remove', 'reverse', 'sort'] 84 | dir(str) 85 | # [..., 'capitalize', 'casefold', 'center', 'count', 'encode', 'endswith', 86 | # 'expandtabs', 'find', 'format', 'format_map', 'index', 'isalnum', 'isalpha', 87 | # 'isdecimal', 'isdigit', 'isidentifier', 'islower', 'isnumeric', 'isprintable', 88 | # 'isspace', 'istitle', 'isupper', 'join', 'ljust', 'lower', 'lstrip', 89 | # 'maketrans', 'partition', 'replace', 'rfind', 'rindex', 'rjust', 90 | # 'rpartition', 'rsplit', 'rstrip', 'split', 'splitlines', 'startswith', 91 | # 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill']' 92 | dir(dict) 93 | # [..., 'clear', 'copy', 'fromkeys', 'get', 'items', 'keys', 'pop', 'popitem', 94 | # 'setdefault', 'update', 'values'] 95 | dir(tuple) 96 | # [..., 'count', 'index'] 97 | dir(set) 98 | # [..., 'add', 'clear', 'copy', 'difference', 'difference_update', 'discard', 99 | # 'intersection', 'intersection_update', 'isdisjoint', 'issubset', 'issuperset', 100 | # 'pop', 'remove', 'symmetric_difference', 'symmetric_difference_update', 101 | # 'union', 'update'] 102 | 103 | 104 | # Docstrings 105 | # ----------------------------------------------------------------------------- 106 | # Builtins, modules and more have their own docstrings, which can also provide 107 | # information. For example: 108 | 109 | print(print.__doc__) 110 | import random 111 | print(random.__doc__) 112 | print(random.choice.__doc__) 113 | 114 | 115 | # HTML PyDoc 116 | # ----------------------------------------------------------------------------- 117 | # Another way to view help similar to the help() and docstrings is to use 118 | # the built-in PyDoc HTML by running this command in terminal: 119 | 120 | # python3 -m pydoc -b 121 | 122 | # The interesting thing about this is, it will include any python modules 123 | # (.py files) it finds from the current directory you were in when you 124 | # launched it. It's actually pretty great! 125 | 126 | 127 | # View the source code 128 | # ----------------------------------------------------------------------------- 129 | # If you want to take a look at the actual file to read the comments and 130 | # docstring there, you can use the __file__ magic method to show you the 131 | # path to where this module lives: 132 | 133 | import os 134 | print(os.__file__) 135 | # /Library/Frameworks/Python.framework/Versions/3.6/lib/python3.6/os.py 136 | -------------------------------------------------------------------------------- /rounding_example.py: -------------------------------------------------------------------------------- 1 | '''Rounding Issue with Floats and Solutions''' 2 | 3 | 4 | # Floating point numbers are subject to rounding errors as they convert between 5 | # binary and decimal. One solution is to use the decimal module that comes with 6 | # python. The decimal module provides support for fast correctly-rounded 7 | # decimal floating point arithmetic. It offers several advantages over the 8 | # float datatype but will require some heavy investigation. 9 | 10 | # The other method of dealing with this rounding error is to work strictly 11 | # with integers instead of floats. This would mean multiplying by 100 and 12 | # essentially doing all calculations as pennies, then dividing by 100 to 13 | # convert back to dollars for the final show_balance. 14 | 15 | 16 | # Example 1: floats 17 | # ----------------------------------------------------------------------------- 18 | 19 | class Account1(): 20 | 21 | def __init__(self, name: str, opening_balance: float=0.0): 22 | self.name = name 23 | self._balance = opening_balance 24 | print('Account created for {}'.format(self.name)) 25 | self.show_balance() 26 | 27 | def deposit(self, amount: float): 28 | if amount > 0.0: 29 | self._balance += amount 30 | print('{} deposited'.format(amount)) 31 | return self._balance 32 | 33 | def withdraw(self, amount: float): 34 | if 0 < amount <= self._balance: 35 | self._balance -= amount 36 | print('{} withdrawn'.format(amount)) 37 | return amount 38 | else: 39 | print('Amount must be greater than 0 and not exceed balance') 40 | return 0.0 41 | 42 | def show_balance(self): 43 | print('Balance on account {} is {}'.format(self.name, self._balance)) 44 | 45 | # testing: 46 | if __name__ == '__main__': 47 | rick = Account1('Rick') 48 | rick.deposit(100.10) 49 | rick.deposit(499.10) 50 | rick.deposit(55.10) 51 | rick.withdraw(80.00) 52 | rick.show_balance() 53 | 54 | # Account created for Rick 55 | # Balance on account Rick is 0.0 56 | # 100.1 deposited 57 | # 499.1 deposited 58 | # 55.1 deposited 59 | # 80.0 withdrawn 60 | # Balance on account Rick is 574.3000000000001 61 | 62 | 63 | # Example 2: decimal module 64 | # ----------------------------------------------------------------------------- 65 | 66 | from decimal import * 67 | 68 | class Account2(): 69 | # class constant, accessible without creating an instance: 70 | _qb = Decimal('0.00') 71 | 72 | def __init__(self, name: str, opening_balance: float=0.0): 73 | self.name = name 74 | self._balance = Decimal(opening_balance).quantize(Account2._qb) 75 | print("Account created for {}. ".format(self.name)) 76 | self.show_balance() 77 | 78 | def deposit(self, amount: float): 79 | decimal_amount = Decimal(amount).quantize(Account2._qb) 80 | if decimal_amount > Account2._qb: 81 | self._balance = self._balance + decimal_amount 82 | print("{} deposited".format(decimal_amount)) 83 | return self._balance 84 | 85 | def withdraw(self, amount: float): 86 | decimal_amount = Decimal(amount).quantize(Account2._qb) 87 | if Account2._qb < decimal_amount <= self._balance: 88 | self._balance = self._balance - decimal_amount 89 | print("{} withdrawn".format(decimal_amount)) 90 | return decimal_amount 91 | else: 92 | print('Amount must be greater than 0 and not exceed balance') 93 | return Account2._qb 94 | 95 | def show_balance(self): 96 | print("Balance on account {} is {}".format(self.name, self._balance)) 97 | 98 | #testing: 99 | if __name__ == '__main__': 100 | rick = Account2('Morty') 101 | rick.deposit(100.10) 102 | rick.deposit(499.10) 103 | rick.deposit(55.10) 104 | rick.withdraw(80.00) 105 | rick.show_balance() 106 | 107 | # Account created for Morty. 108 | # Balance on account Morty is 0.00 109 | # 100.10 deposited 110 | # 499.10 deposited 111 | # 55.10 deposited 112 | # 80.00 withdrawn 113 | # Balance on account Morty is 574.30 114 | 115 | 116 | # Example 3: use integers 117 | # ----------------------------------------------------------------------------- 118 | 119 | class Account3(): 120 | 121 | def __init__(self, name: str, opening_balance: int=0): 122 | self.name = name 123 | self._balance = opening_balance 124 | print('Account created for {}'.format(self.name)) 125 | self.show_balance() 126 | 127 | def deposit(self, amount: int): 128 | if amount > 0.0: 129 | self._balance += amount 130 | print('{:.2f} deposited'.format(amount / 100)) 131 | return self._balance / 100 132 | 133 | def withdraw(self, amount: int): 134 | if 0 < amount <= self._balance: 135 | self._balance -= amount 136 | print('{:.2f} withdrawn'.format(amount / 100)) 137 | return amount / 100 138 | else: 139 | print('Amount must be greater than 0 and not exceed balance') 140 | return 0.0 141 | 142 | def show_balance(self): 143 | print('Balance on account {} is {:.2f}'.format( 144 | self.name, self._balance / 100)) 145 | 146 | # testing: 147 | if __name__ == '__main__': 148 | rick = Account3('Bob') 149 | rick.deposit(10010) 150 | rick.deposit(49910) 151 | rick.deposit(5510) 152 | rick.withdraw(8000) 153 | rick.show_balance() 154 | 155 | # Account created for Bob 156 | # Balance on account Bob is 0.00 157 | # 100.10 deposited 158 | # 499.10 deposited 159 | # 55.10 deposited 160 | # 80.00 withdrawn 161 | # Balance on account Bob is 574.30 162 | -------------------------------------------------------------------------------- /sqlite3_example2.py: -------------------------------------------------------------------------------- 1 | '''sqlite3 Example''' 2 | 3 | 4 | # This is a modified version of sqlite3_example1.py. In continuing to explore 5 | # ways of storing and retrieving both UTC and local time, this example includes 6 | # a column in the history table that contains a pickled datetime object which 7 | # is the local time zone (PDT). This pickled object can then be unpickled and 8 | # displayed later. This whole thing is probably overkill for most situations 9 | # but may prove useful as an example sometime. 10 | 11 | import sqlite3 12 | import datetime 13 | import pytz 14 | import pickle # <-- added 15 | 16 | db = sqlite3.connect('data/accounts2.sqlite') 17 | db.execute('''CREATE TABLE IF NOT EXISTS accounts 18 | (name TEXT PRIMARY KEY NOT NULL, 19 | balance INTEGER NOT NULL)''') 20 | # added timezone column to the history table: 21 | db.execute('''CREATE TABLE IF NOT EXISTS history 22 | (time TIMESTAMP NOT NULL, 23 | account TEXT NOT NULL, 24 | amount INTEGER NOT NULL, 25 | timezone INTEGER NOT NULL, 26 | PRIMARY KEY (time, account))''') 27 | 28 | 29 | class Account(): 30 | 31 | @staticmethod 32 | def _current_time(): 33 | # changed this to return both utc time and local time zone: 34 | utc_time = pytz.utc.localize(datetime.datetime.utcnow()) 35 | local_time = utc_time.astimezone() 36 | timezone = local_time.tzinfo 37 | return utc_time, timezone # <-- returns a tuple 38 | 39 | def __init__(self, name: str, opening_balance: int=0): 40 | select_query = "SELECT name, balance FROM accounts WHERE name = ?" 41 | cursor = db.execute(select_query, (name,)) 42 | row = cursor.fetchone() 43 | if row: 44 | self.name, self._balance = row 45 | print('Retrieved record for {}'.format(self.name)) 46 | else: 47 | self.name = name 48 | self._balance = opening_balance 49 | insert_query = "INSERT INTO accounts VALUES(?, ?)" 50 | cursor.execute(insert_query, (name, opening_balance)) 51 | cursor.connection.commit() 52 | print('Account created for {}'.format(self.name)) 53 | self.show_balance() 54 | 55 | def _save_update(self, amount): 56 | new_balance = self._balance + amount 57 | time, timezone = Account._current_time() # <-- unpack the tuple 58 | pickled_timezone = pickle.dumps(timezone) # <-- pickle the time zone 59 | try: 60 | db.execute("UPDATE accounts SET balance = ? WHERE name = ?", 61 | (new_balance, self.name)) 62 | # add the pickled_timezone to the update: 63 | db.execute("INSERT INTO history VALUES(?, ?, ?, ?)", 64 | (time, self.name, amount, pickled_timezone)) 65 | except sqlite3.Error: 66 | db.rollback() 67 | else: 68 | db.commit() 69 | self._balance = new_balance 70 | 71 | def deposit(self, amount: int): 72 | if amount > 0.0: 73 | self._save_update(amount) 74 | print('{:.2f} deposited - {}'.format(amount/100, self.name)) 75 | return self._balance/100 76 | 77 | def withdraw(self, amount: int): 78 | if 0 < amount <= self._balance: 79 | self._save_update(-amount) 80 | print('{:.2f} withdrawn - {}'.format(amount/100, self.name)) 81 | return amount/100 82 | else: 83 | print('Amount must be greater than 0 and not exceed balance') 84 | return 0.0 85 | 86 | def show_balance(self): 87 | print('Balance for {} is {:.2f}'.format(self.name, self._balance/100)) 88 | 89 | 90 | # Testing 91 | # ----------------------------------------------------------------------------- 92 | 93 | if __name__ == '__main__': 94 | rick = Account('Rick') 95 | morty = Account('Morty', 50000) 96 | pingpong = Account('Ping Pong', 10000) 97 | boktoktok = Account('Boktoktok', 2000) 98 | zed = Account('Zed', 900000) 99 | 100 | rick.deposit(10010) 101 | morty.withdraw(1000) 102 | zed.deposit(99900) 103 | 104 | db.close() 105 | 106 | print('-' * 75) 107 | 108 | 109 | # Get the pickled timezone 110 | # ----------------------------------------------------------------------------- 111 | 112 | db = sqlite3.connect('data/accounts2.sqlite', detect_types=sqlite3.PARSE_DECLTYPES) 113 | 114 | for row in db.execute("SELECT * FROM history"): 115 | utc_time = row[0] 116 | pickled_timezone = row[3] 117 | timezone = pickle.loads(pickled_timezone) 118 | local_time = pytz.utc.localize(utc_time).astimezone(timezone) 119 | print("{}\t{}\t{}".format(utc_time, local_time, local_time.tzinfo)) 120 | 121 | # 2017-12-01 18:45:25.093112 2017-12-01 10:45:25.093112-08:00 PST 122 | # 2017-12-01 18:45:25.095254 2017-12-01 10:45:25.095254-08:00 PST 123 | -------------------------------------------------------------------------------- /template.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | '''Docstring Summary Line, followed by a space. 4 | 5 | Followed by the extended description of the module. This may include 6 | special notes on structure and usage as well as include brief information 7 | on the modules Classes, Functions and variables. 8 | ''' 9 | 10 | import datetime as dt 11 | from random import choice 12 | 13 | 14 | __author__ = "Jessica Rush" 15 | __copyright__ = "Copyright 2017, cyan red" 16 | __credits__ = ["Scott Volk", "Someone Else"] 17 | __license__ = "GPL" 18 | __version__ = "1.0.1" 19 | __maintainer__ = "Jessica Rush" 20 | __email__ = "jesskrush@me.com" 21 | __status__ = "Production" 22 | 23 | 24 | print('template finished') 25 | 26 | # Explanation: The above is a recommended example source code file header. 27 | # The main things to note here: 28 | 29 | # 1. The first line of each file should be #!/usr/bin/env python (or the path 30 | # to wherever your installation is... see which python3 for your path). 31 | # This makes it possible to run the file as a script invoking the 32 | # interpreter implicitly, (basically, just type $ ./template.py in the 33 | # command line instead of $ python3 template.py). Note that the file 34 | # permissions need to allow for the file to be executeable. For example, 35 | # the command $ sudo chmod 744 template.py, would give the file the 36 | # permissions: -rwxr--r-- 37 | 38 | # 2. Next should be the docstring with a description. If the description is 39 | # long, the first line should be a short summary that makes sense on its 40 | # own, separated from the rest by a newline. 41 | 42 | # 3. Third should be all the import statements. Standard library first, then 43 | # third-party libraries, then your own modules. 44 | 45 | # 4. OPTIONAL: Authorship information. Note that many feel this information 46 | # should be kept out of the source file and kept in its own file(s) such 47 | # as AUTHORS and LICENSE. Status should typically be one of "Prototype", 48 | # "Development", or "Production". More information can be found here: 49 | # http://epydoc.sourceforge.net/manual-fields.html#module-metadata-variables 50 | -------------------------------------------------------------------------------- /timezones.py: -------------------------------------------------------------------------------- 1 | '''Timezones''' 2 | 3 | 4 | # A reminder: it's best to avoid working with timezones. Instead, the best 5 | # practice would be to convert any times/dates to UTC at the start, do all 6 | # your processing in UTC and then convert back to timezones only if you have 7 | # to at the end for the user. 8 | 9 | # As it turns out, the web browser knows the user's timezone, and exposes it 10 | # through the standard date and time JavaScript APIs. A good way of utilizing 11 | # this is to let the conversion from UTC to a local timezone happen in the 12 | # web client using JavaScript. There is a small open-source JavaScript library 13 | # called moment.js that handles this very well. Though not demonstrated in this 14 | # file, note that there is a Flask extension called Flask-Moment that makes it 15 | # easy to incorporate moment.js into your Flask app. 16 | 17 | 18 | # pytz module 19 | # ----------------------------------------------------------------------------- 20 | import datetime 21 | import pytz 22 | 23 | # FYI pytz pulls timezone information from this database: 24 | # https://www.iana.org/time-zones 25 | 26 | # see all timezones: 27 | for x in pytz.all_timezones: 28 | print(x) 29 | 30 | # see all country codes: 31 | for x in sorted(pytz.country_names): 32 | print(x, ':', pytz.country_names[x]) 33 | 34 | # see all country names: 35 | for x in sorted(pytz.country_names): 36 | print(f'{x}: {pytz.country_names[x]}: {pytz.country_timezones.get(x)}') 37 | 38 | # see names, zones and their times: 39 | for x in sorted(pytz.country_names): 40 | print(f'{x}: {pytz.country_names[x]}') 41 | if x in pytz.country_timezones: 42 | for zone in sorted(pytz.country_timezones[x]): 43 | tz_to_display = pytz.timezone(zone) 44 | local_time = datetime.datetime.now(tz=tz_to_display) 45 | print(f'\t{zone}: {local_time}') 46 | else: 47 | print('\tNo timezone defined') 48 | 49 | 50 | # pytz example 51 | # ----------------------------------------------------------------------------- 52 | import datetime 53 | import pytz 54 | 55 | 56 | country = "Europe/Moscow" 57 | tz = pytz.timezone(country) 58 | world_time = datetime.datetime.now(tz=tz) 59 | 60 | print(f'UTC is {datetime.datetime.utcnow()}') 61 | # UTC is 2018-03-28 19:39:09.028962 62 | 63 | print(f'The time in {country} is {world_time}') 64 | # The time in Europe/Moscow is 2018-03-28 22:39:09.028943+03:00 65 | 66 | print(f'In {country} it is {world_time.strftime("%A %x %X")} - {world_time.tzname()}') 67 | # In Europe/Moscow it is Wednesday 03/28/18 22:39:09 - MSK 68 | 69 | 70 | # convert a naive datetime to an aware datetime 71 | # ----------------------------------------------------------------------------- 72 | import datetime 73 | import pytz 74 | 75 | 76 | naive_local_time = datetime.datetime.now() 77 | naive_utc_time = datetime.datetime.utcnow() 78 | 79 | print(f'Naive local time: {naive_local_time}') 80 | print(f'Naive UTC: {naive_utc_time}') 81 | 82 | # Naive local time: 2018-03-28 12:39:09.029068 83 | # Naive UTC: 2018-03-28 19:39:09.0290701 84 | 85 | # When these next two print you can tell they are aware because they now 86 | # include an offset at the end. Both will show the same time zone and same 87 | # offset (+00:00, UTC) because the naive datetimes we supplied to it don't 88 | # carry that information. The third example shows how to get the correct local 89 | # offset and time zone: 90 | 91 | aware_local_time = pytz.utc.localize(naive_local_time) 92 | aware_utc_time = pytz.utc.localize(naive_utc_time) 93 | aware_local_time_zone = pytz.utc.localize(naive_utc_time).astimezone() 94 | 95 | print(f'Aware local time: {aware_local_time} - ' 96 | f'time zone: {aware_local_time.tzinfo}') 97 | # Aware local time: 2018-03-28 12:39:09.029068+00:00 - time zone: UTC 98 | 99 | print(f'Aware UTC: {aware_utc_time} - ' 100 | f'time zone: {aware_utc_time.tzinfo}') 101 | # Aware UTC: 2018-03-28 19:39:09.029070+00:00 - time zone: UTC 102 | 103 | print(f'Aware local time: {aware_local_time_zone} - ' 104 | f'time zone: {aware_local_time_zone.tzinfo}') 105 | # Aware local time: 2018-03-28 12:39:09.029070-07:00 - time zone: PDT 106 | 107 | 108 | # date in a timezone from epoch 109 | # ----------------------------------------------------------------------------- 110 | # Use time stamps (seconds since the epoch) to convert to actual date. 111 | # For this example we'll be supplying the timezone since an epoch number could 112 | # be from anywhere. This particular timestamp is the hour before DST in the UK 113 | # on October 25, 2015. You will see the difference before and after the DST in 114 | # the offset. 115 | 116 | s = 1445733000 117 | t = s + (60 * 60) 118 | 119 | tz = pytz.timezone("Canada/Pacific") 120 | dt1 = pytz.utc.localize(datetime.datetime.utcfromtimestamp(s)).astimezone(tz) 121 | dt2 = pytz.utc.localize(datetime.datetime.utcfromtimestamp(t)).astimezone(tz) 122 | 123 | print(f'{s} seconds since epoch is {dt1}') 124 | print(f'{t} seconds since epoch is {dt2}') 125 | 126 | # 1445733000 seconds since epoch is 2015-10-24 17:30:00-07:00 127 | # 1445736600 seconds since epoch is 2015-10-24 18:30:00-07:00 128 | -------------------------------------------------------------------------------- /timezones_example.py: -------------------------------------------------------------------------------- 1 | '''Timezones Example''' 2 | 3 | 4 | # A program that allows a user to choose one time zones from a list. 5 | # The program will then display the time in that timezone, 6 | # as well as local time & UTC time. 7 | 8 | import datetime 9 | import pytz 10 | 11 | 12 | # References 13 | # ----------------------------------------------------------------------------- 14 | # print all timezones: 15 | for x in pytz.all_timezones: 16 | print(x) 17 | 18 | # print all country names, timezones and their times: 19 | for x in sorted(pytz.country_names): 20 | print("{}: {}".format(x, pytz.country_names[x])) 21 | if x in pytz.country_timezones: 22 | for zone in sorted(pytz.country_timezones[x]): 23 | tz_to_display = pytz.timezone(zone) 24 | local_time = datetime.datetime.now(tz=tz_to_display) 25 | print("\t{}: {}:".format(zone, local_time)) 26 | else: 27 | print("\tNo timezone defined") 28 | 29 | 30 | # Program 31 | # ----------------------------------------------------------------------------- 32 | 33 | timezones = {'vancouver': 'America/Vancouver', 34 | 'luxembourg': 'Europe/Luxembourg', 35 | 'hong kong': 'Hongkong', 36 | 'switzerland': 'Europe/Zurich', 37 | 'shanghai': 'Asia/Shanghai', 38 | 'cuba': 'America/Havana', 39 | 'berlin': 'Europe/Berlin', 40 | 'turks & caicos': 'America/Grand_Turk', 41 | 'singapore': 'Singapore'} 42 | 43 | for zone in sorted(timezones): 44 | print(zone.title()) 45 | 46 | while True: 47 | selection = input('Choose a q (q to quit): ').lower() 48 | if selection == 'q': 49 | break 50 | if selection in timezones: 51 | country = timezones[selection] 52 | tz_to_display = pytz.timezone(country) 53 | world_time = datetime.datetime.now(tz=tz_to_display) 54 | 55 | # unformatted output: 56 | # print("{} time is: {}".format(selection.title(), world_time)) 57 | # print("Local time is: {}".format(datetime.datetime.now())) 58 | # print("UTC is: {}".format(datetime.datetime.utcnow())) 59 | 60 | # fancier output: 61 | world = '{} time is:'.format(selection.title()) 62 | local = 'Local time is:' 63 | utc = 'UTC time is:' 64 | print("{:24} {} {}".format( 65 | world, world_time.strftime('%A %x %X'), world_time.tzname())) 66 | print("{:24} {}".format( 67 | local, datetime.datetime.now().strftime('%A %x %X'))) 68 | print("{:24} {}".format( 69 | utc, datetime.datetime.utcnow().strftime('%A %x %X'))) 70 | else: 71 | print("That's not in my list") 72 | -------------------------------------------------------------------------------- /tkinter_example.py: -------------------------------------------------------------------------------- 1 | '''tkinter example - (NOT a functioning calculator)''' 2 | 3 | 4 | import tkinter as tk 5 | from tkinter import ttk 6 | 7 | 8 | def click(key): 9 | '''dummy function for testing purposes only''' 10 | if (key == 'C') or (key == 'CE'): 11 | entry.delete(0, tk.END) 12 | else: 13 | entry.insert(tk.END, key) 14 | 15 | 16 | # Button text is stored as a list of row lists. The list items here are tuples, 17 | # where the second value is being used as the colspan in the code below: 18 | keys = [[('C', 2), ('CE', 2)], 19 | [('7', 1), ('8', 1), ('9', 1), ('+', 1)], 20 | [('4', 1), ('5', 1), ('6', 1), ('-', 1)], 21 | [('1', 1), ('2', 1), ('3', 1), ('*', 1)], 22 | [('0', 1), ('=', 2), ('/', 1)],] 23 | 24 | # Using a variable for padding, since it's also used to calculate window size: 25 | mainWindowPadding = 8 26 | 27 | # Main window setup: 28 | mainWindow = tk.Tk() 29 | mainWindow.title("Calculator") 30 | mainWindow.geometry('50x50-25-25') 31 | mainWindow['padx'] = mainWindowPadding 32 | 33 | # example style some ttk elements: 34 | style = ttk.Style() 35 | 36 | # A bug caused by the 'aqua' ttk style on Mac OSX makes it so that some 37 | # elements won't drop the light gray background color. Classic should fix it: 38 | style.theme_use('classic') 39 | style.configure("test.TLabel", foreground="magenta", background="yellow") 40 | 41 | # Example ttk window label: 42 | label = ttk.Label(mainWindow, text='tkinter example', padding=(38,5,38,5), 43 | style='test.TLabel') 44 | label.grid(row=0, column=0) 45 | 46 | # Result field: 47 | # A "width" configuration option may be specified to provide the number of 48 | # characters wide the entry should be. 49 | entry = tk.Entry(mainWindow) 50 | entry.grid(row=1, column=0, sticky='nsew') 51 | entry.config(width=10) 52 | 53 | # Key pad: 54 | keyPad = tk.Frame(mainWindow) 55 | keyPad.grid(row=2, column=0, sticky='nsew') 56 | 57 | # padx and pady are intended to add external padding - the value can be a 58 | # number or a tuple (5, 15) for separate left, right - top, bottom values. 59 | # ipadx and ipady are intended to add internal padding - the value can only 60 | # be a number, not a tuple. These don't appear to work consistently. In the 61 | # example below both ipady and pady give the same result: 62 | 63 | row = 0 64 | for keyRow in keys: 65 | col = 0 66 | for key in keyRow: 67 | cmd = lambda x=key[0]: click(x) 68 | tk.Button(keyPad, text=key[0], command=cmd).grid( 69 | row=row, column=col, columnspan=key[1], sticky='nsew', ipadx=5) 70 | col += key[1] 71 | row += 1 72 | 73 | # Set the minsize(), and a maxsize() of the window using the width and height 74 | # of the keyPad element - .winfo_height() and winfo_width() methods. 75 | # This won't work unless we force the window to draw the widgets first by 76 | # calling its .update() method: 77 | 78 | mainWindow.update() 79 | 80 | mainWindow.minsize((keyPad.winfo_width() + mainWindowPadding * 2), 81 | (entry.winfo_height() + keyPad.winfo_height() + label.winfo_height() 82 | + mainWindowPadding)) 83 | 84 | mainWindow.maxsize((keyPad.winfo_width() + mainWindowPadding * 2), 85 | (entry.winfo_height() + keyPad.winfo_height() + label.winfo_height() 86 | + mainWindowPadding)) 87 | 88 | mainWindow.mainloop() 89 | -------------------------------------------------------------------------------- /virtual_environments.md: -------------------------------------------------------------------------------- 1 | # Virtual Environments 2 | 3 | ## Table of contents 4 | 5 | 6 | 7 | - [Introduction](#introduction) 8 | - [Create a virtual environment](#create-a-virtual-environment) 9 | - [Run your program in the virtual environment](#run-your-program-in-the-virtual-environment) 10 | - [Keep track of requirements](#keep-track-of-requirements) 11 | 12 | 13 | 14 | ## Introduction 15 | 16 | A Virtual Environment, put simply, is an isolated working copy of Python that allows you to work on a specific project without affecting any other projects. It creates a directory which contains all the necessary executables to use the packages that a Python project would need. 17 | 18 | The basic problem being addressed is one of dependencies and versions, and indirectly permissions. Imagine you have an application that needs version 1 of Library-xyz, but another application requires version 2. How can you use both these applications? It’s easy to end up in a situation where you unintentionally upgrade an application that shouldn’t be. 19 | 20 | More generally, what if you want to install an application and leave it be? If an application works, any change in its libraries or the versions of those libraries can break the application. Also, what if you can’t install packages into the global site-packages directory? For instance, on a shared host. 21 | 22 | In these cases, venv (standard library) or virtualenv (independent library) can help. Both create an environment that has its own installation directories, that don’t share libraries with other virtual environments (and optionally don’t access the globally installed libraries either). 23 | 24 | 25 | 26 | 27 | ## Create a virtual environment 28 | 29 | To create a virtual environment, navigate to your project directory in the command line and enter the following. Note: the *first* 'venv' is the name of the module (I'm choosing to use the one from Python's standard library), the *second* 'venv' is the directory name for all the environment files... call it whatever you want: 30 | 31 | ``` 32 | $ python -m venv venv 33 | ``` 34 | 35 | Next, install packages in the virtual environment. There are two ways to do this. The first method, from the same project directory where venv lives: 36 | 37 | ``` 38 | $ venv/bin/pip install flask 39 | ``` 40 | 41 | Alternatively, you can *activate* your virtual environment first, then pip install as you would normally: 42 | 43 | ``` 44 | $ source venv/bin/activate 45 | (venv) $ pip install flask 46 | ``` 47 | 48 | You should see the name of your environment `(venv)` at the start of the command line prompt. While in the activated state, you can pip install as you normally would (without the preceding venv/bin/ as above). 49 | 50 | 51 | ## Run your program in the virtual environment 52 | 53 | Again, you can do one of two things. First, the long way: 54 | 55 | ``` 56 | $ venv/bin/python myscript.py 57 | ``` 58 | 59 | or, you can activate your environment as above: 60 | 61 | ``` 62 | $ source venv/bin/activate 63 | (venv) $ python myscript.py 64 | ``` 65 | 66 | When you're finished working in the project, deactivate the environment: 67 | 68 | ``` 69 | (venv) $ deactivate 70 | ``` 71 | 72 | ## Keep track of requirements 73 | 74 | Depending on which method you prefer, do one of the following: 75 | 76 | ``` 77 | $ venv/bin/pip freeze > requirements.txt 78 | (venv) $ pip freeze > requirements.txt 79 | ``` 80 | 81 | See also: [package_management.py](https://github.com/jessicarush/python-examples/blob/master/package_management.py) 82 | -------------------------------------------------------------------------------- /web_scraping.py: -------------------------------------------------------------------------------- 1 | '''Web Automation: Scraping''' 2 | 3 | 4 | # The webbrowser Module 5 | # ----------------------------------------------------------------------------- 6 | 7 | import webbrowser 8 | 9 | url = "http://www.python.org/" 10 | 11 | webbrowser.open(url) 12 | 13 | # to open in a new window: 14 | webbrowser.open_new(url) 15 | 16 | # to open in a new tab (some browsers do this already via their own prefs): 17 | webbrowser.open_new_tab(url) 18 | 19 | 20 | # Crawl and Scrape 21 | # ----------------------------------------------------------------------------- 22 | # Sometimes, you might want a little bit of information available only in HTML 23 | # pages, surrounded by ads and extraneous content. An automated web fetcher 24 | # (called a crawler or spider) retrieves content from the remote web servers, 25 | # and a scraper parses the content to find the needle in the haystack. 26 | 27 | # If you need an industrial-strength combined crawler and scraper, Scrapy is 28 | # worth downloading: $ pip install scrapy 29 | 30 | # Scrapy is a framework, not a module. It does more, but it's more complex to 31 | # set up. Learn more at: https://scrapy.org 32 | 33 | 34 | # Scrape with BeautifulSoup - basic parsing example 35 | # ----------------------------------------------------------------------------- 36 | # If you already have the HTML data from a website and just want to extract 37 | # from it, BeautifulSoup is a good choice. HTML parsing is a headache because 38 | # much of the HTML on public web pages is technically invalid: unclosed tags, 39 | # incorrect nesting, and other complications. 40 | 41 | # $ pip install beautifulsoup4 42 | 43 | import requests 44 | from bs4 import BeautifulSoup as soup 45 | 46 | 47 | r = requests.get('http://pythonhow.com/example.html') 48 | c = r.content # the entire source code 49 | s = soup(c, 'html.parser') # feed the content to the bs4 parser 50 | a = s.find('div',{'class': 'cities'}) # returns the first div 51 | a = s.find_all('div',{'class': 'cities'}) # returns a list of all divs 52 | 53 | # print(s.prettify) 54 | 55 | print(type(r)) # 56 | print(type(c)) # 57 | print(type(s)) # 58 | print(type(a)) # 59 | print(type(a[2])) # 60 | print(a) # prints all the divs and their contents 61 | print(a[2]) # prints the 3rd div and its contents 62 | print(a[0].find_all('h2')) # list of all the h2 tags from the 1st div 63 | print(a[0].find_all('h2')[0]) # just the 1st h2 64 | print(a[0].find_all('h2')[0].text) # just the text inside the h2 tag 65 | 66 | for item in a: 67 | print(item.find_all('h2')) 68 | # [

London

] 69 | # [

Paris

] 70 | # [

Tokyo

] 71 | 72 | for item in a: 73 | print(item.find_all('h2')[0].text) 74 | # London 75 | # Paris 76 | # Tokyo 77 | 78 | 79 | # Scrape with BeautifulSoup - links example 80 | # ----------------------------------------------------------------------------- 81 | # The following example uses it to get all the links from a web page. 82 | # A function get_links() will do most of the work, and a main program to get 83 | # one or more URLs as command-line arguments. 84 | 85 | import requests 86 | from bs4 import BeautifulSoup as soup 87 | 88 | 89 | def get_links(url): 90 | result = requests.get(url) 91 | page = result.text 92 | doc = soup(page, 'html.parser') 93 | links = [element.get('href') for element in doc.find_all('a')] 94 | return links 95 | 96 | if __name__ == '__main__': 97 | import sys 98 | for url in sys.argv[1:]: 99 | print ('Links in', url) 100 | for num, link in enumerate(get_links(url), start=1): 101 | print(num, link) 102 | print() 103 | 104 | # The 'a' and 'href' represent the literal HTML: 105 | 106 | # The above can be saved as a program and run like so: 107 | # python3 getlinks.py http://python.org 108 | -------------------------------------------------------------------------------- /while_loops.py: -------------------------------------------------------------------------------- 1 | '''While loops, break and continue''' 2 | 3 | 4 | # while is a looping mechanism that's used with if, elif, else and try. A while 5 | # loop will loop forever unless you, break, continue, pass or return something. 6 | # break is useful when you want to terminate the loop early if some condition 7 | # is met. continue is for when you want to skip past an iteration to the next. 8 | 9 | x = 5 10 | while x > 0: 11 | x -= 1 12 | if x == 2: 13 | break 14 | print(x) 15 | # 4 16 | # 3 17 | 18 | x = 5 19 | while x > 0: 20 | x -= 1 21 | if x == 2: 22 | continue 23 | print(x) 24 | # 4 25 | # 3 26 | # 1 27 | # 0 28 | 29 | 30 | # Example using if, elif, else, break and continue 31 | # ----------------------------------------------------------------------------- 32 | 33 | import random 34 | 35 | print('Guess a number between 1 and 10 (0 to quit):') 36 | 37 | answer = random.randint(1, 9) 38 | 39 | while True: 40 | guess = (int(input('> '))) 41 | if guess != answer: 42 | if guess == 0: 43 | print('bye') 44 | break 45 | elif guess > answer: 46 | print('No, lower') 47 | continue 48 | else: 49 | print('No, higher') 50 | continue 51 | else: 52 | print('You guessed it!') 53 | break 54 | 55 | 56 | # Example using if, try, break, continue 57 | # ----------------------------------------------------------------------------- 58 | 59 | def squared(): 60 | while True: 61 | value = input('Enter an integer (q to quit): ') 62 | if value == 'q': 63 | break 64 | try: 65 | number = int(value) 66 | except ValueError: 67 | print("That's not an integer.") 68 | continue 69 | number = int(value) 70 | print(number, 'squared is', number * number) 71 | 72 | squared() 73 | 74 | 75 | # Example using try and return 76 | # ----------------------------------------------------------------------------- 77 | 78 | print('Enter two numbers.') 79 | 80 | def get_inputs(arg): 81 | while True: 82 | try: 83 | x = input('{} number: '.format(arg)) 84 | x = int(x) 85 | return x 86 | except: 87 | print('I need an integer.') 88 | 89 | a = get_inputs('first') 90 | b = get_inputs('second') 91 | 92 | print('{} * {} = {}'.format(a, b, a * b)) 93 | 94 | 95 | # A slightly different approach 96 | # ----------------------------------------------------------------------------- 97 | # A 'flag' variable is a great way of signaling to a while loop when you have 98 | # many things that could/should end the loop. Using a flag variable, our loop 99 | # only has to check one condition. 100 | 101 | print('Kilograms to Pounds converter.') 102 | prompt = 'Enter kilograms (q to quit): ' 103 | KILO_POUNDS = 2.20462 104 | active = True # this is a flag variable! 105 | 106 | while active: 107 | value = input(prompt) 108 | if value == 'q': 109 | active = False 110 | else: 111 | try: 112 | new_value = int(value) * KILO_POUNDS 113 | print(value, 'kilograms is', new_value, 'pounds') 114 | except: 115 | print("I need a number or 'q' to quit: ") 116 | 117 | 118 | # While Loops with Lists & Dicts 119 | # ----------------------------------------------------------------------------- 120 | # A for loop is effective for iterating through a list but apparently, "You 121 | # shouldn't modify a list inside a for loop because Python will have trouble 122 | # keeping track of the items in the list. To modify a list as you work through 123 | # it, use a while loop." Using a while loop allows you to better collect, 124 | # store and organize. 125 | 126 | unconfirmed_users = ['rick', 'morty', 'raja', 'bob'] 127 | confirmed_users = [] 128 | 129 | while unconfirmed_users: 130 | current_user = unconfirmed_users.pop() 131 | print('Magical verification of user: {}'.format(current_user.title())) 132 | confirmed_users.append(current_user) 133 | 134 | for user in confirmed_users: 135 | print('Confirmed user: {}'.format(user.title())) 136 | 137 | 138 | # Filling a dict with user input 139 | # ----------------------------------------------------------------------------- 140 | 141 | shopping_list = {} 142 | 143 | while True: 144 | item = input('Item (q to quit): ') 145 | if item == 'q': 146 | break 147 | store = input('Store: ') 148 | if store in shopping_list: 149 | shopping_list[store].append(item) 150 | else: 151 | shopping_list[store] = [item] 152 | 153 | if shopping_list: 154 | print('Shopping list\n') 155 | 156 | for store, items, in shopping_list.items(): 157 | print(store.title()) 158 | for item in items: 159 | print('– ', item) 160 | print('-' * 25) 161 | -------------------------------------------------------------------------------- /zip_function.py: -------------------------------------------------------------------------------- 1 | '''zip() Built-in Function''' 2 | 3 | 4 | # Iterating over multiple sequences 5 | # ----------------------------------------------------------------------------- 6 | # the zip() function wraps two or more iterators in a lazy generator. 7 | # It yields a tuple containing the next value from each iterator. 8 | 9 | days = ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday'] 10 | fruits = ['Apple', 'Banana', 'Pear'] 11 | drinks = ['Tea', 'Juice', 'Wine'] 12 | desserts = ['Ice cream', 'Cookies', 'Cake', 'Candy'] 13 | 14 | for day, fruit, drink, dessert in zip(days, fruits, drinks, desserts): 15 | print(day.upper()) 16 | print(f'– {fruit}, {drink}, {dessert}') 17 | 18 | # MONDAY 19 | # – Apple, Tea, Ice cream 20 | # TUESDAY 21 | # – Banana, Juice, Cookies 22 | # WEDNESDAY 23 | # – Pear, Wine, Cake 24 | 25 | # When using zip in this manner, it will stop iterating when it reaches the 26 | # end of the shortest list. If you want it to exhaust the longest list, use 27 | # zip_longest() from itertools: 28 | 29 | from itertools import zip_longest 30 | 31 | for day, fruit, drink, dessert in zip_longest(days, fruits, drinks, desserts): 32 | print(day.upper()) 33 | print(f'– {fruit}, {drink}, {dessert}') 34 | 35 | # MONDAY 36 | # – Apple, Tea, Ice cream 37 | # TUESDAY 38 | # – Banana, Juice, Cookies 39 | # WEDNESDAY 40 | # – Pear, Wine, Cake 41 | # THURSDAY 42 | # – None, None, Candy 43 | # FRIDAY 44 | # – None, None, None 45 | 46 | 47 | # create a dictionary from two sequences 48 | # ----------------------------------------------------------------------------- 49 | 50 | english = ['Monday', 'Tuesday', 'Wednesday', 'Thursday'] 51 | french = ['Lundi', 'Mardi', 'Mecredi'] 52 | 53 | e2f = dict(zip(english, french)) 54 | f2e = dict(zip(french, english)) 55 | 56 | print(e2f['Tuesday']) # Mardi 57 | print(f2e['Mardi']) # Tuesday 58 | 59 | # as above, zip will stop creating key/value pairs at the end of the shortest 60 | # list. In the example above, no key will be created for Thursday. 61 | 62 | 63 | # Another example 64 | # ----------------------------------------------------------------------------- 65 | 66 | colours = ['Red', 'Cyan', 'Magenta', 'Orange'] 67 | hexadecimal = ['FA200C', '0CD3FA', 'FF0D91', 'FF690D'] 68 | 69 | colour_values = dict(zip(colours, hexadecimal)) 70 | 71 | print(colour_values) 72 | # {'Red': 'FA200C', 'Cyan': '0CD3FA', 'Magenta': 'FF0D91', 'Orange': 'FF690D'} 73 | 74 | # NOTE that zip() behaves differently in Python 2. In version 2 it is not a 75 | # generator and can therefor use a lot of memory. In Python 2 use itertools 76 | # instead. 77 | --------------------------------------------------------------------------------