DEV Community

Sanket Saurav for DeepSource

Posted on • Originally published at deepsource.io

5 common beginner mistakes in Python

Python, being a high-level and dynamic programming language, is famously easy to use. This has contributed to it being wildly popular and growing in recent years. The ease of use also comes with ease of misuse. We've put together a list of 5 common mistakes that beginners can do when writing code in Python.

1. Unnecessary lambda expression

Functions are first-class citizens in Python. This means you can assign it to some variable, pass it as an argument in another function invocation, and so on. This can be counter-intuitive for beginners, or developers coming from other programming languages.

A common example of this pattern is:

def request(self, method, **kwargs):
    # ...
    if method not in ("get", "post"):
        req.get_method = lambda: method.upper()
    # ...

The recommended way to write this is:

def request(self, method, **kwargs):
    # ...
    if method not in ("get", "post"):
        req.get_method = method.upper
    # ...

(example)

2. Raising NotImplemented

This is one of those examples where similar naming can confuse developers. NotImplementedError is an exception class that should be raised when derived classes are required to override a method. NotImplemented is a constant which is used to implement binary operators. When you raise NotImplemented, a TypeError will be raised.

Incorrect:

class SitesManager(object):
    def get_image_tracking_code(self):
        raise NotImplemented

Correct:

class SitesManager(object):
    def get_image_tracking_code(self):
        raise NotImplementedError

(examples)

3. Mutable default argument

Default arguments in Python are evaluated once when the function definition is executed. This means that the expression is evaluated only once when the function is defined and that the same value is used for each subsequent call. So if you are modifying the mutable default argument — a list, dict, etc., it will be modified for all future calls.

Incorrect:

def add_item(new_item, items=[]):
    items.append(new_item)

Correct:

def add_item(new_item, items=None):
    if items is None:
        items = []
    items.append(new_item)

(example)

4. Using assert statement as guarding condition

Since assert provides an easy way to check some condition and fail execution, it's very common for developers to use it to check validity. But when the Python interpreter is invoked with the -O (optimize) flag, the assert statements are removed from the bytecode. So, if assert statements are used for user-facing validation in production code, the block won't be executed at all — potentially opening up a security vulnerability. It is recommended to use assert statements only in tests.

Incorrect:

assert re.match(VALID_ADDRESS_REGEXP, email) is not None

Correct:

if not re.match(VALID_ADDRESS_REGEXP, email):
    raise AssertionError

(example)

5. Using isinstance in place of type

Either type or isinstance can be used to check the type of an object in Python. But there is an important difference — isinstance takes care of inheritance while resolving the type of object, while type does not. So sometimes using isinstance might not be what you want to use. Take a look at the following example:

def which_number_type(num):
    if isinstance(num, int):
        print('Integer')
    else:
        raise TypeError('Not an integer')

which_number(False)  # prints 'Integer', which is incorrect

Here, Since bool is a subclass of int in Python, isinstance(num, int) is also evaluated as True, which is not the expected behavior. In this particular case, using type is the correct way. Read more about the difference in the behavior of these two methods here.

Originally published on DeepSource Blog. Ready to free up your code base of these anti-patterns? DeepSource flags all of these, and many others. Sign up today.

Top comments (10)

Collapse
 
sobolevn profile image
Nikita Sobolev

Awesome! All of these rules are covered in github.com/wemake-services/wemake-...

By the way, we use and pass DeepSource checks in this repo.

Collapse
 
sanketsaurav profile image
Sanket Saurav

Thanks Nikita — that's good to know! Would love to hear more about your experience. Let me reach out to you on email. :)

Collapse
 
sobolevn profile image
Nikita Sobolev

Sure, write me straight away: mail@sobolevn.me

Thread Thread
 
sanketsaurav profile image
Sanket Saurav

Done!

Collapse
 
oscherler profile image
Olivier “Ölbaum” Scherler

So Python has buit-in assertions, but if you want to actually use assertions, you have to use an external library instead?

Collapse
 
sanketsaurav profile image
Sanket Saurav

You can use assertions with the assert statement and that is the preferred way. The pattern here says you should not use assertions for some condition validation in your application logic — only use assertions in tests.

I hope that clarifies the point! :)

Collapse
 
oscherler profile image
Olivier “Ölbaum” Scherler

Somewhat, yes. However testing a condition and throwing an exception if it’s not met is exactly the kind of tedious task assertions were made to simplify, make easier to write and easier to read. So if you can’t use them in actual production code, it kinda defeats the purpose. Especially in a dynamically typed language where you have to type-check all your arguments.

Collapse
 
ordonezf profile image
Fran

This is wrong

def add_item(items=None, new_item)

Arguments with default values go last.

Collapse
 
sanketsaurav profile image
Sanket Saurav

My bad! Fixed. Thanks for pointing out :)

Collapse
 
sandip_bandi profile image
sandeepbandi

Awesome. The tip about isinstance usage is very helpful..