diff --git a/basic-input-output-in-python/adventure_game.py b/basic-input-output-in-python/adventure_game.py index e634754a35..1d3e363d81 100644 --- a/basic-input-output-in-python/adventure_game.py +++ b/basic-input-output-in-python/adventure_game.py @@ -4,7 +4,13 @@ enemy_health = 3 while health > 0 and enemy_health > 0: - if input("Attack or Run? ").lower() == "attack": + # Normalize input to handle extra spaces and case variations. + action = input("Attack or Run? ").strip().lower() + if action not in {"attack", "run"}: + print("Invalid choice. Please type 'Attack' or 'Run'.") + continue + + if action == "attack": enemy_health -= 1 print("You hit the enemy!") # Implement a 50% chance that the enemy strikes back. diff --git a/basic-input-output-in-python/guess_the_number.py b/basic-input-output-in-python/guess_the_number.py index 3d485326ef..437b5d7af3 100644 --- a/basic-input-output-in-python/guess_the_number.py +++ b/basic-input-output-in-python/guess_the_number.py @@ -6,4 +6,4 @@ if guess == number: print("You got it!") else: - print(f"Sorry, the number was {number}.") + print("Sorry, the number was", number) diff --git a/celery-async-tasks/source_code_final/requirements.txt b/celery-async-tasks/source_code_final/requirements.txt index bb1f70b615..55274c7b99 100644 --- a/celery-async-tasks/source_code_final/requirements.txt +++ b/celery-async-tasks/source_code_final/requirements.txt @@ -1,22 +1,18 @@ -amqp==5.1.1 -asgiref==3.5.2 -async-timeout==4.0.2 -billiard==3.6.4.0 -celery==5.2.7 -click==8.1.3 -click-didyoumean==0.3.0 +amqp==5.3.1 +asgiref==3.8.1 +billiard==4.2.1 +celery==5.4.0 +click==8.1.7 +click-didyoumean==0.3.1 click-plugins==1.1.1 -click-repl==0.2.0 -Deprecated==1.2.13 -Django==4.0.6 -kombu==5.2.4 -packaging==21.3 -prompt-toolkit==3.0.30 -pyparsing==3.0.9 -pytz==2022.1 -redis==4.3.4 +click-repl==0.3.0 +Django==5.1.3 +kombu==5.4.2 +prompt_toolkit==3.0.48 +python-dateutil==2.9.0.post0 +redis==5.2.0 six==1.16.0 -sqlparse==0.4.2 -vine==5.0.0 -wcwidth==0.2.5 -wrapt==1.14.1 +sqlparse==0.5.2 +tzdata==2024.2 +vine==5.1.0 +wcwidth==0.2.13 diff --git a/celery-async-tasks/source_code_initial/requirements.txt b/celery-async-tasks/source_code_initial/requirements.txt index bb1f70b615..55274c7b99 100644 --- a/celery-async-tasks/source_code_initial/requirements.txt +++ b/celery-async-tasks/source_code_initial/requirements.txt @@ -1,22 +1,18 @@ -amqp==5.1.1 -asgiref==3.5.2 -async-timeout==4.0.2 -billiard==3.6.4.0 -celery==5.2.7 -click==8.1.3 -click-didyoumean==0.3.0 +amqp==5.3.1 +asgiref==3.8.1 +billiard==4.2.1 +celery==5.4.0 +click==8.1.7 +click-didyoumean==0.3.1 click-plugins==1.1.1 -click-repl==0.2.0 -Deprecated==1.2.13 -Django==4.0.6 -kombu==5.2.4 -packaging==21.3 -prompt-toolkit==3.0.30 -pyparsing==3.0.9 -pytz==2022.1 -redis==4.3.4 +click-repl==0.3.0 +Django==5.1.3 +kombu==5.4.2 +prompt_toolkit==3.0.48 +python-dateutil==2.9.0.post0 +redis==5.2.0 six==1.16.0 -sqlparse==0.4.2 -vine==5.0.0 -wcwidth==0.2.5 -wrapt==1.14.1 +sqlparse==0.5.2 +tzdata==2024.2 +vine==5.1.0 +wcwidth==0.2.13 diff --git a/django-user-management/README.md b/django-user-management/README.md new file mode 100644 index 0000000000..d70eeeb218 --- /dev/null +++ b/django-user-management/README.md @@ -0,0 +1,46 @@ +# Get Started With Django User Management + +Follow the [step-by-step instructions](https://realpython.com/django-user-management/) on Real Python. + +## Setup + +You can run the provided example project on your local machine by following the steps outlined below. + +Create a new virtual environment: + +```bash +$ python3 -m venv venv/ +``` + +Activate the virtual environment: + +```bash +$ source venv/bin/activate +``` + +Install the dependencies for this project if you haven't installed them yet: + +```bash +(venv) $ python -m pip install -r requirements.txt +``` + +Navigate into the project's directory: + +```bash +(venv) $ cd user_auth_intro/ +``` + +Make and apply the migrations for the project to build your local database: + +```bash +(venv) $ python manage.py makemigrations +(venv) $ python manage.py migrate +``` + +Run the Django development server: + +```bash +(venv) $ python manage.py runserver +``` + +Navigate to `http://localhost:8000/dashboard` to see the project in action. diff --git a/django-user-management/requirements.txt b/django-user-management/requirements.txt new file mode 100644 index 0000000000..e2ae4cc169 --- /dev/null +++ b/django-user-management/requirements.txt @@ -0,0 +1,3 @@ +asgiref==3.8.1 +Django==5.1.3 +sqlparse==0.5.2 diff --git a/django-user-management/user_auth_intro/manage.py b/django-user-management/user_auth_intro/manage.py new file mode 100755 index 0000000000..c67a51c556 --- /dev/null +++ b/django-user-management/user_auth_intro/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "user_auth_intro.settings") + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == "__main__": + main() diff --git a/django-user-management/user_auth_intro/user_auth_intro/__init__.py b/django-user-management/user_auth_intro/user_auth_intro/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django-user-management/user_auth_intro/user_auth_intro/asgi.py b/django-user-management/user_auth_intro/user_auth_intro/asgi.py new file mode 100644 index 0000000000..a86a136712 --- /dev/null +++ b/django-user-management/user_auth_intro/user_auth_intro/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for user_auth_intro project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "user_auth_intro.settings") + +application = get_asgi_application() diff --git a/django-user-management/user_auth_intro/user_auth_intro/settings.py b/django-user-management/user_auth_intro/user_auth_intro/settings.py new file mode 100644 index 0000000000..41db9704f8 --- /dev/null +++ b/django-user-management/user_auth_intro/user_auth_intro/settings.py @@ -0,0 +1,128 @@ +""" +Django settings for user_auth_intro project. + +Generated by 'django-admin startproject' using Django 5.1.3. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.1/ref/settings/ +""" + +from pathlib import Path + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.1/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = ( + "django-insecure-%&$tji0geb7vpx8@9-&qbf3_%3$s=5*5amo4tr)yg!6&6@bbhl" +) + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" + + +# Application definition + +INSTALLED_APPS = [ + "users.apps.UsersConfig", + "django.contrib.admin", + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", +] + +MIDDLEWARE = [ + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", +] + +ROOT_URLCONF = "user_auth_intro.urls" + +TEMPLATES = [ + { + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [], + "APP_DIRS": True, + "OPTIONS": { + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + ], + }, + }, +] + +WSGI_APPLICATION = "user_auth_intro.wsgi.application" + + +# Database +# https://docs.djangoproject.com/en/5.1/ref/settings/#databases + +DATABASES = { + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": BASE_DIR / "db.sqlite3", + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.1/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + "NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.MinimumLengthValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.CommonPasswordValidator", + }, + { + "NAME": "django.contrib.auth.password_validation.NumericPasswordValidator", + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.1/topics/i18n/ + +LANGUAGE_CODE = "en-us" + +TIME_ZONE = "UTC" + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.1/howto/static-files/ + +STATIC_URL = "static/" + +# Default primary key field type +# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField" diff --git a/django-user-management/user_auth_intro/user_auth_intro/urls.py b/django-user-management/user_auth_intro/user_auth_intro/urls.py new file mode 100644 index 0000000000..b9a774f5ea --- /dev/null +++ b/django-user-management/user_auth_intro/user_auth_intro/urls.py @@ -0,0 +1,7 @@ +from django.contrib import admin +from django.urls import include, path + +urlpatterns = [ + path("", include("users.urls")), + path("admin/", admin.site.urls), +] diff --git a/django-user-management/user_auth_intro/user_auth_intro/wsgi.py b/django-user-management/user_auth_intro/user_auth_intro/wsgi.py new file mode 100644 index 0000000000..8413683629 --- /dev/null +++ b/django-user-management/user_auth_intro/user_auth_intro/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for user_auth_intro project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.1/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "user_auth_intro.settings") + +application = get_wsgi_application() diff --git a/django-user-management/user_auth_intro/users/__init__.py b/django-user-management/user_auth_intro/users/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django-user-management/user_auth_intro/users/admin.py b/django-user-management/user_auth_intro/users/admin.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django-user-management/user_auth_intro/users/apps.py b/django-user-management/user_auth_intro/users/apps.py new file mode 100644 index 0000000000..88f7b1798e --- /dev/null +++ b/django-user-management/user_auth_intro/users/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UsersConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "users" diff --git a/django-user-management/user_auth_intro/users/forms.py b/django-user-management/user_auth_intro/users/forms.py new file mode 100644 index 0000000000..37ef39ddbd --- /dev/null +++ b/django-user-management/user_auth_intro/users/forms.py @@ -0,0 +1,6 @@ +from django.contrib.auth.forms import UserCreationForm + + +class CustomUserCreationForm(UserCreationForm): + class Meta(UserCreationForm.Meta): + fields = UserCreationForm.Meta.fields + ("email",) diff --git a/django-user-management/user_auth_intro/users/migrations/__init__.py b/django-user-management/user_auth_intro/users/migrations/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django-user-management/user_auth_intro/users/models.py b/django-user-management/user_auth_intro/users/models.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django-user-management/user_auth_intro/users/templates/base.html b/django-user-management/user_auth_intro/users/templates/base.html new file mode 100644 index 0000000000..3b233d2011 --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/base.html @@ -0,0 +1,12 @@ + + + + + User Management Intro + + +

