Python

Python Class Vs Instance Properties

Python Class Vs Instance Properties

Reading Time: 3 minutes

Python is not always friendly for developers that come from languages like Java or C++ or SWIFT. The next topic got me debugging my code for a few hours, so I hope to save them for you. I wanted to create a class which its instances would have a list in each of them, for the sake of simplicity we will add only numbers to this list. So this is what I wrote:

class MyClass:
	
	inner_list=[]
	
	def __init__(self):
		pass
		
	def add(self, some):
		self.inner_list.append(some)

Then when I wanted to test it, something strange happens to me, my tests failed and I got weired results, something messed up with 2 instances I created.

a = MyClass()
b = MyClass()

Lets add some values to them:

a.add(1)
b.add(2)

The expected print of them would be:

print(a.inner_list) # expected [1]
print(a.inner_list) # expected [2]

But what we get is:

print(a.inner_list) # [1, 2]
print(a.inner_list) # [1, 2]

So what happened here? The explanation is very simple, properties that initialized in class scope and not inside the constructor (__init__) method and on self, are class properties, just like Java’s static variables, and because list is a mutable object the list is not only shared but referenced to the same list and its values.

If we would have declared immutable property such as number, the case would be a little different. Consider the following:

class MyClass:
	
	number=0
	inner_list=[]
	
	def __init__(self):
		pass
		
	def add(self, some):
		self.inner_list.append(some)

a = MyClass()
b = MyClass()
print(a.number) # prints 0
print(b.number) # prints 0
a.number = 1
print(a.number) # prints 1
print(b.number) # prints 0

Because number property is immutable, when you assign a.number (in contrast to append) you basically override (replacing it) with a new value.

The rewritten class is:

class MyClass:	
	def __init__(self):
		self.inner_list = []
		self.number= 0
	
	def add(self, some):
		self.inner_list.append(some)

Cheers,

Returning Multiple Values In Python Is a Bad Practice

Returning Multiple Values In Python Is a Bad Practice

Reading Time: 4 minutes

Python is great. It has a lot of cool features and perks within the language itself and the standard library. But sometimes we overuse some of those features or maybe the right term is misuse. I’m here to take responsibility for some functions that are written in our products. Even though I wasn’t necessarily the developer who written them, I am the one to blame to make it hang in the code base for months or even years. I won’t provide the real code here because it is private, but let’s take a look at the following function (this is a pseudo code function that is pretty much a copy of some functions in our code base):

def get_user_and_token(http_request, ignore_logged_in=False, check_email_valid=False):
	if not ignore_logged_in:
		email = get_user_email()
		if not_blank(email.lower()):
			domain_string = get_domain_string(email)
			domain = Domain.get(domain_string)
			user = User.get(email)
			if domain and user:
				return (False, None, None)

		has_token, user = get_user(http_request)
		if has_token:
			return (False, None, None)

	if token_exists(http_request):
		token = get_token(http_request)
		token_to_check = get_token(user)
		if token = token_to_check:
			domain = Domain.get(user)
			if domain and check_email_valid:
				if user._email_valid:
					return (True, Domain, user)
				else:
					return (False, None, None)
			else:
				return (True, domain, user)

	return (False, None, None)

Please take a look at the returning values, can you tell by the implementation of the function what each parameter means? Don’t you see how can we use this function with all those (False, None, None)‘s? Me neither. The only place that gives me a hint is in the invocation of this function user_has_token, domain, user = get_user_and_token(http_request=http.request). Let’s leave the issues with the naming of the function which not exactly tells us what the function is going to do (function names should always tell exactly what they do – this is a MUST for clean code) and the horrible logic in the body of the function.

Either if the values returned are related to each other or not, it will be better to know what are we returning from the implementation of the function, and have better access to those values. That sounds like a return of some data structure is what we are looking for. Python provide the namedtuple factory function for those kind of situations. So the first step to get more readable code is to use it as here:

from collections import namedtuple

def get_user_and_token(http_request, ignore_logged_in=False, check_email_valid=False):
	UserMeta = namedtuple("UserMeta", ["user_has_token", "domain", "user"])
	if not ignore_logged_in:
		email = get_user_email()
		if not_blank(email.lower()):
			domain_string = get_domain_string(email)
			domain = Domain.get(domain_string)
			user = User.get(email)
			if domain and user:
				return UserMeta(False, None, None)

		has_token, user = get_user(http_request)
		if has_token:
			return UserMeta(False, None, None)

	if token_exists(http_request):
		token = get_token(http_request)
		token_to_check = get_token(user)
		if token = token_to_check:
			domain = Domain.get(user)
			if domain and check_email_valid:
				if user._email_valid:
					return UserMeta(True, Domain, user)
				else:
					return UserMeta(False, None, None)
			else:
				return UserMeta(True, domain, user)

	return UserMeta(False, None, None)

Now you know what type of values you are returning which makes it much more readable, and the access to the values is more verbose:

user_meta = get_user_and_token(http_request=http.request)
print(user_meta.user_has_token)
print(user_meta.domain)
print(user_meta.user)

Conclusion

Of course the function will be re-factored and partitioned into much more smaller functions to follow SRP (single responsibility principle). This is a small step toward cleaner code.

