5 common beginner mistakes in Python
Tags: python, codequality, codenewbie
Published: 2019-07-23 09:18:02

Python, being a high-level and dynamic programming language, is [famously](https://xkcd.com/353/) easy to use. This has contributed to it being [wildly popular](https://qz.com/1408660/the-rise-of-python-as-seen-through-a-decade-of-stack-overflow/) 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:
```python
def request(self, method, **kwargs):
    # ...
    if method not in ("get", "post"):
        req.get_method = lambda: method.upper()
    # ...
```

The recommended way to write this is:
```python
def request(self, method, **kwargs):
    # ...
    if method not in ("get", "post"):
        req.get_method = method.upper
    # ...
```
[(example)](https://github.com/stripe/stripe-python/blob/625d3f6c012cd56d59acf82da5ca67d86a8453b3/stripe/http_client.py#L547-L548)

## 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:
```python
class SitesManager(object):
    def get_image_tracking_code(self):
        raise NotImplemented
```

Correct:
```python
class SitesManager(object):
    def get_image_tracking_code(self):
        raise NotImplementedError
```
[(examples)](https://github.com/search?l=Python&q=raise+NotImplemented+language%3APython&type=Code)

## 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 &mdash; a `list`, `dict`, etc., it will be modified for all future calls.

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

Correct:
```python
def add_item(new_item, items=None):
    if items is None:
        items = []
    items.append(new_item)
```
[(example)](https://github.com/keras-rl/keras-rl/blob/master/rl/agents/dqn.py#L167)

## 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 &mdash; potentially opening up a security vulnerability. It is recommended to use `assert` statements only in tests.

Incorrect:
```python
assert re.match(VALID_ADDRESS_REGEXP, email) is not None
```

Correct:
```python
if not re.match(VALID_ADDRESS_REGEXP, email):
    raise AssertionError
```
[(example)](https://github.com/syrusakbary/validate_email/blob/master/validate_email.py#L127)


## 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 &mdash; `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:

```python
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](https://stackoverflow.com/a/1549854/1088579).

_Originally published on [DeepSource Blog](https://deepsource.io/blog/python-common-mistakes/). Ready to free up your code base of these anti-patterns? DeepSource flags all of these, and many others. [Sign up today](https://deepsource.io/signup?ref=devto)._