Welcome!

+ {% block content %} + {% endblock content %} + + diff --git a/django-user-management/user_auth_intro/users/templates/registration/_logout.html b/django-user-management/user_auth_intro/users/templates/registration/_logout.html new file mode 100644 index 0000000000..938250f08d --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/_logout.html @@ -0,0 +1,6 @@ +
+ {% csrf_token %} + {{ form.as_p }} + + +
diff --git a/django-user-management/user_auth_intro/users/templates/registration/login.html b/django-user-management/user_auth_intro/users/templates/registration/login.html new file mode 100644 index 0000000000..031a80676d --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/login.html @@ -0,0 +1,14 @@ +{% extends "base.html" %} +{% block content %} +

Login

+
+ {% csrf_token %} + {{ form.as_p }} + + +
+

+ Forgot your password? + Back to dashboard +

+{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/registration/password_change_done.html b/django-user-management/user_auth_intro/users/templates/registration/password_change_done.html new file mode 100644 index 0000000000..fe4de16bff --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/password_change_done.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block content %} +

Password changed

+ Back to dashboard +{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/registration/password_change_form.html b/django-user-management/user_auth_intro/users/templates/registration/password_change_form.html new file mode 100644 index 0000000000..cb7bfa1701 --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/password_change_form.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block content %} +

Change password

+
+ {% csrf_token %} + {{ form.as_p }} + +
+ Back to dashboard +{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/registration/password_reset_complete.html b/django-user-management/user_auth_intro/users/templates/registration/password_reset_complete.html new file mode 100644 index 0000000000..85c89b5f22 --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/password_reset_complete.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block content %} +

Password reset completed

+ Login +{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/registration/password_reset_confirm.html b/django-user-management/user_auth_intro/users/templates/registration/password_reset_confirm.html new file mode 100644 index 0000000000..1f98383069 --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/password_reset_confirm.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block content %} +

Confirm password reset