Cheers.

Create Python Development Environment Ready For TDD

Create Python Development Environment Ready For TDD

Reading Time: 11 minutes

In this post we will create a development environment for python applications which will support the TDD (Test Driven Development) paradigm. I won’t be telling you to stick to one or another way of programming here, this is your decision, but as a professional programmer you must try at least the TDD. This article is all about technical stuff to get things move and make an easy development environment for you.

The project

We will build a very simple application. Like in most unit tests tutorials we will create a calculator app. This will be our skeleton and from there you can take it to any application you want to create. The application itself is not the issue here, what’s important is the environment suite. We will set the project up for Python projects (with PyCharm over Windows 10 os, we will use Python 3.6) but of course there’s an alternative you can get for pretty much all languages and frameworks.

The steps we’ll take in order to complete our task are:

  1. Create the project
  2. Initialize virtual environment (with virtualenv)
  3. Set up dependencies
  4. Run test with pytest watch

Create the project

  1. Create a project with PyCharm (File -> New Project)
  2. Choose the directory where you want to initialize the project. Create a virtual environment (virtualenv) so that we can isolate our local project dependencies from the global ones.
    Create Project

Initialize virtual environment (with virtualenv)

  1. Install virtualenv (although should be ready for use with PyCharm)
  2. Open your terminal window and type venv\Scripts\activate (this will activate the virtual environment, making all dependencies installation be local to the project)
    Virtual Environment* you don’t have to type the .bat suffix

Setup Dependencies For TDD

  1. Here we will install our development dependencies, these are the tools we will use in order to make or coding faster and better.
  2. I like to create 2 files that will handle my dependencies, one is for the development dependencies (such as test runners, linters, etc) and one for production. The pip package manager (which we use in our project) uses the convention of managing the dependencies in a file called requirements.txt, so we’ll create requirements.txt and requirements-dev.txt to distinguish between dev and production.
  3. We will focus on the requirements-dev.txt in this article. We want to install a tool which will run our test continuously so we won’t need to run the tests manually after we finish coding, the test will run automatically for us as we code and with every files save (ctrl+s) the tests automatically will prompt the results on the terminal screen. The tools I would like to introduce you to run our unit tests are Pytest and Pytest-watch (those together run the tests for us, prompt the results in a readable way and make this process continuous). Please add those lines to requirements-dev.txt

    Those are the latest version as for the date this post is published, if you want to install the latest versions just remove the ==VERSION_NUMBER from each line.
  4. The next tool we want to use is a Linter. A linter is a tool that can help you to analyze you code (such as formatting) which eventually minimize the chance to get errors. For python projects, I think the most popular linter is pylint, but PyCharm’s inner pre-installed linter should cover most of the code analysis pylint does.
  5. The requirements-dev.txt file should look like this:
    Dependencies
  6. install the dependencies by running pip install -r requirements-dev.txt (or if you use python3 py -3 -m pip install -r requirements-dev.txt)

Run tests with pytest watch

Now is the interesting and productive part and surprisingly (or not) the easiest one. I won’t be talking about how to develop using TDD in this post, but lets try something very simple to demonstrate the power of TDD. I won’t be talking also about the unittest framework from the standard python lib. Just show how to get it work.

  1. Create a test directory and add a test file in there, for example test_math.py (this should be a convention, write your tests under test directory, and prefix the test files with test_ prefix)
  2. Open the terminal and type ptw, this will run you test with pytest and run it continuously with pytest-watch on every file save.
  3. Import unittest and add a test (a test is a class that starts with the prefix Test, so let’s call it TestMath and it should extend the unittest.TestCase class).
  4. Let’s add a test_math_add method to test an add method with some simple assertions, and import out not yet implemented math library.
  5. Add a main function (also unittest convention)
  6. Your code now should look like this:
    import unittest
    from lib import math
    
    
    class TestMath(unittest.TestCase):
    
        def test_math_add(self):
            self.assertEqual(10, math.add(6, 4))
            self.assertEqual(0, math.add(1, -1))
            self.assertEqual(0, math.add(-1, 1))
            self.assertEqual(-2, math.add(-1, -1))
    
    
    if __name__ == '__main__':
        unittest.main()
    
    
  7. now you should see our tests fail in the terminal and that’s fine because we did not yet implement our library.Tests Fail
  8. let’s add lib directory and math.py module with the following contents:
    def add(x, y):
        return x + y
    
  9. Tests should pass now. (Please make sure the test and lib directories both have __init__.py file so they could be used as modules)
    Tests Pass

 Conclusion

You can take it from here and use it as a skeleton for projects you want to implement by TDD. I didn’t get into the internals of TDD paradigm but there is so much information out there about it you can find easily. Try making you own small project and follow Uncle Bob’s 3 rules of TDD:

  1. You are not allowed to write any production code unless it is to make a failing unit test pass.
  2. You are not allowed to write any more of a unit test than is sufficient to fail; and compilation failures are failures.
  3. You are not allowed to write any more production code than is sufficient to pass the one failing unit test.

After few tries you get the trick and start developing clean[er] code programs.

Fell free to explore the GitHub repository of the project https://github.com/dimagimburg/python-continuous-tdd-skeleton

Cheers.