Python/Django Developer Interview Questions and Answer (#1)
Tags: python, django, webdev, productivity
Published: 2021-05-03 10:34:37

Most of the software engineers are intimidated by technical rounds and I have recently started to give interviews. So, this time I am planning to write up all the questions I get asked in my technical rounds. Hope this will be helpful to some of you to get an idea on what type of questions are being asked. Use this series as a guide to prepare and ace that interview.

Some questions might be specific to Python and Django but most of them will be applicable to any software engineering role and web application developers.

This interview was for a Python developer role and they were looking for a candidate proficient in Django too, hence you will be seeing many questions related to Django architecture and design as well.

####1. Let’s work on a recursive implementation of Fibonacci series.
passed "n", return the fibonacci value at the nth position.

**Solution**: Fibonacci series are created in one of the two ways, iterative and recursive. Interviewer was looking for a recursive solution only.
```python
def fibonacci(n):
    if n == 0 or n == 1:
	  return n
    return fibonacci(n-1) + fibonacci(n-2) 
```
This solution worked fine for smaller numbers, 
n=10, took 0:00:00.001004sec
n=35, took 0:00:05.157989sec which is too long.
So next I was asked to optimize this.

Ideal Solution: Using Dynamic programming, I am storing each value found in a dictionary, so that the next it looks for fibonacci of that number it can just look-up from "value_dict"
```python
def fibonacci(n, value_dict):
    # the first two values of fibonacci is always 0,1
    if n == 0 or n == 1:
        return n
    # if fibonacci(n) is already calculated then return it from dict 
    if value_dict.get(n):
        return value_dict[n]
    #else store it in dict and return next time it is called for.
    else:
        value_dict[n] = fibonacci(n-2,value_dict) + fibonacci(n- 
        1,value_dict)
        return value_dict[n]

print(fibonacci(100, {}))
```

####2. What are decorators? Write a decorator that logs the functions arguments.

**Solution**:
Decorator is any function that takes in another function and adds functionality to it without manipulating the function itself. It does this by using a wrapper function.

```python
def decorator(func):
    def wrapper(*args):
        print("Logging the parameter of ", func, " is ", args)
        return func
    return wrapper

@decorator
def operation(x, y):
    return x+y

operation(5,20)
```
@decorator is just a syntactic sugar, it is equivalent to 
`res = decorator(operation(5,20))`

####3. What GIL (global interpreter lock)? How does it allow multi-threading in python?

**Solution**: GIL is a lock which makes sure python interpreter is held by only one thread at a time. That means only one thread can be in a execution status at any point of time. And this is why python is a single threaded programming language. This was introduced to solve the reference count problem which is used by python for garbage collection.

Then how is multi-threading module working in python, you may ask!
Well, this global interpreter lock applies only for CPU bound activities. So if there is any code which affects or uses CPU it automatically starts acting as a single threaded. But all the general programs can still work in a multi-threaded fashion.

####4. What is WSGI and UWSGI?

**Solution**:
Web server gateway interface, as the name suggests it is a interface between the application itself and web server. 
Django provides runserver for development server and for debugging, but for production we use WSGI in combination of any web servers like nginx, apache, or even uwsgi server.
wsgi and uwsgi are both protocols, and these work with web servers to serve good performance application in production.

So when client makes a request to django application, the request is read by nginx or any web server. Then nginx passes this request to the uwsgi service which intern passes it to concerned django app.

####5. Can we write a custom queryset in dango? How?

**Solution**:
Yes, we can create a custom queryset by customizing Managers. A Manager is the interface through which database query operations are provided to Django models. Each model contains atleast one Manager.
We can override Managers base queryset by override Manager.get_queryset() method.

```python
class Employees(models.Model):
      name = models.CharField(max_length=50)
      location = models.CharField(max_length=100)
```
On running, `Employees.objects.all()` it will return all the employees in database.
Now, if we want to customize this to return only the employees from Toronto location then below can be done using Manager.
```python
class TorontoEmployeesManager(models.Manager):
      def get_queryset(self):
          return super().get_queryset().filter(location="Toronto")

class Employees(models.Model):
      name = models.CharField(max_length=50)
      location = models.CharField(max_length=100)

      objects = models.Manager()
      toronto_objects = TorontoEmployeesManager()
```
Now, `Employees.toronto_objects.all()` will return only the employees from Toronto location.

####6. arr1 = list(range(10)); arr2 = arr1; arr3 = arr1[:]
   Is arr2 == arr3 ?

**Solution**:
No, `arr2 = arr1` creates a new reference of arr1 and assigns it to arr2. whereas, `arr3 = arr1[:]` copies the content of arr1 and assigns it to new variable called arr3.
```python
arr1 = [1,2,3,4]
arr2 = arr1
arr3 = arr1[:]
arr1.append(5)
print(arr2) # [1,2,3,4,5]
print(arr3) # [1,2,3,4]
```
####7. How to hide a class variable from getting accessed outside the class?
```python
class Sample:
  def __init__(self, a, b):
    self.a = a
    self.b = b
```
a and b should be hidden from outside of class.

**Solution:**
```python
class Sample:
  def __init__(self, a, b):
    self.__a = a # using double underscore hides class variable
    self.__b = b
```
####8. What are middlewares in Django? How do you create custom middleware.
**Answer:**
Middleware is a low-level plugin that can be used to hook into Django's request/response cycle. There are already middlewares available by default when a django project is created and it can found in settings.py.
We can create a custom middleware and include it in the list of middleware with the existing ones.

To create a custom middleware following structure needs to be followed:
```python
# Middleware class should always consist __init__() and __call__()
class MyCustomMiddleware():
      def __init__(self, get_response):
          self.get_response = get_response #get_response is the view which will be called after this middleware/ or its the next middleware in the list.
          
      def __call__(self, request):
          # write the code to be executed before calling view here
          
          response = self.get_response(request) #pass the request to view and get the response back, which will be returned.
     
          # write the code to be executed after calling view here.
          return response
```
Add this middleware into the "Middleware" section in settings.py. Also make sure the order of the middleware is correct. Because it is called from top to down while processing requests and then bottom to up while returning response.

Hope this was helpful!
 
If you want to support my work, https://www.buymeacoffee.com/manishanaidu