+
+ {% csrf_token %} + {{ form.as_p }} + +
+ Back to dashboard +{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/registration/password_reset_done.html b/django-user-management/user_auth_intro/users/templates/registration/password_reset_done.html new file mode 100644 index 0000000000..d6aa80f9db --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/password_reset_done.html @@ -0,0 +1,5 @@ +{% extends "base.html" %} +{% block content %} +

Password reset link sent

+ Back to dashboard +{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/registration/password_reset_form.html b/django-user-management/user_auth_intro/users/templates/registration/password_reset_form.html new file mode 100644 index 0000000000..fb3ce5c3e4 --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/password_reset_form.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% block content %} +

Send password reset link

+
+ {% csrf_token %} + {{ form.as_p }} + +
+

+ Back to dashboard +

+{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/registration/sign_up.html b/django-user-management/user_auth_intro/users/templates/registration/sign_up.html new file mode 100644 index 0000000000..66894c1493 --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/registration/sign_up.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% block content %} +

Sign Up

+
+ {% csrf_token %} + {{ form.as_p }} + +
+

+ Back to dashboard +

+{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/templates/users/dashboard.html b/django-user-management/user_auth_intro/users/templates/users/dashboard.html new file mode 100644 index 0000000000..c54024366b --- /dev/null +++ b/django-user-management/user_auth_intro/users/templates/users/dashboard.html @@ -0,0 +1,12 @@ +{% extends "base.html" %} +{% block content %} + Hello, {{ user.username|default:"Guest" }}! +
+ {% if user.is_authenticated %} + {% include "registration/_logout.html" %} + Change password + {% else %} + Login + Sign up + {% endif %} +{% endblock content %} diff --git a/django-user-management/user_auth_intro/users/tests.py b/django-user-management/user_auth_intro/users/tests.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/django-user-management/user_auth_intro/users/urls.py b/django-user-management/user_auth_intro/users/urls.py new file mode 100644 index 0000000000..242774ac89 --- /dev/null +++ b/django-user-management/user_auth_intro/users/urls.py @@ -0,0 +1,9 @@ +from django.urls import include, path + +from . import views + +urlpatterns = [ + path("accounts/", include("django.contrib.auth.urls")), + path("dashboard/", views.dashboard, name="dashboard"), + path("sign_up/", views.sign_up, name="sign_up"), +] diff --git a/django-user-management/user_auth_intro/users/views.py b/django-user-management/user_auth_intro/users/views.py new file mode 100644 index 0000000000..fb0c0069c8 --- /dev/null +++ b/django-user-management/user_auth_intro/users/views.py @@ -0,0 +1,23 @@ +from django.contrib.auth import login + +# Remove: from django.contrib.auth.forms import UserCreationForm +from django.shortcuts import redirect, render +from django.urls import reverse + +from .forms import CustomUserCreationForm + + +def dashboard(request): + return render(request, "users/dashboard.html") + + +def sign_up(request): + if request.method == "POST": + form = CustomUserCreationForm(request.POST) + if form.is_valid(): + user = form.save() + login(request, user) + return redirect(reverse("dashboard")) + else: + form = CustomUserCreationForm() + return render(request, "registration/sign_up.html", {"form": form}) diff --git a/expressions-vs-statements/README.md b/expressions-vs-statements/README.md new file mode 100644 index 0000000000..ace4085f6e --- /dev/null +++ b/expressions-vs-statements/README.md @@ -0,0 +1,66 @@ +# Expression vs Statement in Python: What's the Difference? + +This folder contains sample code from the Real Python tutorial [Expression vs Statement in Python: What's the Difference?](https://realpython.com/python-expression-vs-statement/) + +## Code Inspector + +Identify whether a piece of Python code is an expression or a statement: + +```shell +$ python code_inspector.py +Type a Python code snippet or leave empty to exit. +>>> yield +statement +>>> (yield) +expression +>>> 2 + +invalid +``` + +## GUI App + +Register a lambda expression as a callback, which delegates to a function with statements: + +```shell +$ python gui_app.py +``` + +## Echo Program + +Compile with a C compiler and pipe stdin to the echo program: + +```shell +$ gcc echo.c -o echo.x +$ echo "Hello, World!" | ./echo.x +Hello, World! +``` + +## HEX Reader + +Read a binary file and display its bytes in hexadecimal format: + +```shell +$ python hex_reader.py /path/to/HelloJava.class --columns 8 +ca fe ba be 00 00 00 41 +00 0f 0a 00 02 00 03 07 +00 04 0c 00 05 00 06 01 +(...) +``` + +## Generators + +Generate a random signal and use a low-pass filter to make it smooth: + +```shell +$ python generators.py +-0.96: -0.96 +-0.81: -0.89 +-0.52: -0.67 + 0.22: -0.15 + 0.51: 0.37 + 0.40: 0.46 +-0.08: 0.16 +-0.24: -0.16 + 0.80: 0.28 + 0.47: 0.64 +``` diff --git a/expressions-vs-statements/code_inspector.py b/expressions-vs-statements/code_inspector.py new file mode 100644 index 0000000000..b91a1eff77 --- /dev/null +++ b/expressions-vs-statements/code_inspector.py @@ -0,0 +1,31 @@ +import ast + + +def main(): + print("Type a Python code snippet or leave empty to exit.") + while code := input(">>> "): + print(describe(code)) + + +def describe(code): + if valid(code, mode="eval"): + return "expression" + elif valid(code, mode="exec"): + return "statement" + else: + return "invalid" + + +def valid(code, mode): + try: + ast.parse(code, mode=mode) + return True + except SyntaxError: + return False + + +if __name__ == "__main__": + try: + main() + except EOFError: + pass diff --git a/expressions-vs-statements/echo.c b/expressions-vs-statements/echo.c new file mode 100644 index 0000000000..99c9b342d7 --- /dev/null +++ b/expressions-vs-statements/echo.c @@ -0,0 +1,11 @@ +#include + +int main() { + int x; + while (x = fgetc(stdin)) { + if (x == EOF) + break; + putchar(x); + } + return 0; +} diff --git a/expressions-vs-statements/generators.py b/expressions-vs-statements/generators.py new file mode 100644 index 0000000000..a324e00bd2 --- /dev/null +++ b/expressions-vs-statements/generators.py @@ -0,0 +1,24 @@ +import random + + +def main(): + lf = lowpass_filter() + lf.send(None) + for value in generate_noise(10): + print(f"{value:>5.2f}: {lf.send(value):>5.2f}") + + +def generate_noise(size): + for _ in range(size): + yield 2 * random.random() - 1 + + +def lowpass_filter(): + a = yield + b = yield a + while True: + a, b = b, (yield (a + b) / 2) + + +if __name__ == "__main__": + main() diff --git a/expressions-vs-statements/gui_app.py b/expressions-vs-statements/gui_app.py new file mode 100644 index 0000000000..0a995bb6b7 --- /dev/null +++ b/expressions-vs-statements/gui_app.py @@ -0,0 +1,19 @@ +import tkinter as tk + + +def main(): + window = tk.Tk() + button = tk.Button(window, text="Click", command=lambda: on_click(42)) + button.pack(padx=10, pady=10) + window.mainloop() + + +def on_click(age): + if age > 18: + print("You're an adult.") + else: + print("You're a minor.") + + +if __name__ == "__main__": + main() diff --git a/expressions-vs-statements/hex_reader.py b/expressions-vs-statements/hex_reader.py new file mode 100644 index 0000000000..904719a27f --- /dev/null +++ b/expressions-vs-statements/hex_reader.py @@ -0,0 +1,24 @@ +import argparse +import itertools + +MAX_BYTES = 1024 + + +def main(args): + buffer = bytearray() + with open(args.path, mode="rb") as file: + while chunk := file.read(MAX_BYTES): + buffer.extend(chunk) + for row in itertools.batched(buffer, args.columns): + print(" ".join(f"{byte:02x}" for byte in row)) + + +def parse_args(): + parser = argparse.ArgumentParser() + parser.add_argument("path") + parser.add_argument("-c", "--columns", type=int, default=16) + return parser.parse_args() + + +if __name__ == "__main__": + main(parse_args()) diff --git a/how-to-remove-item-from-list-python/README.md b/how-to-remove-item-from-list-python/README.md new file mode 100644 index 0000000000..002ac70cac --- /dev/null +++ b/how-to-remove-item-from-list-python/README.md @@ -0,0 +1,3 @@ +# How to Remove Items From Lists in Python + +This folder provides the code examples for the Real Python tutorial [How to Remove Items From Lists in Python](https://realpython.com/remove-item-from-list-python/). \ No newline at end of file diff --git a/how-to-remove-item-from-list-python/books.py b/how-to-remove-item-from-list-python/books.py new file mode 100644 index 0000000000..ad517c6d61 --- /dev/null +++ b/how-to-remove-item-from-list-python/books.py @@ -0,0 +1,36 @@ +books = ["Dragonsbane", "The Hobbit", "Wonder", "Jaws"] +print(books.pop(0)) +print(books) + +books = ["Dragonsbane", "The Hobbit", "Wonder", "Jaws"] +read_books = [] +read = books.pop(0) +read_books.append(read) +print(read_books) +print(books) + +books = ["Dragonsbane", "The Hobbit", "Wonder", "Wonder", "Jaws", "Jaws"] +del books[2] +print(books) +del books[-1] +print(books) + +books = ["Dragonsbane", "The Hobbit", "Wonder", "Jaws"] +books.remove("The Hobbit") +print(books) + +# books = ["Dragonsbane", "The Hobbit", "Wonder", "Jaws"] +# books.remove("The Two Towers") +# print(books) + +books = ["Dragonsbane", "The Hobbit", "Wonder", "Jaws"] +del books[0:3] +print(books) + +books = ["Dragonsbane", "The Hobbit", "Wonder", "Jaws", "It"] +del books[-3:-1] +print(books) + +books = ["Dragonsbane", "The Hobbit", "Wonder", "Jaws", "It"] +books.clear() +print(books) diff --git a/how-to-remove-item-from-list-python/phone_book.py b/how-to-remove-item-from-list-python/phone_book.py new file mode 100644 index 0000000000..4444f84b91 --- /dev/null +++ b/how-to-remove-item-from-list-python/phone_book.py @@ -0,0 +1,15 @@ +phone_numbers = ["54123", "54123", "54456", "54789", "54789", "54123"] +for phone_number in phone_numbers[:]: + while phone_numbers.count(phone_number) > 1: + phone_numbers.remove(phone_number) +print(phone_numbers) + + +phone_numbers = ["54123", "54123", "54456", "54789", "54789", "54123"] +phone_numbers = list(dict.fromkeys(phone_numbers)) +print(phone_numbers) + + +phone_numbers = ["54123", "54123", "54456", "54789", "54789", "54123"] +phone_numbers = set(phone_numbers) +print(phone_numbers) diff --git a/name-main-idiom/echo.py b/name-main-idiom/echo.py index 5b7b5d2d3c..940b7664b6 100644 --- a/name-main-idiom/echo.py +++ b/name-main-idiom/echo.py @@ -1,9 +1,7 @@ def echo(text: str, repetitions: int = 3) -> str: """Imitate a real-world echo.""" - echoed_text = "" - for i in range(repetitions, 0, -1): - echoed_text += f"{text[-i:]}\n" - return f"{echoed_text.lower()}." + echoes = [text[-i:].lower() for i in range(repetitions, 0, -1)] + return "\n".join(echoes + ["."]) if __name__ == "__main__": diff --git a/name-main-idiom/echo_args.py b/name-main-idiom/echo_args.py index b33a75f23e..4cad04ff4b 100644 --- a/name-main-idiom/echo_args.py +++ b/name-main-idiom/echo_args.py @@ -3,10 +3,8 @@ def echo(text: str, repetitions: int = 3) -> str: """Imitate a real-world echo.""" - echoed_text = "" - for i in range(repetitions, 0, -1): - echoed_text += f"{text[-i:]}\n" - return f"{echoed_text.lower()}." + echoes = [text[-i:].lower() for i in range(repetitions, 0, -1)] + return "\n".join(echoes + ["."]) def main() -> None: diff --git a/name-main-idiom/echo_demo.py b/name-main-idiom/echo_demo.py index 5e9f5bd9fa..fb7ff89fff 100644 --- a/name-main-idiom/echo_demo.py +++ b/name-main-idiom/echo_demo.py @@ -1,9 +1,7 @@ def echo(text: str, repetitions: int = 3) -> str: """Imitate a real-world echo.""" - echoed_text = "" - for i in range(repetitions, 0, -1): - echoed_text += f"{text[-i:]}\n" - return f"{echoed_text.lower()}." + echoes = [text[-i:].lower() for i in range(repetitions, 0, -1)] + return "\n".join(echoes + ["."]) if __name__ == "__main__": diff --git a/numpy-examples/full_portfolio.csv b/numpy-examples/full_portfolio.csv new file mode 100644 index 0000000000..2285eed597 --- /dev/null +++ b/numpy-examples/full_portfolio.csv @@ -0,0 +1,7 @@ +Company,Sector,Mon,Tue,Wed,Thu,Fri +Company_A,technology,100.5,101.2,102,101.8,112.5 +Company_B,finance,200.1,199.8,200.5,201.0,200.8 +Company_C,healthcare,50.3,50.5,51.0,50.8,51.2 +Company_D,technology,110.5,101.2,102,111.8,97.5 +Company_E,finance,200.1,200.8,200.5,211.0,200.8 +Company_F,healthcare,55.3,50.5,53.0,50.8,52.2 diff --git a/numpy-examples/issued_checks.csv b/numpy-examples/issued_checks.csv index 79ebe66d15..c4a57d0e5d 100644 --- a/numpy-examples/issued_checks.csv +++ b/numpy-examples/issued_checks.csv @@ -1,5 +1,5 @@ Check_ID,Payee,Amount,Date_Issued -1341,K.Starmer,150.00,2024-03-29 +1341,K Starmer,150.00,2024-03-29 1342,R Sunak,175.00,2024-03-29 1343,L Truss,30.00,2024-03-29 1344,B Johnson,45.00,2024-03-22 @@ -8,4 +8,4 @@ Check_ID,Payee,Amount,Date_Issued 1347,G Brown,100.00,2024-03-15 1348,T Blair,250.00,2024-03-15 1349,J Major,500.00,2024-03-15 -1350,M Thatcher,220.00,2024-03-15 \ No newline at end of file +1350,M Thatcher,220.00,2024-03-15 diff --git a/numpy-examples/passports.csv b/numpy-examples/passports.csv index 729d3351f9..91d80d959e 100644 --- a/numpy-examples/passports.csv +++ b/numpy-examples/passports.csv @@ -4,7 +4,7 @@ passport_no,passenger_no,nationality 493456399,3,American 375456228,4,American 457345975,5,Austrian -345957363,6,Norewegian +345957363,6,Norwegian 567546577,7,French 453667456,8,German 456785778,9,Danish @@ -19,4 +19,4 @@ passport_no,passenger_no,nationality 654672278,18,Spanish 683637288,19,Norwegian 768357788,20,British -768357788,21,American \ No newline at end of file +768357788,21,American diff --git a/numpy-examples/tutorial_code.ipynb b/numpy-examples/tutorial_code.ipynb index 453c501358..e24f9e54df 100644 --- a/numpy-examples/tutorial_code.ipynb +++ b/numpy-examples/tutorial_code.ipynb @@ -15,10 +15,8 @@ "metadata": {}, "outputs": [], "source": [ - "!python -m pip install numpy\n", - "!python -m pip install matplotlib\n", - "!python -m pip install pathlib\n", - "!python -m pip install jupyterlab" + "# !python -m pip install numpy\n", + "# !python -m pip install matplotlib" ] }, { @@ -39,16 +37,10 @@ "import numpy as np\n", "from pathlib import Path\n", "\n", - "array = np.zeros(\n", - " (\n", - " 3,\n", - " 2,\n", - " 3,\n", - " )\n", - ")\n", + "array = np.zeros((3, 2, 3))\n", "print(id(array))\n", "\n", - "for file_count, csv_file in enumerate(Path.cwd().glob(\"file?.csv\")):\n", + "for file_count, csv_file in enumerate(sorted(Path.cwd().glob(\"file?.csv\"))):\n", " array[file_count] = np.loadtxt(csv_file.name, delimiter=\",\")\n", "\n", "print(id(array))\n", @@ -63,15 +55,9 @@ "metadata": {}, "outputs": [], "source": [ - "array = np.zeros(\n", - " (\n", - " 4,\n", - " 2,\n", - " 3,\n", - " )\n", - ")\n", + "array = np.zeros((4, 2, 3))\n", "\n", - "for file_count, csv_file in enumerate(Path.cwd().glob(\"file?.csv\")):\n", + "for file_count, csv_file in enumerate(sorted(Path.cwd().glob(\"file?.csv\"))):\n", " array[file_count] = np.loadtxt(csv_file.name, delimiter=\",\")\n", "\n", "array[3, 0] = np.loadtxt(\"short_file.csv\", delimiter=\",\")\n", @@ -86,24 +72,16 @@ "metadata": {}, "outputs": [], "source": [ - "array = np.zeros(\n", - " (\n", - " 4,\n", - " 2,\n", - " 3,\n", - " )\n", - ")\n", + "array = np.zeros((4, 2, 3))\n", "print(id(array))\n", "\n", - "for file_count, csv_file in enumerate(Path.cwd().glob(\"file?.csv\")):\n", + "for file_count, csv_file in enumerate(sorted(Path.cwd().glob(\"file?.csv\"))):\n", " array[file_count] = np.loadtxt(csv_file.name, delimiter=\",\")\n", "\n", "array = np.insert(arr=array, obj=2, values=0, axis=1)\n", - "\n", "array[3] = np.loadtxt(\"long_file.csv\", delimiter=\",\")\n", "\n", "print(id(array))\n", - "\n", "array" ] }, @@ -117,21 +95,10 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": null, "id": "6d760bcd-1be5-4396-8eb8-d729768909d6", "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array(['At The Back', 'Fast Eddie', 'Almost There'], dtype=' first_day:\n", - " return True\n", - " return False\n", - "\n", + "def profit_with_bonus(first_day, last_day):\n", + " if last_day >= first_day * 1.01:\n", + " return (last_day - first_day) * 1.1\n", + " else:\n", + " return last_day - first_day\n", "\n", - "monday_prices = portfolio[\"mon\"]\n", - "friday_prices = portfolio[\"fri\"]\n", "\n", - "in_profit(monday_prices, friday_prices)" + "# The following causes an error because in_profit() doesn't know\n", + "# how to work with NumPy arrays:\n", + "#\n", + "# profit_with_bonus(portfolio[\"mon\"], portfolio[\"fri\"])" ] }, { @@ -476,27 +438,15 @@ "metadata": {}, "outputs": [], "source": [ - "def in_profit(first_day, last_day):\n", - " if last_day > first_day:\n", - " return True\n", - " return False\n", + "def profit_with_bonus(first_day, last_day):\n", + " if last_day >= first_day * 1.01:\n", + " return (last_day - first_day) * 1.1\n", + " else:\n", + " return last_day - first_day\n", "\n", "\n", - "monday_prices = portfolio[\"mon\"]\n", - "friday_prices = portfolio[\"fri\"]\n", - "\n", - "vectorized_in_profit = np.vectorize(in_profit)\n", - "vectorized_in_profit(monday_prices, friday_prices)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "05eaa992-bed3-4af0-9508-c58be2f36381", - "metadata": {}, - "outputs": [], - "source": [ - "portfolio[vectorized_in_profit(monday_prices, friday_prices)]" + "vectorized_profit_with_bonus = np.vectorize(profit_with_bonus)\n", + "vectorized_profit_with_bonus(portfolio[\"mon\"], portfolio[\"fri\"])" ] }, { @@ -506,7 +456,7 @@ "metadata": {}, "outputs": [], "source": [ - "in_profit(3, 5)" + "profit_with_bonus(3, 5)" ] }, { @@ -517,18 +467,14 @@ "outputs": [], "source": [ "@np.vectorize\n", - "def in_profit(first_day, last_day):\n", - " if last_day > first_day:\n", - " return True\n", - " return False\n", - "\n", - "\n", - "monday_prices = portfolio[\"mon\"]\n", - "friday_prices = portfolio[\"fri\"]\n", + "def profit_with_bonus(first_day, last_day):\n", + " if last_day >= first_day * 1.01:\n", + " return (last_day - first_day) * 1.1\n", + " else:\n", + " return last_day - first_day\n", "\n", - "print(in_profit(monday_prices, friday_prices))\n", "\n", - "portfolio[in_profit(monday_prices, friday_prices)]" + "profit_with_bonus(portfolio[\"mon\"], portfolio[\"fri\"])" ] }, { @@ -538,7 +484,7 @@ "metadata": {}, "outputs": [], "source": [ - "in_profit(3, 5)" + "profit_with_bonus(3, 5)" ] }, { @@ -548,7 +494,11 @@ "metadata": {}, "outputs": [], "source": [ - "portfolio[np.where(friday_prices > monday_prices, True, False)]" + "np.where(\n", + " portfolio[\"fri\"] > portfolio[\"mon\"] * 1.01,\n", + " (portfolio[\"fri\"] - portfolio[\"mon\"]) * 1.1,\n", + " portfolio[\"fri\"] - portfolio[\"mon\"],\n", + ")" ] } ], @@ -568,7 +518,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.12.0" + "version": "3.12.7" } }, "nbformat": 4, diff --git a/polars-missing-data/tutorial_code.ipynb b/polars-missing-data/tutorial_code.ipynb index d4efb09e7d..408e1ce561 100644 --- a/polars-missing-data/tutorial_code.ipynb +++ b/polars-missing-data/tutorial_code.ipynb @@ -15,13 +15,13 @@ "metadata": {}, "outputs": [], "source": [ - "!python -m pip install polars" + "!python -m pip install polars\n" ] }, { "cell_type": "code", "execution_count": null, - "id": "22c85bb2-8b10-4075-ab58-3b212f1ed050", + "id": "8a05aa96-ae34-41de-a7ef-1498e6d94cab", "metadata": {}, "outputs": [], "source": [ @@ -29,7 +29,20 @@ "\n", "tips = pl.scan_parquet(\"tips.parquet\")\n", "\n", - "tips.null_count().collect()" + "tips.collect()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "22c85bb2-8b10-4075-ab58-3b212f1ed050", + "metadata": {}, + "outputs": [], + "source": [ + "(\n", + " tips\n", + " .null_count()\n", + ").collect()\n" ] }, { @@ -51,7 +64,28 @@ "\n", "tips = pl.scan_parquet(\"tips.parquet\")\n", "\n", - "tips.filter(pl.col(\"total\").is_null() & pl.col(\"tip\").is_null()).collect()" + "(\n", + " tips\n", + " .filter(\n", + " pl.col(\"total\").is_null() & pl.col(\"tip\").is_null()\n", + " )\n", + ").collect()\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d79f6c04-cfcd-45e5-aa36-4a097d6e2082", + "metadata": {}, + "outputs": [], + "source": [ + "(\n", + " tips\n", + " .drop_nulls(pl.col(\"total\"))\n", + " .filter(\n", + " pl.col(\"total\").is_null() & pl.col(\"tip\").is_null()\n", + " )\n", + ").collect()\n" ] }, { @@ -62,10 +96,10 @@ "outputs": [], "source": [ "(\n", - " tips.drop_nulls(\"total\")\n", + " tips.drop_nulls(pl.col(\"total\"))\n", " .with_columns(pl.col(\"tip\").fill_null(0))\n", " .filter(pl.col(\"tip\").is_null())\n", - ").collect()" + ").collect()\n" ] }, { @@ -87,7 +121,7 @@ "\n", "tips = pl.scan_parquet(\"tips.parquet\")\n", "\n", - "tips.filter(pl.col(\"time\").is_null()).collect()" + "(tips.filter(pl.col(\"time\").is_null())).collect()\n" ] }, { @@ -97,7 +131,12 @@ "metadata": {}, "outputs": [], "source": [ - "tips.filter(pl.col(\"record_id\").is_in([2, 3, 4, 14, 15, 16])).collect()" + "(\n", + " tips\n", + " .filter(\n", + " pl.col(\"record_id\").is_in([2, 3, 4, 14, 15, 16])\n", + " )\n", + ").collect()\n" ] }, { @@ -108,15 +147,12 @@ "outputs": [], "source": [ "(\n", - " tips.drop_nulls(\"total\")\n", + " tips\n", + " .drop_nulls(\"total\")\n", " .with_columns(pl.col(\"tip\").fill_null(0))\n", - " .with_columns(\n", - " pl.when(pl.col(\"record_id\") == 2)\n", - " .then(pl.col(\"time\").fill_null(strategy=\"forward\"))\n", - " .otherwise(pl.col(\"time\").fill_null(strategy=\"backward\"))\n", - " )\n", + " .with_columns(pl.col(\"time\").fill_null(strategy=\"forward\"))\n", " .filter(pl.col(\"record_id\").is_in([3, 15]))\n", - ").collect()" + ").collect()\n" ] }, { @@ -134,21 +170,16 @@ "metadata": {}, "outputs": [], "source": [ - "tips = pl.scan_parquet(\"tips.parquet\")\n", + "import polars as pl\n", "\n", - "(tips.filter(pl.all_horizontal(pl.col(\"total\", \"tip\").is_null()))).collect()" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "91b280c0-f7f7-4874-86b6-df349b8b6927", - "metadata": {}, - "outputs": [], - "source": [ "tips = pl.scan_parquet(\"tips.parquet\")\n", "\n", - "(tips.filter(pl.all_horizontal(pl.col(\"total\", \"tip\").is_null()))).collect()" + "(\n", + " tips\n", + " .filter(\n", + " pl.all_horizontal(pl.col(\"total\", \"tip\").is_null())\n", + " )\n", + ").collect()\n" ] }, { @@ -158,7 +189,14 @@ "metadata": {}, "outputs": [], "source": [ - "(tips.filter(~pl.all_horizontal(pl.col(\"total\", \"tip\").is_null()))).collect()" + "tips = pl.scan_parquet(\"tips.parquet\")\n", + "\n", + "(\n", + " tips\n", + " .filter(\n", + " ~pl.all_horizontal(pl.col(\"total\", \"tip\").is_null())\n", + " )\n", + ").collect()\n" ] }, { @@ -173,14 +211,13 @@ "tips = pl.scan_parquet(\"tips.parquet\")\n", "\n", "(\n", - " tips.filter(~pl.all_horizontal(pl.col(\"total\", \"tip\").is_null()))\n", - " .with_columns(pl.col(\"tip\").fill_null(0))\n", - " .with_columns(\n", - " pl.when(pl.col(\"record_id\") == 2)\n", - " .then(pl.col(\"time\").fill_null(strategy=\"forward\"))\n", - " .otherwise(pl.col(\"time\").fill_null(strategy=\"backward\"))\n", + " tips\n", + " .filter(\n", + " ~pl.all_horizontal(pl.col(\"total\", \"tip\").is_null())\n", " )\n", - ").null_count().collect()" + " .with_columns(pl.col(\"tip\").fill_null(0))\n", + " .with_columns(pl.col(\"time\").fill_null(strategy=\"forward\"))\n", + ").null_count().collect()\n" ] }, { @@ -210,7 +247,7 @@ " }\n", ")\n", "\n", - "scientists.collect()" + "scientists.collect()\n" ] }, { @@ -226,7 +263,7 @@ " scientists.with_columns(cs.string().fill_null(\"Unknown\")).with_columns(\n", " cs.integer().fill_null(0)\n", " )\n", - ").collect()" + ").collect()\n" ] }, { @@ -248,7 +285,7 @@ "\n", "sales_trends = pl.scan_csv(\"sales_trends.csv\")\n", "\n", - "sales_trends.collect()" + "sales_trends.collect()\n" ] }, { @@ -259,12 +296,13 @@ "outputs": [], "source": [ "(\n", - " sales_trends.with_columns(\n", + " sales_trends\n", + " .with_columns(\n", " pl.col(\"next_year\").replace(\n", " [float(\"inf\"), -float(\"inf\"), float(\"NaN\")], None\n", " )\n", " )\n", - ").collect()" + ").collect()\n" ] }, { @@ -275,17 +313,19 @@ "outputs": [], "source": [ "(\n", - " sales_trends.with_columns(\n", + " sales_trends\n", + " .with_columns(\n", " pl.col(\"next_year\").replace(\n", " [float(\"inf\"), -float(\"inf\"), float(\"NaN\")], None\n", " )\n", - " ).with_columns(\n", + " )\n", + " .with_columns(\n", " pl.col(\"next_year\").fill_null(\n", " pl.col(\"current_year\")\n", " + (pl.col(\"current_year\") - pl.col(\"last_year\"))\n", " )\n", " )\n", - ").collect()" + ").collect()\n" ] }, { @@ -307,7 +347,7 @@ "\n", "episodes = pl.scan_parquet(\"ft_exercise.parquet\")\n", "\n", - "episodes.null_count().collect()" + "episodes.null_count().collect()\n" ] }, { @@ -321,17 +361,22 @@ "\n", "episodes = pl.scan_parquet(\"ft_exercise.parquet\")\n", "\n", - "episodes.with_columns(\n", - " pl.when(pl.col(\"episode\") == 6)\n", - " .then(pl.col(\"series\").fill_null(strategy=\"forward\"))\n", - " .otherwise(pl.col(\"series\").fill_null(strategy=\"backward\"))\n", - ").with_columns(\n", - " pl.when(pl.col(\"episode\") == 4)\n", - " .then(pl.col(\"title\").fill_null(\"The Hotel Inspectors\"))\n", - " .otherwise(pl.col(\"title\").fill_null(\"Waldorf Salad\"))\n", - ").with_columns(\n", - " pl.col(\"original_date\").interpolate()\n", - ").null_count().collect()" + "(\n", + " episodes\n", + " .with_columns(\n", + " pl.when(pl.col(\"episode\") == 6)\n", + " .then(pl.col(\"series\").fill_null(strategy=\"forward\"))\n", + " .otherwise(pl.col(\"series\").fill_null(strategy=\"backward\"))\n", + " )\n", + " .with_columns(\n", + " pl.when(pl.col(\"episode\") == 4)\n", + " .then(pl.col(\"title\").fill_null(\"The Hotel Inspectors\"))\n", + " .otherwise(pl.col(\"title\").fill_null(\"Waldorf Salad\"))\n", + " )\n", + " .with_columns(\n", + " pl.col(\"original_date\").interpolate()\n", + " )\n", + ").null_count().collect()\n" ] } ], diff --git a/python-dicts/README.md b/python-dicts/README.md new file mode 100644 index 0000000000..ac4a108380 --- /dev/null +++ b/python-dicts/README.md @@ -0,0 +1,3 @@ +# Dictionaries in Python + +This folder provides the code examples for the Real Python tutorial [Dictionaries in Python](https://realpython.com/python-dicts/). diff --git a/python-dicts/configs.py b/python-dicts/configs.py new file mode 100644 index 0000000000..4e7c01c880 --- /dev/null +++ b/python-dicts/configs.py @@ -0,0 +1,62 @@ +config = { + "color": "green", + "width": 42, + "height": 100, + "font": "Courier", +} + +# Access a value through its key +print(config["color"]) + +# Update a value +config["font"] = "Helvetica" +print(config) + +config = { + "color": "green", + "width": 42, + "height": 100, + "font": "Courier", +} +user_config = { + "path": "/home", + "color": "red", + "font": "Arial", + "position": (200, 100), +} +config.update(user_config) +print(config) +config.update([("width", 200), ("api_key", 1234)]) +print(config) +config.update(color="yellow", script="__main__.py") +print(config) + +default_config = { + "color": "green", + "width": 42, + "height": 100, + "font": "Courier", +} +user_config = { + "path": "/home", + "color": "red", + "font": "Arial", + "position": (200, 100), +} +config = default_config | user_config +print(config) + +config = { + "color": "green", + "width": 42, + "height": 100, + "font": "Courier", +} +user_config = { + "path": "/home", + "color": "red", + "font": "Arial", + "position": (200, 100), +} +config |= user_config +print(config) diff --git a/python-dicts/counter.py b/python-dicts/counter.py new file mode 100644 index 0000000000..511cac6f08 --- /dev/null +++ b/python-dicts/counter.py @@ -0,0 +1,3 @@ +from collections import Counter + +print(Counter("mississippi")) diff --git a/python-dicts/dict_zip.py b/python-dicts/dict_zip.py new file mode 100644 index 0000000000..6038d67fdc --- /dev/null +++ b/python-dicts/dict_zip.py @@ -0,0 +1,4 @@ +cities = ["Colorado", "Chicago", "Boston", "Minnesota", "Milwaukee", "Seattle"] +teams = ["Rockies", "White Sox", "Red Sox", "Twins", "Brewers", "Mariners"] + +print(dict(zip(cities, teams))) diff --git a/python-dicts/employees.py b/python-dicts/employees.py new file mode 100644 index 0000000000..6671bc6390 --- /dev/null +++ b/python-dicts/employees.py @@ -0,0 +1,15 @@ +from collections import defaultdict + +employees = [ + ("Sales", "John"), + ("Sales", "Martin"), + ("Accounting", "Kate"), + ("Marketing", "Elizabeth"), + ("Marketing", "Linda"), +] + +departments = defaultdict(list) +for department, employee in employees: + departments[department].append(employee) + +print(departments) diff --git a/python-dicts/equality.py b/python-dicts/equality.py new file mode 100644 index 0000000000..822bceba5b --- /dev/null +++ b/python-dicts/equality.py @@ -0,0 +1,4 @@ +print([1, 2, 3] == [3, 2, 1]) +print({1: 1, 2: 2, 3: 3} == {3: 3, 2: 2, 1: 1}) +print([1, 2, 3] != [3, 2, 1]) +print({1: 1, 2: 2, 3: 3} != {3: 3, 2: 2, 1: 1}) diff --git a/python-dicts/from_keys.py b/python-dicts/from_keys.py new file mode 100644 index 0000000000..ab7815163b --- /dev/null +++ b/python-dicts/from_keys.py @@ -0,0 +1,2 @@ +inventory = dict.fromkeys(["apple", "orange", "banana", "mango"], 0) +print(inventory) diff --git a/python-dicts/inventory.py b/python-dicts/inventory.py new file mode 100644 index 0000000000..dc58767e33 --- /dev/null +++ b/python-dicts/inventory.py @@ -0,0 +1,12 @@ +inventory = {"apple": 100, "orange": 80, "banana": 100} +inventory.get("apple") + +print(inventory.get("mango")) + +print(inventory.get("mango", 0)) + +print(inventory.values()) + +print(inventory.keys()) + +print(inventory.items()) diff --git a/python-dicts/iteration.py b/python-dicts/iteration.py new file mode 100644 index 0000000000..27c1ebe1a5 --- /dev/null +++ b/python-dicts/iteration.py @@ -0,0 +1,32 @@ +students = { + "Alice": 89.5, + "Bob": 76.0, + "Charlie": 92.3, + "Diana": 84.7, + "Ethan": 88.9, + "Fiona": 95.6, + "George": 73.4, + "Hannah": 81.2, +} +for student in students: + print(student) + +for student in students.keys(): + print(student) + +for student in students: + print(student, "->", students[student]) + +MLB_teams = { + "Colorado": "Rockies", + "Chicago": "White Sox", + "Boston": "Red Sox", + "Minnesota": "Twins", + "Milwaukee": "Brewers", + "Seattle": "Mariners", +} +for team in MLB_teams.values(): + print(team) + +for city, team in MLB_teams.items(): + print(city, "->", team) diff --git a/python-dicts/membership.py b/python-dicts/membership.py new file mode 100644 index 0000000000..b3c1d6fc0e --- /dev/null +++ b/python-dicts/membership.py @@ -0,0 +1,32 @@ +import timeit + +MLB_teams = { + "Colorado": "Rockies", + "Chicago": "White Sox", + "Boston": "Red Sox", + "Minnesota": "Twins", + "Milwaukee": "Brewers", + "Seattle": "Mariners", +} + +# Run timeit to compare the membership test +time_in_dict = timeit.timeit( + '"Milwaukee" in MLB_teams', globals=globals(), number=1000000 +) +time_in_keys = timeit.timeit( + '"Milwaukee" in MLB_teams.keys()', globals=globals(), number=1000000 +) +time_not_in_dict = timeit.timeit( + '"Indianapolis" in MLB_teams', globals=globals(), number=1000000 +) +time_not_in_keys = timeit.timeit( + '"Indianapolis" in MLB_teams.keys()', globals=globals(), number=1000000 +) + +print( + f"{time_in_dict = } seconds", + f"{time_in_keys = } seconds", + f"{time_not_in_dict = } seconds", + f"{time_not_in_keys = } seconds", + sep="\n", +) diff --git a/python-dicts/mlb_teams.py b/python-dicts/mlb_teams.py new file mode 100644 index 0000000000..82751e201b --- /dev/null +++ b/python-dicts/mlb_teams.py @@ -0,0 +1,33 @@ +MLB_teams = { + "Colorado": "Rockies", + # "Chicago": "White Sox", + "Chicago": "Chicago Cubs", + "Boston": "Red Sox", + "Minnesota": "Twins", + "Milwaukee": "Brewers", + "Seattle": "Mariners", +} +print(MLB_teams) + +MLB_teams = dict( + [ + ("Colorado", "Rockies"), + ("Chicago", "White Sox"), + ("Boston", "Red Sox"), + ("Minnesota", "Twins"), + ("Milwaukee", "Brewers"), + ("Seattle", "Mariners"), + ] +) +print(MLB_teams) + + +print("Milwaukee" in MLB_teams) +print("Indianapolis" in MLB_teams) +print("Indianapolis" not in MLB_teams) +print("Milwaukee" in MLB_teams.keys()) +print("Indianapolis" in MLB_teams.keys()) +print("Indianapolis" not in MLB_teams.keys()) + +print(("Boston", "Red Sox") in MLB_teams.items()) +print(("Boston", "Red Sox") not in MLB_teams.items()) diff --git a/python-dicts/number.py b/python-dicts/number.py new file mode 100644 index 0000000000..204e6c17f6 --- /dev/null +++ b/python-dicts/number.py @@ -0,0 +1,6 @@ +class Number: + def __init__(self, value): + self.value = value + + +print(Number(42).__dict__) diff --git a/python-dicts/person.py b/python-dicts/person.py new file mode 100644 index 0000000000..3c28b64260 --- /dev/null +++ b/python-dicts/person.py @@ -0,0 +1,21 @@ +person = { + "first_name": "John", + "last_name": "Doe", + "age": 35, + "spouse": "Jane", + "children": ["Ralph", "Betty", "Bob"], + "pets": {"dog": "Frieda", "cat": "Sox"}, +} + +print(person["children"][0]) +print(person["children"][2]) +print(person["pets"]["dog"]) + +person = {} +person["first_name"] = "John" +person["last_name"] = "Doe" +person["age"] = 35 +person["spouse"] = "Jane" +person["children"] = ["Ralph", "Betty", "Bob"] +person["pets"] = {"dog": "Frieda", "cat": "Sox"} +print(person) diff --git a/python-dicts/sorted_dict.py b/python-dicts/sorted_dict.py new file mode 100644 index 0000000000..b6d4208042 --- /dev/null +++ b/python-dicts/sorted_dict.py @@ -0,0 +1,35 @@ +class SortableDict(dict): + def sort_by_keys(self, reverse=False): + sorted_items = sorted( + self.items(), key=lambda item: item[0], reverse=reverse + ) + self.clear() + self.update(sorted_items) + + def sort_by_values(self, reverse=False): + sorted_items = sorted( + self.items(), key=lambda item: item[1], reverse=reverse + ) + self.clear() + self.update(sorted_items) + + +students = SortableDict( + { + "Alice": 89.5, + "Bob": 76.0, + "Charlie": 92.3, + "Diana": 84.7, + "Ethan": 88.9, + "Fiona": 95.6, + "George": 73.4, + "Hannah": 81.2, + } +) + +print(id(students)) +students.sort_by_keys() +print(students) +students.sort_by_values(reverse=True) +print(students) +print(id(students)) diff --git a/python-dicts/squares.py b/python-dicts/squares.py new file mode 100644 index 0000000000..7a78da28af --- /dev/null +++ b/python-dicts/squares.py @@ -0,0 +1,10 @@ +squares = {} + +for integer in range(1, 10): + squares[integer] = integer**2 + +print(squares) + +squares = {integer: integer**2 for integer in range(1, 10)} + +print(squares) diff --git a/python-dicts/students.py b/python-dicts/students.py new file mode 100644 index 0000000000..5fdeba916d --- /dev/null +++ b/python-dicts/students.py @@ -0,0 +1,12 @@ +students = { + "Alice": 89.5, + "Bob": 76.0, + "Charlie": 92.3, + "Diana": 84.7, + "Ethan": 88.9, + "Fiona": 95.6, + "George": 73.4, + "Hannah": 81.2, +} +print(dict(sorted(students.items(), key=lambda item: item[1]))) +print(dict(sorted(students.items(), key=lambda item: item[1], reverse=True))) diff --git a/python-dicts/values.py b/python-dicts/values.py new file mode 100644 index 0000000000..c85a45c026 --- /dev/null +++ b/python-dicts/values.py @@ -0,0 +1,14 @@ +class Point: + def __init__(self, x, y): + self.x = x + self.y = y + + +print( + { + "colors": ["red", "green", "blue"], + "plugins": {"py_code", "dev_sugar", "fasting_py"}, + "timeout": 3, + "position": Point(42, 21), + } +) diff --git a/python-f-string/README.md b/python-f-string/README.md index 6b2bfa0c4c..d014672494 100644 --- a/python-f-string/README.md +++ b/python-f-string/README.md @@ -1,3 +1,3 @@ -# Python's F-String: An Improved String Interpolation and Formatting Tool +# Python's F-String for String Interpolation and Formatting -This folder provides the code examples for the Real Python tutorial [Python's F-String: An Improved String Interpolation and Formatting Tool](https://realpython.com/python-f-string/). \ No newline at end of file +This folder provides the code examples for the Real Python tutorial [Python's F-String for String Interpolation and Formatting](https://realpython.com/python-f-strings/). diff --git a/python-get-all-files-in-directory/create_large_dir.py b/python-get-all-files-in-directory/create_large_dir.py index 2cf4623c49..5e7c9671e5 100644 --- a/python-get-all-files-in-directory/create_large_dir.py +++ b/python-get-all-files-in-directory/create_large_dir.py @@ -66,18 +66,18 @@ class Item: def create_item(item: Item, path_to: Path = Path.cwd()) -> None: - if not item.children and not item.junk_files: - path_to.joinpath(item.name).touch() + if not item.children and not item.num_junk_files: + path_to.joinpath(item.name).touch(exist_ok=True) return root = path_to.joinpath(item.name) - root.mkdir() + root.mkdir(exist_ok=True) for child in item.children: create_item(child, path_to=root) for i in range(item.num_junk_files): - root.joinpath(f"{i}.txt").touch() + root.joinpath(f"{i}.txt").touch(exist_ok=True) create_item(folder_structure) diff --git a/python-range/README.md b/python-range/README.md index 138a72e91d..6b06fe92b6 100644 --- a/python-range/README.md +++ b/python-range/README.md @@ -2,6 +2,22 @@ This repository holds the code for Real Python's [Python `range()`: Represent Numerical Ranges](https://realpython.com/python-range/) tutorial. +## reverse_range() + +In [`reverse_range.py`](reverse_range.py), you can find an explicit implementation of a function that can reverse a general range. + +```python +>>> from reverse_range import reverse_range + +>>> reverse_range(range(1, 20, 4)) +range(17, 0, -4) + +>>> list(reverse_range(range(1, 20, 4))) +[17, 13, 9, 5, 1] +``` + +In practical applications, you should use `reversed(range(1, 20, 4))` or `range(1, 20, 4)[::-1]` instead. + ## PiDigits The file [`pi_digits.py`](pi_digits.py) shows the implementation of `PiDigits` which is an integer-like type that can be used as arguments to `range()`: diff --git a/python-range/reverse_range.py b/python-range/reverse_range.py new file mode 100644 index 0000000000..4b776905f1 --- /dev/null +++ b/python-range/reverse_range.py @@ -0,0 +1,27 @@ +def reverse_range(rng): + """Explicitly calculate necessary parameters to reverse a general range. + + In practice, you should use reversed() or [::-1] instead. + """ + adj = 1 if rng.step > 0 else -1 + return range( + (rng.stop - adj) - (rng.stop - rng.start - adj) % rng.step, + rng.start - adj, + -rng.step, + ) + + +if __name__ == "__main__": + numbers = range(1, 20, 4) + + print("\nOriginal:") + print(numbers) + print(list(numbers)) + + print("\nReversed:") + print(reverse_range(numbers)) + print(list(reverse_range(numbers))) + + print("\nTwice reversed, has the same elements as the original:") + print(reverse_range(reverse_range(numbers))) + print(list(reverse_range(reverse_range(numbers)))) diff --git a/python-set-comprehension/README.md b/python-set-comprehension/README.md new file mode 100644 index 0000000000..a0022a23fe --- /dev/null +++ b/python-set-comprehension/README.md @@ -0,0 +1,3 @@ +# Python Set Comprehensions: How and When to Use Them + +This folder provides the code examples for the Real Python tutorial [Python Set Comprehensions: How and When to Use Them](https://realpython.com/python-set-comprehension/). diff --git a/python-set-comprehension/colors.py b/python-set-comprehension/colors.py new file mode 100644 index 0000000000..a7fab40509 --- /dev/null +++ b/python-set-comprehension/colors.py @@ -0,0 +1,4 @@ +colors = {"blue", "red", "green", "orange", "green"} +print(colors) +colors.add("purple") +print(colors) diff --git a/python-set-comprehension/complex_expression.py b/python-set-comprehension/complex_expression.py new file mode 100644 index 0000000000..b23821e1b2 --- /dev/null +++ b/python-set-comprehension/complex_expression.py @@ -0,0 +1,13 @@ +numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] + +print({number**2 if number % 2 == 0 else number**3 for number in numbers}) + +result_set = set() +for number in numbers: + if number % 2 == 0: + value = number**2 + else: + value = number**3 + result_set.add(value) + +print(result_set) diff --git a/python-set-comprehension/emails.py b/python-set-comprehension/emails.py new file mode 100644 index 0000000000..138c79ca95 --- /dev/null +++ b/python-set-comprehension/emails.py @@ -0,0 +1,10 @@ +emails = { + " alice@example.org ", + "BOB@example.com", + "charlie@EXAMPLE.com", + "David@example.net", + " bob@example.com", + "JohnDoe@example.com", +} + +print({email.strip().lower() for email in emails}) diff --git a/python-set-comprehension/filter_emails.py b/python-set-comprehension/filter_emails.py new file mode 100644 index 0000000000..9552a7e326 --- /dev/null +++ b/python-set-comprehension/filter_emails.py @@ -0,0 +1,9 @@ +emails_set = { + "alice@example.org", + "bob@example.com", + "johndoe@example.com", + "charlie@example.com", + "david@example.net", +} + +print({email for email in emails_set if email.endswith(".com")}) diff --git a/python-set-comprehension/matrix.py b/python-set-comprehension/matrix.py new file mode 100644 index 0000000000..91ba5d3f66 --- /dev/null +++ b/python-set-comprehension/matrix.py @@ -0,0 +1,8 @@ +matrix = [ + [9, 3, 8, 3], + [4, 5, 2, 8], + [6, 4, 3, 1], + [1, 0, 4, 5], +] + +print({value**2 for row in matrix for value in row}) diff --git a/python-set-comprehension/text.py b/python-set-comprehension/text.py new file mode 100644 index 0000000000..0e75e62995 --- /dev/null +++ b/python-set-comprehension/text.py @@ -0,0 +1,18 @@ +unique_words = set() +text = """ +Beautiful is better than ugly +Explicit is better than implicit +Simple is better than complex +Complex is better than complicated +""".lower() + +for word in text.split(): + unique_words.add(word) + +print(unique_words) + +print(set(text.split())) + +unique_words = {word for word in text.split()} + +print(unique_words) diff --git a/python-set-comprehension/tools.py b/python-set-comprehension/tools.py new file mode 100644 index 0000000000..ee3f1dea32 --- /dev/null +++ b/python-set-comprehension/tools.py @@ -0,0 +1,7 @@ +tools = ["Python", "Django", "Flask", "pandas", "NumPy"] +tools_set = {tool.lower() for tool in tools} + +print(tools_set) +print("python".lower() in tools_set) +print("Pandas".lower() in tools_set) +print("Numpy".lower() in tools_set)