Practical Way to Get Started With Python
Tags: python, programming, beginners
Published: 2021-03-13 10:51:36

No matter the what the use case is, Python can always be utilized to solve the problem statement. Maybe, not in the most efficient way but it does solve your problem. And the best part is, it's one of the easiest Programming language to learn in today's date. And here's how you can get started with it in the most practical way possible.

One can skim through the internet trying to find ways to learn Python and most of the articles/tutorial/courses you find will tell you how to use Python's constructs, conditional statements, iterations, OOP concepts, a few libraries like NumPy, Pandas etc. and that's a really good starting point. But the question always lies, WHAT NEXT?

This article talks about what to do next to implement the basics of Python and getting comfortable with the language.

**Start with a small project to read a file from filesystem and log all the events happening inside the code.**

All those who want to explore on their own may now close this tab and move ahead with googling "How to read a file in Python?" or "How to log events in Python?". Others who would like a guiding hand, may follow along this article.

1. Reading files in Python.

We'll start by reading a simple text file.
For starters create a text file manually and paste loren ipsum statement into it.

Now to read this file, we need 2 objects:
1. Path to this file
2. IO object to perform read operation.

From a bunch of filesystem libraries, I find <a href="https://docs.python.org/3/library/pathlib.html">`pathlib`</a> the most intuitive. You might like to explore more on similar libraries as well.

```
from pathlib import Path

file_path = Path.cwd() / 'loren_ipsum.txt'

with open(file_path, 'r') as text_file:
    file_data = text_file.read()

print(file_data)
```
Let's see what's happening here.

We import Path module from pathlib package. `Path.cwd()`'s `__str__()` implementation returns the string representation of pathlib object for _current working directory_. `/ 'loren_ipsum.txt'` uses the operator overloading concept to append the text file name in the path.

Using the context manager `with` we then `open()` the text file as `text_file` object in reading `('r')` mode. And then we print the data. Pretty easy stuff, right?

> Obtaining resources using `with` is generally a good idea because then you won't have to worry about releasing those resources.

Now we move to the logging part.

2. Logging in Python

It's quite easy to implement basic logging functionality in Python. All you need to do is import <a href="https://docs.python.org/3/library/logging.html">`logging`</a> package, set logging configurations using `logging.basicConfig()` method and start logging events using `logging.info('Event Here')/logging.warning('Warning Here')` etc. methods.

Here's how it looks like:

```
import logging

logging.basicConfig(filename="applog.log", datefmt="%d-%m-%Y %H:%M:%S", level=logging.DEBUG, format="%(asctime)s - %(levelname)s %(message)s")

logging.debug("This is a DEBUG MESSAGE")
logging.info("This is a INFO MESSAGE")
logging.warning("This is a WARNING MESSAGE")
logging.critical("This is a CRITICAL MESSAGE")
```

We'll take a advanced step and configure logging in detail to understand underlying concepts.

We'll start with importing logging package as usual

```
import logging
```

Logging package basically requires 4 objects to work.

* Logger Object: Logger is never instantiated directly but through a module-level function called `getLogger(logger_name)`. It exposes interfaces which application code can directly consume. If no logger is defined, the default logger, Root Logger, will be used.

```
logger = logging.getLogger("newLogger")
```

* Handlers: Handlers is also never instantiated directly. There are mostly 2 kinds of handlers used, one being the `StreamHandler` which outputs logs directly into the console window and the other being `FileHandler` which outputs the logs in a file

```
console_handler = logger.StreamHandler()
```

* Log Level: Logging in general, concerns with 5 kinds of message namely, DEBUG, INFO, ERROR, WARNING and CRITICAL.

Both Logger and Handler requires log level to be specified to them.
Level for Logger determines the type of logs to be collected by the logger. Level for Handler determines the type of logs to be displayed by the handler.

Example:
If I set logger level to `ERROR`, then logger will only collect `ERROR`, `WARNING` and `CRITICAL` logs but not `DEBUG` and `INFO` logs. And then if I set handler level to `WARNING` then handler will be display `WARNING` and `CRITICAL` logs and `ERROR` logs will be ignored.

```
logger.setLevel(logging.DEBUG)
console_handler.setLevel(logging.DEBUG)
```

* Formatter: Formatter object takes in, what will be format of logs being displayed on console or written into the log file, as an argument.

One of the most generic format of a log would be:

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

Now all we need to attach this formatter to the handler and then attach the handler to the logger

```
console_handler.setFormatter(formatter)
logger.addHandler(console_handler)
```

Here we are done with setting up the logging in our application.

Now we can put all the code together which would look something like this:

```
from pathlib import Path
import pretty_errors
import logging

app_name = "Logging APP"

logger = logging.getLogger(app_name)
logger.setLevel(logging.DEBUG)

console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

console_handler.setFormatter(formatter)
logger.addHandler(console_handler)

try:
    logger.info("Locating File...")
    file_path = Path.cwd() / 'loren_ipsum.txt'
except FileNotFoundError as e:
    logger.error(f"Locating File Unsuccessful: Error:: {e}")
    raise e

logger.info("File Located!")
logger.info("Opening File")
with open(file_path, 'r') as text_file:
    logger.info("Reading File")
    file_data = text_file.read()
    logger.info("Content of the File is read")
logger.info("File Closed!")
logger.info("Execution Complete")

print(file_data)
```

Once you run the program, you'll get this output on your screen:

```
2021-03-13 16:01:04,442 - Logging APP - INFO - This is a info message
2021-03-13 16:01:04,443 - Logging APP - INFO - Locating File...
2021-03-13 16:01:04,443 - Logging APP - INFO - File Located!
2021-03-13 16:01:04,443 - Logging APP - INFO - Opening File
2021-03-13 16:01:04,444 - Logging APP - INFO - Reading File
2021-03-13 16:01:04,444 - Logging APP - INFO - Content of the File is read
2021-03-13 16:01:04,444 - Logging APP - INFO - File Closed!
2021-03-13 16:01:04,445 - Logging APP - INFO - Execution Complete
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
```

Once you have understood how to write this code, you can go ahead and read more about the objects of <a href="https://docs.python.org/3/library/logging.html">`logging`</a> library in depth.


Reading configuration files and logging events are two major components of every application you develop. This mechanism is omnipresent. Once you are comfortable with reading files and logging, consider yourself a notch better than yesterday at any enterprise.