What are nolocal
and global
keywords used for?
These two keywords are used to change the scope of a previously declared variable. nolocal
is often used when you need to access a variable in a nested function:
def func1():
x = 5
def func2():
nolocal x
print(x)
func2()
global
is a more straightforward instruction. It makes a previously declared variable global. For example, consider this code:
x = 5
def func1():
print(x)
func1()
> 5
Since x
is declared before the function call, func1
can access it. However, if you try to change it:
x = 5
def func2():
x += 3
func2()
> UnboundLocalError: local variable 'c' referenced before assignment
To make it work, we need to indicate that by x
we mean the global variable x
:
x = 5
def func2():
global x
x += 3
func2()
What is the difference between classmethod
and staticmethod
?
Both of them define a class method that can be called without instantiating an object of the class. The only difference is in their signature:
class A:
@staticmethod
def func1():
pass
@classmethod
def func2(cls):
pass
As you can see, the classmethod
accepts an implicit argument cls
, which will be set to the class A
itself. One common use case for classmethod
is creating alternative inheritable constructors.
What is GIL and what are some of the ways to get around it?
GIL stands for the Global Interpreter Lock and it is a mechanism Python is using for concurrency. It is built deep into the Python system and it is not possible at the moment to get rid of it. The major downside of GIL is that it makes threading not truly concurrent. It locks the interpreter, and even though it looks like you are working with threads, they are not executed at the same time, resulting in performance losses. Here are some ways of getting around it:
multiprocessing
module. It lets you spawn new Python processes and manage them the same way you would manage threadsasyncio
module. It effectively enables asyncronous programming and adds theasync/await
syntax. While it does not solve the GIL problem, it will make the code way more readable and clearer.- Stackless Python. This is a fork of Python without GIL. It’s most notable use is as a backend for the EVE Online game.
What are metaclasses and when are they used?
Metaclasses are classes for classes. A metaclass can specify certain behavior that is common for many classes for cases when inheritance will be too messy. One common metaclass is ABCMeta
, which is used to create abstract classes.
Metaclasses and metaprogramming in Python are huge topics and feel free to read this if you are interested in it.
What are type annotations? What are generic type annotations?
While Python is a dynamically typed language, there is a way to annotate types for clarity purposes. These are the built-in types:
int
float
bool
str
bytes
Complex types are available from the typing
module:
List
Set
Dict
Tuple
Optional
- etc.
Here is how you would define a function with type annotations:
def func1(x: int, y: str) -> bool:
return False
Generic type annotations are annotations that take another type as a parameter, allowing you to specify complex logic:
List[int]
Optional[List[int]]
Tuple[bool]
- etc.
Note that these are only used for warnings and static type checking. You will not be guaranteed these types at runtime.
What are generator functions? Write your own version of range
Generator functions are functions that can suspend their execution after returning a value, in order to resume it at some later time and return another value. This is made possible by the yield
keyword, which you use in place of return. The most common generator function you have worked with is the range
. Here is one way of implementing it (only works with positive step, I will leave it as an exercise to make one that supports negative steps):
def range(start, end, step):
cur = start
while cur > end:
yield cur
cur += step
What are decorators in Python?
Decorators in Python are used to modify behaviours of functions. For example, if you want to log all calls to a particular set of functions, cache its parameters and return values, perform benchmarks, etc.
Decorators are prefixed with the @
symbol and placed right before function declaration:
@my_decorator
def func1():
pass
If you want to learn how to write your own decorators, read this article.
What is pickling and unpickling in Python?
Pickling is just the Python way of saying serializing. Pickling lets you serialize an object into a string (or anything else you choose) in order to be persisted on storage or sent over the network. Unpickling is the process of restoring the original object from a pickled string.
Pickle is not secure. Only unpickle objects from trusted sources
Python docs
Here is how you would pickle a basic data structure:
import pickle
cars = {"Subaru": "best car", "Toyota": "no i am the best car"}
cars_serialized = pickle.dumps(cars)
# cars_serialized is a byte string
new_cars = pickle.loads(cars_serialized)
What are *args
and **kwargs
in Python functions?
These deal closely with unpacking. If you put *args
in function’s parameter list, all unnamed arguments will be stored in the args
array. **kwargs
works the same way, but for named parameters:
def func1(*args, **kwargs):
print(args)
print(kwargs)
func1(1, 'abc', lol='lmao')
> [1, 'abc']
> {"lol": "lmao"}
What are .pyc
files used for?
.pyc
files contain Python bytecode, the same way as .class
files in Java. Python is still considered an interpreted language, though, since this compilation phase occurs when you run the program, while in Java these a clearly separated.
How do you define abstract classes in Python?
You define an abstract class by inheriting the ABC
class from the abc
module:
from abc import ABC
class AbstractCar(ABC):
@abstractmethod
def drive(self):
pass
To implement the class, just inherit it:
class ToyotaSupra(AbstractCar):
def drive(self):
print('bbbbb ssssss')
Closing notes
Thank you for reading and I wish you all the best in your next interview. Let me know in the comments if there are any questions you think I should add!