A time to learn Python
I’ve programmed in more than twenty programming languages over the past four decades. I’ve coded in Java for 20+ years, and am the creator of a Java collections framework called Eclipse Collections. I have never tried to learn Python before. That is, until now.
Why learn Python?
I am a Java Champion, and regularly teach and advocate for the Java programming language. My learning Python might seem a bit odd to some. In fact, some of my friends started pinging me on Twitter after I posted several tweets about me solving the Pet Kata using Python wondering if I was having a mid-life crisis or if I had been abduckted. I tried to alleviate their concerns with a tweet.
Python is an older programming language than Java, which might surprise some folks. I often recommend to developers to look at older languages to learn new techniques and approaches. I try to practice what I preach.
I recently shared my experience returning to my nostalgic Smalltalk days and implementing the Deck of Cards Kata using the Pharo Smalltalk IDE. Programming in Smalltalk using unit tests was a tremendous amount of fun. You can see the results in the following blog.
A little Smalltalk for the soul
Smalltalk helped me become the software engineer that I am today.
I thought that I might as well make the most of my recent visit to a dynamic language I knew well and see how quickly I would be able to code productively in Python. Just as I did with Smalltalk, I chose a code kata to implement. This time I went with the Pet Kata. I’ve often told folks they can use the Eclipse Collections Katas to learn any programming language, as long as there is a way to specify unit tests and implement a domain in the language.
I made the job of learning Python a lot easier by using the quite capable PyCharm IDE from JetBrains. PyCharm felt very familiar to me, because I have programmed in the IntelliJ IDE from JetBrains for almost twenty years.
To start things off, I implemented the domain classes. I knew Python supported object-oriented programming, so I created three classes to represent
Person has zero or more
Pets and each
Pet has a
PetType, which I implemented via an
Enum, which is a type both Java and Python support.
This is how I implemented
This is how I implemented the
This is how I implemented the
You can compare the solutions for the domain classes in Python, to the solutions in Java using Eclipse Collections here.
What I learned in the domain classes?
Space matters in Python. So do spaces. There are no curly braces like in Java, and Python is less forgiving than Smalltalk in terms of how code is formatted and edited.
You don’t define instance variables as you would in Java or Smalltalk, with a special declaration area. The variables come into existence as you define them in the init method. Since Python is dynamic, there is no type to specify.
self keyword is the same name Smalltalk uses for the instance, and is the equivalent of
this in Java.
 is a literal form of an empty list. You can add things to a list using the
