Intermediate- Python Developer Interview - part 2

Intermediate- Python Developer Interview - part 2

Dunder methods, PIP, Virtual Environments, comprehensions(list, dictionary)

Cover Photo by Amélie Mourichon on Unsplash

Dunder methods

artem-maltsev-3n7DdlkMfEg-unsplash.jpg Photo by Artem Maltsev on Unsplash

What are Dunder methods?

Dunder methods are also called as magic methods. These methods are called as Dunder because of below equation

Dunder = Double + Underscores

Dunder methods are surrounded by double underscores. These are special methods which python uses internally in various cases like operator overloading, inside builtin functions. Let me give you an example_

print("Hi"+" there")
print(3456+1234)
print(24.89+12.67)

#output
Hi there
4690
37.56

Here you can see the we are using '+' operator to add different diffrent data types, and it can automatically add those data types without need of specifying the particular type. This is possible because of special method _ add _

We can do manipulations using this operator overloading for user defined classes as well as shown in below example:

class Book:
    def __init__(self, name, author, price):
        self.name = name
        self.author = author
        self.price = price

    def __add__(self,other):
        return self.price+other.price

b1 = Book("book1", "author1", 450)
b2=Book("book2", "author2", 600)
print(b1+b2)

#output
1050

If you comment the _ add _ method in the code then you will get an error as shown below:

class Book:
    def __init__(self, name, author, price):
        self.name = name
        self.author = author
        self.price = price

    #def __add__(self,other):
        #return self.price+other.price

b1 = Book("book1", "author1", 450)
b2=Book("book2", "author2", 600)
print(b1+b2)

#output
Traceback (most recent call last):
  File "<string>", line 12, in <module>
TypeError: unsupported operand type(s) for +: 'Book' and 'Book'

This error occurs because by default type "Book" is not supported for operand '+'. Now you must have got an idea how dunder methods work. Some of the commonly used dunder methods are :

_ init _ : Invoked automatically when the object is instantiated

_ repr _ : Called by the repr() built-in function to compute the “official” string representation of an object.

_ str _ : Called by str(object) and the built-in functions format() and print() to compute the “informal” or nicely printable string representation of an object.

If you want more information related to special methods in Python then please official documentation

Virtual Environment

barbara-zandoval-w0lI3AkD14A-unsplash.jpg Photo by Barbara Zandoval on Unsplash

What is virtual environment?

Virtual environment is nothing but the isolated environment for particular project. This isolated python environment will contain all the dependencies of that particular project. The best feature of this is that we can create as many virtual environments as we want for different different projects, so that their dependencies won't conflict with each other. I will explain what is meant by conflict of dependencies in below question.

Why do we need virtual environment?

I will explain this case by taking an example: Suppose we have to work on two projects at a time that is Project1 and Project2 and both projects need one package but with different versions in such cases we won't be able install the package with different versions in same environment/directory, here virtual environment comes to rescue.

How to create virtual environment?

Python3 has built in module called venv to create the virtual environment, the command will be simple as shown below:

python3 -m venv env

This command will create virtual environment "env" in the current directory wherever you ran this command, now next step will be activating the virtual environment, that can be done as below:

source env/bin/activate

And you will see your virtual environment "env" activated as shown below:

01HW56780:Desktop gayatrikale$ source env/bin/activate
(env) 01HW56780:Desktop gayatrikale$

If you want more information related to virtual environment then please refer official documentation

PIP

petrebels-JwMGy1h-JsY-unsplash.jpg Photo by Petrebels on Unsplash

What is pip?

PIP is nothing but the Python package manager. Python has huge variety of builtin modules/packages .In addition to that the huge family of active contributors of Python has created a large set of custom packages which are listed at Python Package Index(PyPI). So to install these packages we can use pip.

How to use pip to install the particular package?

You need to simply use below command:

pip install <package name>

Some important pip commands

You can list down all the commands by using "pip help" on command line as shown below, you will see all the different pip commands

pip help

Usage:   
  pip <command> [options]

Commands:
  install                     Install packages.
  download                    Download packages.
  uninstall                   Uninstall packages.
  freeze                      Output installed packages in requirements format.
  list                        List installed packages.
  show                        Show information about installed packages.
  check                       Verify installed packages have compatible dependencies.
  config                      Manage local and global configuration.
  search                      Search PyPI for packages.
  cache                       Inspect and manage pip's wheel cache.
  wheel                       Build wheels from your requirements.
  hash                        Compute hashes of package archives.
  completion                  A helper command used for command completion.
  debug                       Show information useful for debugging.
  help                        Show help for commands.

