Like I keep harping so often, one of the nicest thing about Python is that it can interface to native libraries using only standard Python, with no additional native code or changes to the native code needed. It means you can create Python bindings to a native library writing only a bit of Python, using e.g. the built-in
ctypes and perhaps
struct modules.
To me, it means you only write Python code for the topmost "business" logic: in my case, mostly graphical user interfaces. I used to do a lot of backend web server coding, though.
(There is one interesting Python tricks when using Python under FastCGI, that I'd love to see used more. If your Python page engine acts like a FastCGI server, you can preload things like the site navigation structure into an object, and then just fork the entire Python process when serving a new client request, like FastCGI servers normally do (with the same implied resource checking limitations and checks suggested). This cuts down on latency, and means common I/O (to find out the navigation structure to properly implement menus and paths and such) is not unnecessarily duplicated. Makes a significant difference in practice. You can even drop privileges based on a quick prefix URL check, before doing any work for the request, and make security bugs slightly less dangerous that way.)
It also means that Python examples where you do serious data processing or computation (without dedicated libraries like numpy/scipy), are not useful.
You want to use Python where its strengths lie, and those are mainly in the high-level concepts.
Because of that, I recommend learning early how to effectively use Python with GUI toolkits. Currently, I recommend Qt5, via either PySide2 or PyQt5 bindings. The two are very similar, and really only differ in their dynamic UI building facilities. QtPy (a Python package) is a thin loader layer on top of either, that provides the same interface to both, by implementing a minimal
uic class that PyQt5 provides when using PySide2 bindings.
The main reason for that recommendation is so that you get comfortable how to use 1) derived classes to add your own desired functionality, and 2) event-driven programming model.
With Python and Qt5, I also recommend keeping user interfaces as XML .ui files (that you can save from e.g. Qt Designer), and loading and constructing them at run time using the aforementioned
uic class. For maintenance and adaptability reasons, I strenuously recommend against using any Designer or similar utilities to generate the Python code needed to implement an user interface: that shit is write-only. It looks nice-ish, and you can make tiny changes, but major revamping is always a complete rewrite. With .ui files, you just switch to another one, and only have to make sure the existing element names are all implemented and match.
If you dislike Qt, then Gtk is also a possibility, but it looks very, very similar to Qt Python code. Gtk differs from Qt in that there is just one set of bindings, the ones provided by GObject Introspection, aka GI or gir. In current Linux distributions, these are packages named
gir1.2-library, and a Python
gi module (in Linux, typically
python3-gi package) builds the bindings to the library using the GI files. It is quite nifty, actually. (All you need is
import gi;
gi.require_version('Gtk','3.0') (for Gtk 3.x); and
from gi.repository import Gtk. Same for any other library that you have GI for.)
The UI builder for Gtk is called Glade, and it too saves .ui files (not compatible with Qt ones) that you can load and instantiate at run time.
Because current Python interpreters can only run one thread of Python code within each process simultaneously, you will soon have to learn to do distributed processing using process-based parallelism.
Personally, I heavily use a Python I/O thread, and Python
Queue or toolkit-specific event dispatching to communicate between the main UI thread and the I/O thread. For communication with a microcontroller or similar, I often use two I/O threads and full-duplex asynchronous comms. (In Linux, with native serial and USB Serial, I exclusively use
termios, not any serial library.) For computation, that/those I/O threads communicate with other processes that do the heavy computation, using the Python
subprocess module. Note that when using libraries like numpy/scipy/fftw etc., the library code can run concurrently to Python code in a different thread, which means that there is no problem using e.g. multiple Python threads for computation using these libraries. It is only Python code that cannot run concurrently in different threads in the same process: different processes, or Python and native code, thread-parallelizes just fine.
If you want Python examples (noting that I only have Linux OS available right now), let me know: I really like to help others learn, and if you can come up with an interesting example scenario, I'd be happy to give it a try. It's even better if others are willing to point out weaknesses and suggest changes in my suggestions, because then I learn myself too. I'm of the opinion that one never knows all nor enough, and can and should always keep learning.