Python
Key features
Interpreted language. Does not need to be compiled before it is run.
Dynamically typed. No need to state the types of variables when you declare them. You can do
x=111
and thenx="I'm a string"
.Object orientated programming. It allows the definition of classes along with composition and inheritance. Python does not have access specifiers (like C++’s
public
,private
): “we are all adults here”.Functions are first-class objects. They can be assigned to variables, returned from other functions and passed into functions. Classes are also first class objects.
Writing Python code is quick but running it is often slower than compiled languages. Fortunately,Python allows the inclusion of C based extensions so bottlenecks can be optimized away and often are. The
numpy
package is a good example of this, it’s really quite quick because a lot of the number crunching it does isn’t actually done by PythonUse in many spheres – web applications, automation, scientific modeling, big data applications and many more.
Built-In Types
Immutable built-in datatypes:
Numbers
Strings
Tuples
Mutable built-in datatypes:
List
Dictionaries
Sets
*args
and **kwargs
*args
and **kwargs
*args
when we aren’t sure how many arguments are going to be passed to a function, or if we want to pass a stored list or tuple of arguments to a function.
It gives us the ability to pass N (variable) number of arguments.
This type of argument syntax doesn’t allow passing a named argument to the function.
Example of using the *args:
Output:
**kwargs
is used when we don’t know how many keyword arguments will be passed to a function, or it can be used to pass the values of a dictionary as keyword arguments.
It lets us pass N (variable) number of arguments which can be named or keyworded.
Example of using the **kwargs:
Output:
The identifiers args
and kwargs
are a convention, you could also use *bob
and **billy
but that would not be wise.
When to use them?
Function decorators.
Monkey patching (modifying some code at runtime). Example:
lists versus tuples
Lists are mutable i.e they can be edited.
Tuples are immutable (tuples are lists which can’t be edited).
Lists are slower than tuples.
Tuples are faster than lists.
Syntax: list_1 = [10, ‘Chelsea’, 20]
Syntax: tup_1 = (10, ‘Chelsea’ , 20)
How is Multithreading achieved in Python?
Python doesn't allow multi-threading in the truest sense of the word.
Python has a multi-threading package but if you want to multi-thread to speed your code up, then it’s usually not a good idea to use it.
Python has a construct called the Global Interpreter Lock (GIL). The GIL makes sure that only one of your ‘threads’ can execute at any one time. A thread acquires the GIL, does a little work, then passes the GIL onto the next thread.
This happens very quickly so to the human eye it may seem like your threads are executing in parallel, but they are really just taking turns using the same CPU core.
All this GIL passing adds overhead to execution. This means that if you want to make your code run faster then using the threading package often isn’t a good idea.
range() vs xrange()
For the most part, xrange and range are the exact same in terms of functionality. They both provide a way to generate a list of integers for you to use, however. The only difference is that range returns a Python list object and x range returns an xrange object.
This means that xrange doesn’t actually generate a static list at run-time like range does. It creates the values as you need them with a special technique called yielding. This technique is used with a type of object known as generators. That means that if you have a really gigantic range you’d like to generate a list for, say one billion, xrange is the function to use.
Pickling / Unpickling
Pickling: Pickle module accepts any Python object and converts it into a string representation and dumps it into a file by using dump function.
Unpickling: the process of retrieving original Python objects from the stored string representation.
How are arguments passed by value or by reference?
Everything in Python is an object and all variables hold references to the objects. The references values are according to the functions; as a result you cannot change the value of the references. However, you can change the objects if it is mutable.
Call By Value
In call-by-value, the argument, whether an expression or a value, gets bound to the respective variable in the function.
Python will treat that variable as local in the function-level scope. Any changes made to that variable will remain local and will not reflect outside the function.
Call By Reference
We use both “call-by-reference” and “pass-by-reference” interchangeably. When we pass an argument by reference, then it is available as an implicit reference to the function, rather than a simple copy. In such a case, any modification to the argument will also be visible to the caller.
Advantage: time and space efficiency because it leaves the need for creating local copies.
Disadvantage: a variable can get changed accidentally during a function call.
Namespaces
On starting the interpreter, it creates a namespace for as long as we don’t exit.
We have:
local namespaces
global namespaces
built-in namespaces
Inheritance
OOP mechanism which allows an object to access its parent class features. It carries forward the base class functionality to the child.
We do it intentionally to abstract away the similar code in different classes.
The common code rests with the base class, and the child class objects can access it via inheritance. Example:
Output:
Multiple inheritance and MRO
MRO (Method Resolution Order): When we search for an attribute in a class that is involved in python multiple inheritance, an order is followed.
First, it is searched in the current class. If not found, the search moves to parent classes. This is left-to-right, depth-first.
Example:
super()
Calls a method from the parent class.
Example:
Composition
A type of inheritance in Python. It intends to inherit from the base class but a little differently, i.e., by using an instance variable of the base class acting as a member of the derived class.
See the below diagram.
To demonstrate composition, we need to instantiate other objects in the class and then make use of those instances.
Output:
Determine the type of instance and inheritance
type()
Tells us the type of object we’re working with.
isinstance()
Takes in two arguments- a value and a type. If the value is of the kind of the specified type, it returns True.
issubclass()
Takes two classes as arguments. If the first one inherits from the second, it returns True.
Errors And Exceptions
Errors: coding issues in a program which may cause it to exit abnormally.
Exceptions happen due to the occurrence of an external event which interrupts the normal flow of the program.
Exception handling with Try / Except / Finally
We enclose the unsafe code indented under the try block.
We can keep our fall-back code inside the except block.
Any instructions intended for execution last should come under the finally block.
Output:
Raising Exceptions For A Predefined Condition
We can raise an exception based on some condition.
Example: we want the user to enter only odd numbers, else will raise an exception.
Output:
Module vs package
Each Python program file is a module, which imports other modules like objects and attributes.
The folder of Python program is a package of modules. A package can have modules or subfolders.
Closures
Function objects returned by another function. We use them to eliminate code redundancy.
Example: simple closure for multiplying numbers.
The output is:
Monkey patching. Is it ever a good idea?
Changing the behavior of a function or object after it has already been defined.
Example:
What Are Class Or Static Variables In Python Programming?
All the objects share common class or static variables.
But the instance or non-static variables are altogether different for different objects.
The programming languages like C++ and Java need to use the static keyword to make a variable as the class variable. However, Python has a unique way to declare a static variable.
All names initialized with a value in the class declaration becomes the class variables. And those which get assigned values in the class methods becomes the instance variables.
@classmethod
, @staticmethod
, @property
@classmethod
, @staticmethod
, @property
These are decorators: a special kind of function that either takes a function and returns a function, or takes a class and returns a class. The @
symbol is just syntactic sugar.
Is equivalent to:
This is how they behave:
Optional Statements Inside A Try-Except Block In Python.
else:
Runs a piece of code when the try block doesn’t create an exception.
finally:
Executes some steps which run, regardless of whether an exception happens or not.
Iterators
Array-like objects which allow moving on the next element. We use them in traversing a loop, for example, in a “for” loop.
Generators
A kind of function which lets us specify a function that acts like an iterator and hence can get used in a “for” loop.
In a generator function, the yield keyword substitutes the return statement.
The output is:
Decorators
Give us the ability to add new behavior to the given objects dynamically. In the example below, we’ve written a simple example to display a message pre and post the execution of a function.
The output is:
Generators
A function which returns an iterable object. We can iterate on the generator object using the yield keyword. But we can only do that once because their values don’t persist in memory, they get the values on the fly.
Generators give us the ability to hold the execution of a function or a step as long as we want to keep it.
Examples where it is beneficial to use generators.
Replace loops with generators for efficiently calculating results involving large data sets.
When we don’t want all the results and wish to hold back for some time.
Instead of using a callback function, we can replace it with a generator. We can write a loop inside the function doing the same thing as the callback and turns it into a generator.
Yield Keyword
"yield" can turn any function into a generator. It works like a standard return keyword. But it’ll always return a generator object.
A method can have multiple calls to the yield keyword.
See the example below.
Functional programming: map, reduce, filter
a. filter()
Filter in some values based on conditional logic
list(filter(lambda x:x>5,range(8)))
[6, 7]
b. map()
Applies a function to every element in an iterable.
list(map(lambda x:x**2,range(8)))
[0, 1, 4, 9, 16, 25, 36, 49]
c. reduce()
Repeatedly reduces a sequence pair-wise until we reach a single value.
Python2 vs Python 3
Last updated