You define a function using
def, followed by the function name, and a parameter list in parentheses and then ending the definition line with a
:. When you define an instance method, the first parameter in every method needs to be
To implement an
Enum, you need to import the
Enum type from the
enum module or package (not too sure on the terminology here yet). You then have your type extend the
Enum type, by putting the
Enum type inside the parentheses after the class name, which is
PetType in this case.
lambda keyword is used to create a
lambda, or anonymous function. The parameters are specified after the keyword, and separated from the expression with a
:. I used lambdas with both
Functions I used in the domain were
map. This is one place where I think Python is missing some object-oriented goodness of encapsulated methods. I think
map should be methods on the list type, not functions that accept an iterable type. Making them methods instead of functions would make them easier to discover. The IDE can help you with code assist if you type
. after a type and show you a method list of available methods. This made it harder to discover these functions without using Google with questions like “How do I find the size of a list in python?”
The Tests in the Kata
There are five separate exercises in the Pet Kata. I decided I would only implement the first four exercises, because that would be enough to get started and develop a feel for what it is like to program in Python.
I had to learn how to develop a unit test in Python, so I started Googling for the info. What I discovered is that unit testing in Python is somewhat similar to the unit testing I was familiar with in JUnit 3.x versions. Each test class has to extend
unittest.TestCase, and each test method must be prefixed with the word
The assertions are available directly on the instance of the class using the
self keyword. So to assert equality, you would use the method
Before I started writing the tests, I created the test data I used in the tests. In Java, I put this code in an abstract class, but I had trouble figuring out how to define an instance variable in a parent class and then extending the class and accessing the variable. So instead I decided to just create a function called
get_people() which I used in each test case to get the list of people with their pets.
You can compare the solutions for the test data setup in Python, to the solutions in Java using Eclipse Collections here.
Exercise 1 Test
The tests for exercise 1 in the Pet Kata are fairly straight forward. You need to learn how to transform and filter a list in this exercise.
In this test class, I learned how to use the functions
filter return iterators which then need to be converted to lists using the list function.
You can compare the solutions for exercise 1 in Python, to the solutions in Java using Eclipse Collections here.
Exercise 2 Test
Most of the tests for exercise 2 in the Pet Kata are also fairly straightforward.
In this test class, I learned how to use the
sum methods in addition to what I learned in exercise 1. I was surprised there was no
count function, but I was able to find how to use
filter to accomplish what was needed.
The one test that was not straightforward was the one where I needed to use
flatMap. Python does not have a function called
flatMap, and instead you need to use a function named
chain in a library called itertools along with
You can compare the solutions for exercise 2 in Python, to the solutions in Java using Eclipse Collections here.
Exercise 3 Test
The tests in exercise 3 are much harder to complete. This is because they do more complex things like counting and grouping things by other things.
I had to use both the
collections.Counter class and
itertools.chain function in order to implement the method known in Eclipse Collections as
countByEach. The method
countByEach is a
flatCollect followed by a
countBy. In Eclipse Collections,
countBy will return a
Bag type. I learned that the equivalent type in Python is called
There is a
groupBy function in
itertools, but it didn’t quite behave as I had hoped. I could not find a simple way to transform the result from
groupBy to a dictionary, so I followed the recommended imperative way of using a for loop and converting each group value to a list using the
list function. It was interesting to see how to iterate over
group together in the for loop.
Finally, I had to switch to completely imperative approach with nested loops to convert the list of people into a dictionary of pet types mapped to sets of people.
You can compare the solutions for exercise 3 in Python, to the solutions in Java using Eclipse Collections here.
Exercise 4 Test
After exercises 2 and 3, I was was worried exercise 4 was going to be even more difficult. It turned out to not be so hard.
I couldn’t find a way to calculate
mean in Python using a single iteration like we do in Java using
IntSummaryStatistics, but there are functions for
mean (you need to import the statistics module for
mean). I was able to use
all again, but I could not find an equivalent of none, so used
For the remaining tests I learned how to use the
join method on a string along with
map to get a string representation of the names of Bob’s Smiths pets. I then used
Counter along with a method named
most_common to find the top three pets. Finally, I used the
statistics.median function to calculate the
median age of the pets.
You can compare the solutions for exercise 4 in Python, to the solutions in Java using Eclipse Collections here.
I learned a little bit about Python in this exercise. It was interesting to see how quickly I could get a bunch of tests written in Python up and running. Where I slowed down a bit was in learning whether I needed a function or a method to accomplish something. It was also challenging to understand what the type of something was so I could explore its API in more detail. I was able to leverage the debugger in PyCharm a bit, and it helped in some cases.
I was comparing and contrasting the Python solutions I came up with along with Java solutions using Eclipse Collections on Twitter. What I discovered along the way was that there were APIs in Eclipse Collections that weren’t used in the solutions that actually made things more concise. I submitted two pull requests to the Eclipse Collections Kata as a result. The additional methods I used to refactor some of the solutions were
containsBy. These are more advanced “fused” APIs that have evolved over time. You can read more about the evolution of the
containsBy “fused” API in the following blog.
I hope you enjoyed this learning experiment implementing the Pet Kata in Python. I definitely feel like I have developed some basic understanding of the Python programming language as a result. And now that I have written this blog, I always have something I can refer back to.
I am a Project Lead and Committer for the Eclipse Collections OSS project at the Eclipse Foundation. Eclipse Collections is open for contributions. If you like the library, you can let us know by starring it on GitHub.