General Options:
  -h, --help                  Show help.
  --isolated                  Run pip in an isolated mode, ignoring environment variables and user configuration.
  -v, --verbose               Give more output. Option is additive, and can be used up to 3 times.
  -V, --version               Show version and exit.
  -q, --quiet                 Give less output. Option is additive, and can be used up to 3 times (corresponding to
                              WARNING, ERROR, and CRITICAL logging levels).
  --log <path>                Path to a verbose appending log.
  --proxy <proxy>             Specify a proxy in the form [user:passwd@]proxy.server:port.
  --retries <retries>         Maximum number of retries each connection should attempt (default 5 times).
  --timeout <sec>             Set the socket timeout (default 15 seconds).
  --exists-action <action>    Default action when a path already exists: (s)witch, (i)gnore, (w)ipe, (b)ackup, (a)bort.
  --trusted-host <hostname>   Mark this host or host:port pair as trusted, even though it does not have valid or any
                              HTTPS.
  --cert <path>               Path to alternate CA bundle.
  --client-cert <path>        Path to SSL client certificate, a single file containing the private key and the
                              certificate in PEM format.
  --cache-dir <dir>           Store the cache data in <dir>.
  --no-cache-dir              Disable the cache.
  --disable-pip-version-check
                              Don't periodically check PyPI to determine whether a new version of pip is available for
                              download. Implied with --no-index.
  --no-color                  Suppress colored output
  --no-python-version-warning
                              Silence deprecation warnings for upcoming unsupported Pythons.

If you want more information related to pip then please refer official documentation

List Comprehension and Dictionary Comprehension

What is List Comprehension

List comprehension is the shorter way of writing/creating new lists from existing iterable item. Usually we use for loops to iterate over iterables but list comprehension gives us shorter way of achieving same thing.

Syntax of List comprehension

  • Simplest syntax:
    new_list = [expression for item in iterable]
    
    Example for this syntax:
numbers = [number for number in range(10) ]
print( numbers)

#output
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
  • Syntax with if condition:
    new_list = [expression for item in iterable if condition]
    
    Example for this syntax:
even_numbers = [number for number in range(10) if number%2==0]
print(even_numbers)

#output:
[0, 2, 4, 6, 8]

How we have written same example in for loop syntax:

even_numbers = []
for number in range(10):
    if number%2==0:
        even_numbers.append(number)
print(even_numbers)

#output:
[0, 2, 4, 6, 8]
  • Syntax with if-else condition:
new_list = [expression if condition else <expression> for item in iterable]

Example for this syntax:

factor_3 = [f"3 is factor of {n}" if n%3==0 else "3 is not factor of {n}" for n in range(30)]
print(factor_3)

#output
['3 is factor of 0', '3 is not factor of 1', '3 is not factor of 2', '3 is factor of 3', '3 is not factor of 4', '3 is not factor of 5', '3 is factor of 6', '3 is not factor of 7', '3 is not factor of 8', '3 is factor of 9', '3 is not factor of 10', '3 is not factor of 11', '3 is factor of 12', '3 is not factor of 13', '3 is not factor of 14', '3 is factor of 15', '3 is not factor of 16', '3 is not factor of 17', '3 is factor of 18', '3 is not factor of 19', '3 is not factor of 20', '3 is factor of 21', '3 is not factor of 22', '3 is not factor of 23', '3 is factor of 24', '3 is not factor of 25', '3 is not factor of 26', '3 is factor of 27', '3 is not factor of 28', '3 is not factor of 29']

How we can write same example in for loop syntax:

factor_3 = []
for n in range(30):
    if n%3==0:
        factor_3.append(f"3 is factor of {n}")
    else:
        factor_3.append(f"3 is not factor of {n}")
print(factor_3)

#output
['3 is factor of 0', '3 is not factor of 1', '3 is not factor of 2', '3 is factor of 3', '3 is not factor of 4', '3 is not factor of 5', '3 is factor of 6', '3 is not factor of 7', '3 is not factor of 8', '3 is factor of 9', '3 is not factor of 10', '3 is not factor of 11', '3 is factor of 12', '3 is not factor of 13', '3 is not factor of 14', '3 is factor of 15', '3 is not factor of 16', '3 is not factor of 17', '3 is factor of 18', '3 is not factor of 19', '3 is not factor of 20', '3 is factor of 21', '3 is not factor of 22', '3 is not factor of 23', '3 is factor of 24', '3 is not factor of 25', '3 is not factor of 26', '3 is factor of 27', '3 is not factor of 28', '3 is not factor of 29']

Why we use list comprehension?

  • List comprehension is more pythonic way of creating the list

  • It is faster compared to the loops, let's see the comparison for time consumed in both the cases.

