Page Body

Exploring Python With the Python Interpreter

One of Python's strengths is how interactive it is. Its interpreter provides a friendly environment for you to start quickly creating and iterating on your code. Also, it provides a path for you to explore the Python language itself and what its libraries are capable of.

Note: If you are not familiar with the GNU/Linux command line interface, or you intend to use a script obtained from this site, review the Conventions page before proceeding.

The Python Interpreter

Starting and Exiting

On GNU/Linux distributions like Debian or Fedora, Python comes packaged with your operating system. We can perform an interactive start of the Python interpreter by entering python3.

For example, on a Debian 12 system, starting an interactive Python session looks something like this:

$ python3
Python 3.11.2 (main, Mar 13 2023, 17:18:29) [GCC 12.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> 

Notice that the command prompt has changed from the Bash shell's $ to the interactive Python shell's >>>.

To exit back to the Bash shell, we can either use the Ctrl+d keyboard shortcut or enter quit().

We can also utilize Python non-interactively to quickly run some Python code, without ever leaving the Bash shell, by using the -c option:

$ python3 -c 'print("Simple is better than complex.")'
Simple is better than complex.
$ 

Often, you will see python3 used with the -m option. This option runs a .py file as a script. For example, the package installer for Python, pip, is used this way:

python3 -m pip install ex_pkg...

Also, we can start up an interactive Python session after running a .py file as a script (or when using the previously mentioned -c option) by adding the -i option to the command:

$ python3 -i -c 'print("Flat is better than nested.")'
Flat is better than nested.
>>> 

As you progress in your Python journey, knowing the different ways that you can interact with Python may help you better achieve your goals.

Help

In Python, there are a vast array of objects you may want to inspect, so knowing how to use the help system is beneficial.

Entering help() invokes the built-in help system:

>>> help()

Welcome to Python 3.11's help utility!

If this is your first time using Python, you should definitely check out
the tutorial on the internet at https://docs.python.org/3.11/tutorial/.

Enter the name of any module, keyword, or topic to get help on writing
Python programs and using Python modules.  To quit this help utility and
return to the interpreter, just type "quit".

To get a list of available modules, keywords, symbols, or topics, type
"modules", "keywords", "symbols", or "topics".  Each module also comes
with a one-line summary of what it does; to list the modules whose name
or summary contain a given string such as "spam", type "modules spam".

help> 

Inside the help system, you have a new prompt, help>, where you can enter module, keyword, or topic names to get more information. The blurb above does a good job of giving you a few places you may want to get started.

Before you proceed, you may want to make sure that you are familiar with less keyboard shortcuts. On Debian and Fedora GNU/Linux systems, less is likely going to be the paginator application used by the Python help system, and understanding how it works will help you navigate the system.

To exit the help system, enter quit.

Often, you will use the help system non-interactively. This can be accomplished by passing either the string name of a module/function/class/method/keyword/documentation topic (or a Python object itself) to help():

>>> help('topics')

Here is a list of available topics.  Enter any topic name to get more help.

ASSERTION           DELETION            LOOPING             SHIFTING
ASSIGNMENT          DICTIONARIES        MAPPINGMETHODS      SLICINGS
ATTRIBUTEMETHODS    DICTIONARYLITERALS  MAPPINGS            SPECIALATTRIBUTES
ATTRIBUTES          DYNAMICFEATURES     METHODS             SPECIALIDENTIFIERS
AUGMENTEDASSIGNMENT ELLIPSIS            MODULES             SPECIALMETHODS
BASICMETHODS        EXCEPTIONS          NAMESPACES          STRINGMETHODS
BINARY              EXECUTION           NONE                STRINGS
BITWISE             EXPRESSIONS         NUMBERMETHODS       SUBSCRIPTS
BOOLEAN             FLOAT               NUMBERS             TRACEBACKS
CALLABLEMETHODS     FORMATTING          OBJECTS             TRUTHVALUE
CALLS               FRAMEOBJECTS        OPERATORS           TUPLELITERALS
CLASSES             FRAMES              PACKAGES            TUPLES
CODEOBJECTS         FUNCTIONS           POWER               TYPEOBJECTS
COMPARISON          IDENTIFIERS         PRECEDENCE          TYPES
COMPLEX             IMPORTING           PRIVATENAMES        UNARY
CONDITIONAL         INTEGER             RETURNING           UNICODE
CONTEXTMANAGERS     LISTLITERALS        SCOPING             
CONVERSIONS         LISTS               SEQUENCEMETHODS     
DEBUGGING           LITERALS            SEQUENCES           

>>> help(str)

Help on class str in module builtins:

class str(object)
 |  str(object='') -> str
 |  str(bytes_or_buffer[, encoding[, errors]]) -> str
 ...

>>> 

Exploring Python

Introspection With dir()

Python has many built-in functions. One such function that is useful for examining Python scopes and objects is dir().

Object
Any data with state (attributes or values) and defined behavior (methods). Also, the ultimate base class of any new-style class.
Attribute
A value associated with an object that is usually referenced by name using dotted expressions (i.e., any name following a .).
Namespace
A mapping from names to objects. Namespaces are implemented as dictionaries.
Scope
A textual region of a Python program where a namespace is directly accessible.

Scopes are statically determined, but dynamically used. At any time during program execution, there are three or four nested scopes whose name spaces are directly accessible:

Local > Enclosed > Global > Built-In (LEGB)

  1. Local - The innermost scope. This scope is searched first and contains the local names.
  2. Enclosed - The scopes of enclosing functions, which are searched starting with the nearest scope. This scope contains non-local and non-global names.
  3. Global - The penultimate scope contains the current module’s global names.
  4. Built-In - The outermost scope is searched last and is the namespace containing built-in names.

Without any arguments, dir() returns the list of names in the current local scope:

>>> dir()
['__annotations__', '__builtins__', '__doc__', '__loader__', '__name__', '__package__', '__spec__']
>>> 

Above, we can see several system-defined names, or dunder names (because they are denoted with double underscores). Three names of particular interest are __builtins__, __doc__, and __name__:

  • __builtins__ provides access to Python's built-in identifiers.
  • __doc__ contains the class, function, or module's documentation string (docstring). This becomes useful when dealing with documented code.
  • __name__ is the name of the class, function, method, descriptor, or generator instance:
    >>> __name__
    '__main__'
    >>>
    

With an argument, dir() attempts to return a list of valid attributes for that object:

>>> from pprint import pprint
>>> pprint(dir(__doc__))
['__bool__',
 '__class__',
 '__delattr__',
 '__dir__',
 '__doc__',
 '__eq__',
 '__format__',
 '__ge__',
 '__getattribute__',
 '__getstate__',
 '__gt__',
 '__hash__',
 '__init__',
 '__init_subclass__',
 '__le__',
 '__lt__',
 '__ne__',
 '__new__',
 '__reduce__',
 '__reduce_ex__',
 '__repr__',
 '__setattr__',
 '__sizeof__',
 '__str__',
 '__subclasshook__']
>>> 

Namespaces

To better appreciate the previous output of dir(), we need to understand a little more about namespaces.

Namespaces are created at two different times and have different lifetimes:

  • The built-in names namespace is created when the Python interpreter starts and is never deleted. Built-in names also exist in a module called builtins.

    Most modules have the name __builtins__ made available, and __builtins__ is usually the builtins module or the value of that module's __dict__ attribute.

    >>> import builtins
    >>> builtins
    <module 'builtins' (built-in)>
    >>> builtins is __builtins__  # Identity test
    True
    >>> id(builtins), id(__builtins__)  # Obtain actual identities
    (140243143445216, 140243143445216)
    >>>
    
  • The global namespace for a module is created when the module definition is read in. The module namespace lasts until the interpreter quits.

    Statements executed by the top-level invocation of the interpreter (either read from a script or interactively) are considered part of a module called __main__, and have their own global namespace.

Since we are interactively using the Python interpreter, our statements are considered part of a module whose __name__ is __main__, and this module also makes the __builtins__ name available.

Scripts
Top-level files intended for execution by the user.
Modules
Collections of related code intended to be imported. Modules extend the power of scripts.
Packages
A directory of modules. Packages allow the hierarchical structure of the module namespace. Like how files are organized on a drive into folders and sub-folders, modules are organized into packages and subpackages.
To be considered a package (or subpackage), a directory must contain a file named __init__.py. Usually, this file includes the initialization code for the corresponding package.
Note that a package is still a module.
Libraries
An umbrella term referring to a reusable collection of code. Usually, a Python library is a directory of related modules and packages.

Often, this behavior is utilized when a Python developer wants to make their code usable as both a script and a module:

if __name__ == '__main__':
    ex_code

ex_code will only run when the module is executed as the main file, and not when it is imported as a module.

Utilizing Python's Interactivity

At its simplest, the Python interpreter can be used as a quick and handy calculator:

>>> 2 + 2
4
>>> (1000 / 23) - 7
36.47826086956522
>>> 

Above, the Python interpreter took our expression statements, evaluated them, and output the appropriate value.

We can use the bool() function to evaluate the truthiness of a statement:

>>> bool(2 + 2 == 5)
False
>>> bool(3 * 3 == 9)
True
>>> 

Let us write out some text and associate it with an identifier:

>>> sarah_connor = "Good morning, Dr. Silberman. How's the knee?"
>>> sarah_connor
"Good morning, Dr. Silberman. How's the knee?"
>>> print(sarah_connor)
Good morning, Dr. Silberman. How's the knee?
>>> 

Notice that when we just enter the identifier name, sarah_connor, we get the string, as is. However, when we supply it to the print() function as an argument, we get more readable output where the enclosing quotes are omitted. print() processes any escaped/special characters, as well.

Entering the string itself yields the same result as using its identifier:

>>> "Good morning, Dr. Silberman. How's the knee?"
"Good morning, Dr. Silberman. How's the knee?"
>>> 

Let us confirm that sarah_connor actually represents a string object using the type() function:

>>> type(sarah_connor)
<class 'str'>
>>> 

To double check the object type, we can use the isinstance() function, which returns a boolean, instead of the object type:

>>> isinstance(sarah_connor, str)
True
>>> 

Let us try working with a longer string:

>>> erik_lehnsherr = '''\
... I will bring you hope, old friend, and I ask only one thing in return.
... 
... Don't get in my way.\
... '''
>>> erik_lehnsherr
"I will bring you hope, old friend, and I ask only one thing in return.\n\nDon't get in my way."
>>> print(erik_lehnsherr)
I will bring you hope, old friend, and I ask only one thing in return.

Don't get in my way.
>>> 

Now, we can get a better appreciation for how nicely the print() function presents our strings.

In Python, a built-in operator or function can show different behavior for objects of different classes. This is referred to as operator overloading or function overloading. For example, the + operator can be used for mathematical addition, and both string and list concatenation:

>>> # String concatenation
>>> the = "This is what we're going to do. I'm going to count to three, "
>>> future = "and I'm going to move the coin."
>>> the + future
"This is what we're going to do. I'm going to count to three, and I'm going to move the coin."
>>> # List concatenation
>>> from textwrap import fill
>>> one = [
...     'You know, in Genosha I felt a lot of things. Pain, grief, admiration '
...     'for those who fought, despite the odds. But you know what the '
...     'oddest thing was? No one seemed shocked or surprised. Not even me!'
... ]
>>> two = [
...     'Yes, I was scared but, really, I just had the most profound sense '
...     "of déjà vu. As if past, present, and future didn't matter and "
...     'never had, because we always end up in the same, ugly place.'
... ]
>>> three = [
...     'Thing is, Magneto knows us better than Charles ever did. Knows we '
...     'know better. That most of us experience tragedies like Genosha as '
...     'a bit of déjà vu before getting on with our day. But the scariest '
...     "thing about Genosha wasn't the death or the chaos, it was a thought. "
...     'The only sane thought you can have when being chased by giant robots '
...     'that were built to crush you.'
... ]
>>> enough = ['Magneto was right.']
>>> for idx, para in enumerate(one + two + three + enough):
...     if idx < 3:
...         print(f'{fill(para)}\n')
...     else:
...         print(fill(para))
... 
You know, in Genosha I felt a lot of things. Pain, grief, admiration
for those who fought, despite the odds. But you know what the oddest
thing was? No one seemed shocked or surprised. Not even me!

Yes, I was scared but, really, I just had the most profound sense of
déjà vu. As if past, present, and future didn't matter and never had,
because we always end up in the same, ugly place.

Thing is, Magneto knows us better than Charles ever did. Knows we know
better. That most of us experience tragedies like Genosha as a bit of
déjà vu before getting on with our day. But the scariest thing about
Genosha wasn't the death or the chaos, it was a thought. The only sane
thought you can have when being chased by giant robots that were built
to crush you.

Magneto was right.
>>> 

Python strings are really sequences of characters. This means that we can do things like:

>>> from textwrap import fill
>>> quellcrist_falconer = (
...     'I thought I was freeing the human spirit, '
...     'but I was building the roads for Rome. '
...     'Eternal life for those who can afford it, '
...     "means eternal control over those who can't."
... )
>>> len(quellcrist_falconer)  # Length of a string
166
>>> # Lowest index of a substring
>>> quellcrist_falconer.find('the human spirit, ')
24
>>> quellcrist_falconer[42:80]  # String slice
'but I was building the roads for Rome.'
>>> 'control' in quellcrist_falconer  # Substring membership
True
>>> # Search and replace
>>> print(
...     fill(
...         quellcrist_falconer.replace('who can afford it, ',
...                                     'who can afford it...')
...     )
... )
I thought I was freeing the human spirit, but I was building the roads
for Rome. Eternal life for those who can afford it...means eternal
control over those who can't.
>>> 

With formatted string literals, or f-strings, we can place expressions into our Python strings:

>>> luke = 'cage'
>>> f'No one can {luke} a man if he truly wants to be free.'
'No one can cage a man if he truly wants to be free.'
>>> 

The format specification mini-language can be used in an f-string's replacement field to define how values are represented:

>>> 35098234.19872 - 82738493.29873
-47640259.10001
>>> # Use comma as separator, present with two decimal places as a float
>>> f'{35098234.19872 - 82738493.29873:,.2f}'
'-47,640,259.10'
>>> # Use tilde as fill character, center text, limit width to 66 characters
>>> eric_draven = 'Each one of these was a life, a life you helped destroy.'
>>> f'{eric_draven:~^66}'
'~~~~~Each one of these was a life, a life you helped destroy.~~~~~'
>>> 

Strings can be broken up into other sequence types, like lists, and joined back together to their original form:

>>> from pprint import pprint
>>> from textwrap import fill
>>> jessica_jones = "It took someone coming back from the dead...to show me that I've been dead, too."
>>> jj = jessica_jones.split()  # Split string into list of strings
>>> pprint(jj)
['It',
 'took',
 'someone',
 'coming',
 'back',
 'from',
 'the',
 'dead...to',
 'show',
 'me',
 'that',
 "I've",
 'been',
 'dead,',
 'too.']
>>> how_to_live = 'The problem is, I never really figured out how to live.'
>>> jj_how_to_live = jj + how_to_live.split()  # Concatenate lists of strings
>>> pprint(jj_how_to_live)
['It',
 'took',
 'someone',
 'coming',
 'back',
 'from',
 'the',
 'dead...to',
 'show',
 'me',
 'that',
 "I've",
 'been',
 'dead,',
 'too.',
 'The',
 'problem',
 'is,',
 'I',
 'never',
 'really',
 'figured',
 'out',
 'how',
 'to',
 'live.']
>>> # Join list of strings into single string
>>> print(fill(' '.join(jj_how_to_live)))
It took someone coming back from the dead...to show me that I've been
dead, too. The problem is, I never really figured out how to live.
>>> 

If you need an immutable sequence type, a tuple might be the object type that you are looking for:

>>> from pprint import pprint
>>> matt_murdock = (
...     'He spent the next month in a hospital, eating through a straw, '
...     'and I never slept better.'
... )
>>> dd = tuple(matt_murdock.split())
>>> pprint(dd)
('He',
 'spent',
 'the',
 'next',
 'month',
 'in',
 'a',
 'hospital,',
 'eating',
 'through',
 'a',
 'straw,',
 'and',
 'I',
 'never',
 'slept',
 'better.')
>>> # Elements of a tuple cannot be modified
>>> dd[-1] = 'better.\n\n- Matt Murdock'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: 'tuple' object does not support item assignment
>>> 

ranges are also immutable sequence types and are helpful when you need to loop a specific number of times in for loops:

>>> robert_and_william = (
...     'Look at the creatures you have to share this world with.',
...     "I don't belong to you, or this world. ",
...     'These men of stone.',
...     'I belong to another world.',
...     'All this ugliness, all this pain, so they can patch a hole '
...     'in their own, broken code.',
...     'I always have.'
... )
>>> 
>>> for idx in range(0, 6, 2):
...     print(robert_and_william[idx])
... 
Look at the creatures you have to share this world with.
These men of stone.
All this ugliness, all this pain, so they can patch a hole in their own, broken code.
>>> for idx in range(1, 6, 2):
...     print(robert_and_william[idx])
... 
I don't belong to you, or this world. 
I belong to another world.
I always have.
>>> 

If you just want to iterate through every element in a sequence, there is a more intuitive approach:

>>> apocalypse = (
...     'Always the same, and now, all this.\n',
...     'No more stones. No more spears. No more slings. No more swords. '
...     'No more weapons. No more systems. No more.\n',
...     'No more superpowers.'
... )
>>> for sentence in apocalypse:
...     print(sentence)
... 
Always the same, and now, all this.

No more stones. No more spears. No more slings. No more swords. No more weapons. No more systems. No more.

No more superpowers.
>>> 

This is possible because the apocalypse object is an iterable, i.e., an object capable of returning its members one at a time. All sequence types, some non-sequence types (e.g., dict), and objects that define an .__iter__() method, or a .__getitem__() method that implements sequence semantics, are iterables.

>>> hasattr(apocalypse, '__iter__')  # Test for attribute presence
True
>>> 

The for statement uses the iter() function on the container object. iter() returns an iterator object that defines a .__next__() method, which accesses container elements one at a time. When there are no more elements, .__next__() raises a StopIteration exception that tells the for loop to terminate.

>>> walkaway = "This all can't last.".split()
>>> iterator = iter(walkaway)
>>> iterator
<list_iterator object at 0x7f7062b3b790>
>>> next(iterator)
'This'
>>> next(iterator)
'all'
>>> next(iterator)
"can't"
>>> next(iterator)
'last.'
>>> next(iterator)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
StopIteration
>>> 

Sometimes, you may want both the index of a value, as well as the value itself, as you loop through a sequence. enumerate() can help here:

>>> sita = (
...     "There's more people than ever who don't have any love "
...     'for the way things are.'
... )
>>> for idx, word in enumerate(sita.split()):
...     if idx < 10:
...         print(f'{idx}:  {word}')
...     else:
...         print(f'{idx}: {word}')
... 
0:  There's
1:  more
2:  people
3:  than
4:  ever
5:  who
6:  don't
7:  have
8:  any
9:  love
10: for
11: the
12: way
13: things
14: are.
>>> 

If you need to iterate through two sequences in tandem, try using zip():

>>> names = ('hubert', 'natalie')
>>> surnames = ('espinoza', 'redwater')
>>> for name, surname in zip(names, surnames):
...     print(f'{name} {surname}'.title())
... 
Hubert Espinoza
Natalie Redwater
>>> 

Often, you may want to perform some kind of transformation or filtering on a collection of objects. List comprehensions are an effective way of accomplishing this:

>>> from pprint import pprint
>>> mala = (
...     'Controlling yourself is overrated. '
...     "There's plenty to be said for letting go."
... ).split()
>>> pprint(mala)
['Controlling',
 'yourself',
 'is',
 'overrated.',
 "There's",
 'plenty',
 'to',
 'be',
 'said',
 'for',
 'letting',
 'go.']
>>> for_the_win = [
...     word
...     for word in mala
...     if word in ('Controlling', 'overrated.', 'letting', 'go.')
... ]
>>> for_the_win
['Controlling', 'overrated.', 'letting', 'go.']
>>> 

If you need to deduplicate a collection, or perform mathematical operations like union, intersection, difference, and symmetric difference, try using a set:

>>> authors = ('George Orwell', 'Aldous Huxley', 'Aldous Huxley', 'George Orwell',
...            'Cory Doctorow', 'Aldous Huxley')
>>> set(authors)
{'Cory Doctorow', 'George Orwell', 'Aldous Huxley'}
>>> 

For creating mappings, Python offers dictionaries:

>>> from pprint import pprint
>>> the_left_hand_of_darkness = {
...     'karhide': (
...         "Under that nation's politics and parades and passions runs an "
...         'old darkness, passive, anarchic, silent, the fecund darkness '
...         'of the Handdara.'
...     ),
...     'faxe': (
...         "There's really only one question that can be answered, Genry, "
...         'and we already know the answer....'
...     )
... }
>>> pprint(the_left_hand_of_darkness)
{'faxe': "There's really only one question that can be answered, Genry, and we "
         'already know the answer....',
 'karhide': "Under that nation's politics and parades and passions runs an old "
            'darkness, passive, anarchic, silent, the fecund darkness of the '
            'Handdara.'}
>>> the_left_hand_of_darkness.keys()
dict_keys(['karhide', 'faxe'])
>>> the_left_hand_of_darkness.values()
dict_values(["Under that nation's politics and parades and passions runs an old darkness, passive, anarchic, silent, the fecund darkness of the Handdara.", "There's really only one question that can be answered, Genry, and we already know the answer...."])
>>> for key, value in the_left_hand_of_darkness.items():
...     print(f'{key.title()}: {value}')
... 
Karhide: Under that nation's politics and parades and passions runs an old darkness, passive, anarchic, silent, the fecund darkness of the Handdara.
Faxe: There's really only one question that can be answered, Genry, and we already know the answer....
>>> 

The Standard Library

So far, we have been mostly using plain Python to explore the language. However, one benefit of Python is its Standard Library.

The Python Standard Library is a set of modules included with every Python installation. If there is a common task you want to accomplish, the odds are that there is a module in the Standard Library that can help you.

For example, the sys module provides access to some variables used or maintained by the interpreter, and to functions that strongly interact with the interpreter:

>>> import sys
>>> from pprint import pprint  # Enable pretty printing of objects
>>> sys.argv  # Return list of command line arguments passed to Python script
['-c']
>>> pprint(sys.path)  # Pretty print module search path
['',
 '/usr/lib/python311.zip',
 '/usr/lib/python3.11',
 '/usr/lib/python3.11/lib-dynload',
 '/usr/local/lib/python3.11/dist-packages',
 '/usr/lib/python3/dist-packages',
 '/usr/lib/python3.11/dist-packages']
>>> sys.getdefaultencoding()  # Return system default text encoding
'utf-8'
>>> # Return named tuple of five components of the Python version number
>>> sys.version_info
sys.version_info(major=3, minor=11, micro=2, releaselevel='final', serial=0)
>>> 

platform lets you access your underlying platform's identifying data:

>>> import platform
>>> platform.system()  # Return system/OS name
'Linux'
>>> platform.python_version()  # Return Python version as a string
'3.11.2'
>>> 

os lets you interact with operating systems in a portable way:

>>> import os
>>> os.path.expanduser('~')  # Obtain user home directory
'/home/essun'
>>> os.getcwd()  # Get current working directory
'/home/essun/books'
>>> os.listdir()  # Return list of directory entries
['the_fifth_season.md', 'the_obelisk_gate.md', 'the_stone_sky.md']
>>> 

shutil provides a high-level interface for file and directory management tasks:

>>> import shutil
>>> # Copy file, preserving metadata
>>> shutil.copy2('the_fifth_season.md', '/home/essun/complete/')
'/home/essun/complete/the_fifth_season.md'
>>> # Rename file
>>> shutil.move('the_fifth_season.md', 'the_fifth_season_final.md')
'the_fifth_season_final.md'
>>> # Move file
>>> shutil.move('the_obelisk_gate.md', '/home/nassun/')
'/home/nassun/the_obelisk_gate.md'
>>> # Move directory
>>> shutil.move('/home/essun/drafts/', '/home/hoa/')
'/home/hoa/drafts'
>>> 

datetime provides classes for manipulating dates and times:

>>> import datetime as dt
>>> dt.date.today()
datetime.date(2024, 3, 17)
>>> dt.datetime.now()
datetime.datetime(2024, 3, 17, 16, 43, 49, 998823)
>>> ukl_bday = dt.date(1929, 10, 21)
>>> ukl_bday
datetime.date(1929, 10, 21)
>>> ukl_bday.month, ukl_bday.day, ukl_bday.year
(10, 21, 1929)
>>> ukl_bday.strftime('%m/%d/%Y')
'10/21/1929'
>>> diff = dt.date.today() - ukl_bday
>>> type(diff)
<class 'datetime.timedelta'>
>>> f'{diff.days:,}'
'34,488'
>>> dt.datetime.strptime('01/22/2018', '%m/%d/%Y')
datetime.datetime(2018, 1, 22, 0, 0)
>>> 

urllib is a package that collects several modules for working with URLs:

>>> from urllib.request import urlopen
>>> from textwrap import fill  # Enable wrapping single paragraph
>>> url = (
...     'https://theanarchistlibrary.org/library/'
...     'shevek-you-can-only-be-the-revolution'
... )
>>> # Open URL and read its contents
>>> with urlopen(url) as rsp:
...     content = rsp.read()
... 
>>> print(fill(content[27750:28401].decode()))
It is our suffering that brings us together. It is not love. Love does
not obey the mind, and turns to hate when forced. The bond that binds
us is beyond choice. We are brothers. We are brothers in what we
share. In pain, which each of us must suffer alone, in hunger, in
poverty, in hope, we know our brotherhood. We know it, because we have
had to learn it. We know that there is no help for us but from one
another, that no hand will save us if we do not reach out our hand.
And the hand that you reach out is empty, as mine is. You have
nothing. You possess nothing. You own nothing. You are free. All you
have is what you are, and what you give.
>>> from urllib.request import urlretrieve
>>> url = 'https://en.wikipedia.org/wiki/The_Dispossessed'
>>> f_path = 'the_dispossessed.html'
>>> # Copy network object to local file
>>> f_path, headers = urlretrieve(url, f_path)
>>> f_path, headers
('the_dispossessed.html', <http.client.HTTPMessage object at 0x7f34378cdf10>)
>>> for name, value in headers.items():
...     print(f'{name}: {value}')
... 
date: Sun, 24 Mar 2024 17:04:25 GMT
server: mw1487.eqiad.wmnet
x-content-type-options: nosniff
...
accept-ranges: bytes
content-length: 237497
connection: close
>>> 

time provides various time-related functions:

>>> from time import sleep
>>> def ka() -> None:
...     '''Print message about the nature of ka.'''
...     print('Ka was like a wheel, its one purpose to turn...')
...     sleep(5)  # Pause for 5 seconds
...     print(
...         '...and in the end it always came back to the place '
...         'where it had started.'
...     )
... 
>>> ka()
Ka was like a wheel, its one purpose to turn...
...and in the end it always came back to the place where it had started.
>>> 

timeit provides a simple way to time small bits of Python code:

>>> from timeit import Timer
>>> # Time executions of the main statement
>>> Timer('stephen_king = s1 + s2', "s1, s2 = 'And each time you forget the last time. ', 'For you, each time is the first time.'").timeit()
0.09024540099926526
>>> Timer("stephen_king = f'{s1}{s2}'", "s1, s2 = 'And each time you forget the last time. ', 'For you, each time is the first time.'").timeit()
0.06281828700048209
>>> 

PYTHONSTARTUP

As we have seen, the Standard Library has many helpful modules. Over time, you may find yourself repeatedly using the same modules, and continually importing them may become nettlesome.

One way to address this issue is to take advantage of PYTHONSTARTUP. PYTHONSTARTUP is an environment variable that can contain the path of a .py file. In this file, you can place Python code that you would like executed before the first prompt is displayed in interactive mode.

This file can be used to create custom objects or import modules that you would like available in the Python interpreter's namespace upon startup. Here is an example file for PYTHONSTARTUP, named pythonstartup.py and saved in ${HOME}/Scripts/:

import datetime as dt  # Enable classes for manipulating dates and times
# Enable a portable way of using operating system dependent functionality
import os
# Enable returning Python version and system/OS name
from platform import python_version, system
# Enable printing the formatted representation of object on stream
from pprint import pprint
# Enable a number of high-level operations on files and collections of files
import shutil
# Enable access to some variables used/maintained by the interpreter and to
# functions that strongly interact with the interpreter
import sys
import time  # Enable various time-related functions
# Enable suspending execution of the calling thread
# for the given number of seconds
from time import sleep
# Enable a simple way to time small bits of Python code
import timeit
from urllib.request import urlopen  # Enable opening of URLs


#############
# Functions #
#############
def clr() -> None:
    '''Clear the screen.'''
    WIN = (system() == 'Windows')  # Determine if running Windows
    if WIN:
        os.system('cls')
    else:
        os.system('clear')

In order to make sure that PYTHONSTARTUP is properly set and available upon logging into our GNU/Linux distribution, we add the following line to either ${HOME}/.profile (Debian) or ${HOME}/.bash_profile (Fedora):

# Set Python startup file location
export PYTHONSTARTUP="${HOME}/Scripts/pythonstartup.py"

Wrapping Things Up

These examples may have given you new ideas on how Python can be used to accomplish your goals.

For a more comprehensive introduction to the Python language, check out python.org's excellent Python Tutorial. For a wide variety of introductory and in-depth material on Python and its many libraries, check out the exemplary content at Real Python.

To stay in touch with the latest happenings in the Python community, try subscribing to the Planet Python RSS feed, or one of the many great Python podcasts.

If you can, attend a PyCon. They can be wonderful opportunities to connect with other Pythonistas, and share knowledge of and passion for Python.

Documentation

The following resources may be beneficial in further exploring the Python language and ecosystem:

Enjoyed this post?

Subscribe to the feed for the latest updates.