← All articles

Python Basics

Python is a great language for beginners and experts alike. Let's explore the basics of Python. From modules, packages, scope, and more. Let's dive in!

5 min read

The simplicity of Python makes the language a great option for almost everyone aiming to use programming in any field. This is the reason Python is widely used in the industry, from web development, data science, machine learning, automation, scientific computing, and more. Having a huge community, and a vast number of libraries, Python is a great choice for beginners and experts alike.

In this post, we will explore some of the best features of Python, and how you can use them to your advantage.

Modules

A module is a file containing Python definitions and statements. The file name is the module name with the suffix .py appended. Within a module, the module’s name (as a string) is available as the value of the global variable __name__.

A module can be imported by another program to make use of its functionality. We can define our most used functions in a module and import it, instead of copying their definitions into different programs.

if __name__ == "__main__"

You will often see this construct in Python files. This is used to execute some code only if the file was run directly, and not imported. That is, if the file is imported, the code is not run. This is because when you import a module, the code in the module is executed, just like any other script. So if we want to have some code that we only want to run when the module is run directly, we can use this construct.

def main():
    print("Running as script!")

# execute only if run as a script
if __name__ == "__main__":
    main()

Packages

A package organizes modules and subpackages under a dotted namespace. A regular package contains __init__.py, which may be empty or contain initialization code. Namespace packages can omit that file and span multiple directories; they have been supported since Python 3.3. See the Python import referencedocs.python.orgPython language reference for the import system, at packages: regular packages and namespace packages..

# __init__.py
# In a package directory, you would use relative imports:
# from . import module1, module2, module3
print("Package initialized!")

Doing this will allow us to use namespaced modules, such as package.module1, package.module2, etc.

Scope

Scopes are the contexts in which names are looked up. There are four different scopes in Python: local, enclosing, global, and built-in.

The scope of a name defines the area of the program where you can unambiguously access that name, such as variables, functions, objects, and so on. It is determined by the place where it is declared.

Names that are declared outside of all functions are in the global scope. This means that those names can be accessed inside or outside of functions.

Names that are declared inside a function are in the local scope, and can only be accessed inside that function.

The enclosing scope is a special scope that only exists for nested functions. If the local scope is an inner or nested function, then the enclosing scope is the scope of the outer or enclosing function.

The built-in scope is the outermost scope in Python, and it is the scope that contains all of the built-in names in Python. The built-in scope is searched last, after the local, enclosing, and global scopes (LEGBLocal, Enclosing, Global, Built-in: the order in which Python looks up names).

built-inprint, len, range, …globalx = 10enclosingdef outer(): y = 20localdef inner(): z = 30print(x)① no x② no x③ x = 10, found④ not reached
To resolve x inside inner(), Python checks the local scope, then the enclosing outer(), and finds x = 10 in the global scope. The search stops at the first match, so built-in is never checked.

Example Global Scope

x = 10

def my_func():
    print(x)

my_func()
print(x)
10
10

Example Local Scope

def my_func():
    x_ = 10
    print(x_)

my_func()
try:
    print(x_)
except NameError as e:
    print(e)
10
name 'x_' is not defined

Example Enclosing Scope

def outer():
    x = 'local'

    def inner():
        nonlocal x
        x = 'nonlocal'
        print('inner:', x)

    inner()
    print('outer:', x)

outer()
inner: nonlocal
outer: nonlocal

Note that the nonlocal keyword is used to declare that x is not a local variable. Hence, when we assign a value to x inside the nested function, that change is reflected in the local variable in the enclosing function.

Example: A Local Name Shadows a Global Name

x = 10

def my_func():
    x = 20
    print(x)

my_func()
print(x)
20
10

Example Keyword global

x = 10

def my_func():
    global x
    x = 20
    print(x)

my_func()
print(x)
20
20

Note that the global keyword is used to declare that x is a global variable - hence, when we assign a value to x inside the function, that change is reflected when we use the value of x in the main block.

Built-in Scope

These are the names in the pre-defined built-in modules. These are always available in your Python programs. You can see the list of built-in names by typing import builtins; dir(builtins) in the Python interpreter.

Splat Operator * and Double Splat Operator **

Splat operator is a kind of unpacking operator (destructuringSyntax that unpacks values from arrays, or properties from objects, into separate variables. in JSJavaScript). It can be used to allow an iterable to be unpacked into positional arguments in a function call, or into a list, tuple, or set display. ** unpacks a mapping into keyword arguments or a dictionary display. These are often called the splat (*) and double splat (**) operators.

Splat Operator

def add(x, y):
    return x + y

numbers = [3, 5]
print(add(*numbers)) # 8

Double Splat Operator

def display_names(first, second):
    print(f'{first} says hello to {second}')

names = {"first": "John", "second": "Bob"}
display_names(**names) # John says hello to Bob

Multiple Arguments

def print_everything(*args):
    for count, thing in enumerate(args):
        print('{0}. {1}'.format(count, thing))

print_everything('apple', 'banana', 'cabbage')
0. apple
1. banana
2. cabbage

Keyword Arguments

def table_things(**kwargs):
    for name, value in kwargs.items():
        print('{0} = {1}'.format(name, value))

table_things(apple='fruit', cabbage='vegetable')
apple = fruit
cabbage = vegetable

Unpacking Arguments

def print_three_things(a, b, c):
    print('a = {0}, b = {1}, c = {2}'.format(a, b, c))

my_list = ['aardvark', 'baboon', 'cat']
print_three_things(*my_list)

my_dict = {'a': 'apple', 'b': 'banana', 'c': 'cherry'}
print_three_things(**my_dict)
a = aardvark, b = baboon, c = cat
a = apple, b = banana, c = cherry

Positional and keyword arguments (*args, **kwargs)

def func(*args, **kwargs):
    print(args)
    print(kwargs)

func(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, a=1, b=2, c=3)
(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
{'a': 1, 'b': 2, 'c': 3}