#time required for 'for loop'
import time
start_time = time.time()
v = []
for i in range(50):
    v.append(2**i)
print(v)
print("--- %s seconds ---" % (time.time() - start_time))

#output
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 140737488355328, 281474976710656, 562949953421312]
--- 6.29425048828125e-05 seconds ---
#time consumed using list comprehension
import time
start_time = time.time()
V = [2**i for i in range(50)]
print(V)
print("--- %s seconds ---" % (time.time() - start_time))

#output
[1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536, 131072, 262144, 524288, 1048576, 2097152, 4194304, 8388608, 16777216, 33554432, 67108864, 134217728, 268435456, 536870912, 1073741824, 2147483648, 4294967296, 8589934592, 17179869184, 34359738368, 68719476736, 137438953472, 274877906944, 549755813888, 1099511627776, 2199023255552, 4398046511104, 8796093022208, 17592186044416, 35184372088832, 70368744177664, 140737488355328, 281474976710656, 562949953421312]
--- 5.817413330078125e-05 seconds ---

So from above examples we can clearly see the difference between the performance of both the codes.

Now let's see what is the Dictionary comprehension.

What is the Dictionary comprehension?

Dictionary comprehension is alternative way of creating the dictionary. We can use the dictionary comprehension in place of for loop.

Syntax of Dictionary comprehension

  • Simplest syntax:
new_dict = {key:value for (key,value) in dictonary.items()}
#or 
new_dict = {key:value for item in iterator}

Example using this syntax:

Suppose we want to create the dictionary of squares of keys

square_dict = {n:n**2 for n in range(10)}
print(square_dict)

#output
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}

How we have written same example in for loop syntax:

square_dict={}
for n in range(10):
    square_dict[n]=n**2
print(square_dict)

#output
{0: 0, 1: 1, 2: 4, 3: 9, 4: 16, 5: 25, 6: 36, 7: 49, 8: 64, 9: 81}
  • Syntax of using if condition in dictionary comprehension:
new_dict = {key:value for (key,value) in dictonary.items() if condition}
#or
new_dict = {key:value for item in iterator if condition}

Example using this syntax:

Suppose we want to create the dictionary of squares of keys which are odd

square_dict = {n:n**2 for n in range(10) if n%2!=0}
print(square_dict)

#output
{1: 1, 3: 9, 5: 25, 7: 49, 9: 81}

How we have written same example in for loop syntax:

square_dict={}
for n in range(10):
    if n%2!=0:
        square_dict[n]=n**2
print(square_dict)

#output
{1: 1, 3: 9, 5: 25, 7: 49, 9: 81}
  • Syntax of using if-else condition in dictionary comprehension:
new_dict = {key:(expression if condition else expression) for (key,value) in dictonary.items() }
#or
new_dict = {key:(expression if condition else expression) for item in iterable }

Example using this syntax:

Suppose we want o create the dictionary where we want to assign the value to the key as square of key itself if the key is even else the value will be "odd"

selective_square = {n:(n**2 if n%2==0 else "odd") for n in range(20)}
print(selective_square)

#output
{0: 0, 1: 'odd', 2: 4, 3: 'odd', 4: 16, 5: 'odd', 6: 36, 7: 'odd', 8: 64, 9: 'odd', 10: 100, 11: 'odd', 12: 144, 13: 'odd', 14: 196, 15: 'odd', 16: 256, 17: 'odd', 18: 324, 19: 'odd'}

How we have written same example in for loop syntax:

selective_square = {}
for n in range(20):
    if n%2==0:
        selective_square[n]=n**2
    else:
        selective_square[n]="odd"

print(selective_square)

#output
{0: 0, 1: 'odd', 2: 4, 3: 'odd', 4: 16, 5: 'odd', 6: 36, 7: 'odd', 8: 64, 9: 'odd', 10: 100, 11: 'odd', 12: 144, 13: 'odd', 14: 196, 15: 'odd', 16: 256, 17: 'odd', 18: 324, 19: 'odd'}

Why we use dictionary comprehension?

The reasons are same as explained for list comprehension.

  • This is more pythonic way of creating the new dictionary.
  • It reduces the extra lines which are required in for loop method
  • It is faster

Though comprehensions have their own perks we can't use it all the time. We can use the comprehensions, but it depends on our use case.

Summary

We have covered below important topics in this article:

  • Dunder methods also called as special methods/magic methods, used in builtin functions, operator overloading

  • Virtual Environment is used to avoid dependencies conflict between different projects

  • PIP : It is python package manager

  • List and Dictionary comprehension : This is shorter way of creating list/dictionary

I hope this helps you in revising the concepts, if you find this article useful then do share this article with your friends, family and colleagues.

Keep coding and all the best for the preparation!