Python logo

Python!

Introduction

I have been using Python for some 25 years, see one of my earliest blog posts regarding that: Python Rocks! Mostly, I have used Python for various scripts and throw-away type programs like analyzing logs, etc. But in my current project I had a chance to implement one application on top of the AWS infrastructure I implemented using AWS Serverless Application Model (SAM), you can read about that in my earlier post Using AWS Serverless Application Model (SAM) First Impressions.

I was pretty excited about this opportunity. Finally, after all these years, a chance to use Python in production! First, I decided to upgrade my Python knowledge. I read two books in my free time:

  • Dead Simple Python. This book provided an excellent and fast introduction to the modern Python 3 features.
  • Fluent Python. This book provided more insight why Python works as it works, and provided interesting pieces of information on various Python implementation details.

Using Python

Python is a really simple and easy to use language. But at the same time, Python is very impressive and provides good abstractions, e.g. list comprehensions and generator expressions. The more I read the books mentioned above and I wrote the Python application, the more I fell in love with Python. I have written a lot more Clojure than Python, and there are just two major features that I feel lack in Python: immutable data structures and a good Python REPL. Immutable data structures are a design decision in Clojure, and therefore using Clojure you don’t have to worry about many of those awkward warnings that e.g. the above-mentioned books warn about. A classic example is this:

list1 = [1, 2]

def buggy_add_to_list(l,n):
    l.append(n)
    return l

list2 = buggy_add_to_list(list1, 3)

print(f"list1: {list1}")
print(f"list2: {list2}")
print("list1 is list2: ", list1 is list2)

All pythonists see immediately that the output is:

list1: [1, 2, 3]
list2: [1, 2, 3]
list1 is list2:  True

Since lists in Python are mutable data structures, the function add_to_list mutates the list1 and the assignment list2 = add_to_list(list1, 3) aliases the list2 to the same list that is also assigned to list1.

One way to fix this is to append an item to the list, hence creating a new list:

list1 = [1, 2]

def fixed_add_to_list(l, n):
    return l + [n]

list2 = fixed_add_to_list(list1, 3)

print(f"list1: {list1}")
print(f"list2: {list2}")
print("list1 is list2: ", list1 is list2)

… and the output is of course:

list1: [1, 2]
list2: [1, 2, 3]
list1 is list2:  False

It is quite nice to program with Clojure since you don’t have to worry about these things with immutable data structures. But once you remember that all arguments are passed to functions by assignment, you are good to go with Python.

Another thing that I really miss with Clojure is a good REPL. Using Clojure, the REPL provides a unique workflow. But the reality is that you just cannot have a decent REPL without a homoiconic language, like the Lisp family languages are.

Tooling

Python provides nice tooling. The package manager pip is easy to use, and there are a plethora of Python libraries that you can use. By the way, there are nowadays a couple of other alternatives: poetry, and uv.

For linting there are good tools: flake8, and pylint. You can configure these tools as a pre-commit hook, so you make sure you don’t commit code that violates project linting rules. I use nowadays VSCode for all programming, and VSCode provides these linting tools as extensions.

Writing tests is really easy using pytest. I have used moto for mocking AWS services, more about that library in my next blog post.

Types

Python is a dynamic language, but you can use so called type hints to provide information regarding e.g., function parameter types and return types. Mostly, providing type hints is straight-forward. But when using some libraries that provide dynamically created objects, providing type information takes a bit of research.

Python and Cloud

We use AWS in our project. There is an excellent library for manipulating AWS resources using Python: boto3. I just realized a few days ago, that using the moto testing library to setup a fixture that is a one-to-one copy of your production AWS infrastructure, you can quite nicely develop with the boto3 library your application that interacts with AWS. You kind of have the AWS resources as virtual entities in the RAM of your workstation. True test-driven-development!

Next Chapter

I plan to write more about Python unit testing with the pytest, and specifically mocking AWS resources with the moto library. So, stay tuned!

Conclusions

Python is definitely my recommendation if you want to learn an easy to use general-purpose language that also provides good job opportunities. Python is also nowadays the #1 in the TIOBE list of the most popular programming languages.

The writer is working at a major international IT corporation building cloud infrastructures and implementing applications on top of those infrastructures.

Kari Marttila

Kari Marttila’s Home Page in LinkedIn: https://www.linkedin.com/in/karimarttila/