Python Features


10 Lesser-Known Python Features You Should Be Using

Python is a powerful programming language with a wide range of features and capabilities. However, there are some lesser-known features that can greatly improve the efficiency and readability of your code. 

In this guide, we’ll explore 10 hidden Python features that you should be using.

1. List Comprehensions

1.1 What are List Comprehensions?

List comprehensions are a concise way to create lists in Python. They allow you to generate a new list by applying a transformation to each element of an existing list or other iterable, subject to some condition.

List comprehensions have the following syntax:


new_list = [expression for variable in iterable if condition]

Here, expression is the operation to be performed on each element of the iterable, variable is the name of the variable used to represent each element of the iterable, iterable is the list or other iterable to be transformed, and condition is an optional condition that must be true for each element to be included in the new list.

For example, let’s say we have a list of integers, and we want to create a new list containing only the even numbers, multiplied by 2. We can do this with a list comprehension as follows:


original_list = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] new_list = [x * 2 for x in original_list if x % 2 == 0] print(new_list)



[4, 8, 12, 16, 20]

Here, x * 2 is the expression to be performed on each even number in the original list, x is the variable used to represent each element of the list, original_list is the list to be transformed, and x % 2 == 0 is the condition that must be true for each element to be included in the new list.

List comprehensions can also be used to create lists of tuples, dictionaries, and even other lists. They are a powerful tool for creating new lists in a concise and readable way.

1.2 Example Usage:


# Create a list of even numbers

evens = [i for i in range(10) if i % 2 == 0]

# Create a list of squares

squares = [i**2 for i in range(10)]

2. Generators

2.1 What are Generators?

Generators are a type of iterable, like lists or tuples. However, unlike lists, generators do not store their values in memory. Instead, they generate their values on the fly as you iterate over them.

The main advantage of using generators over lists is that generators can be more memory-efficient, especially when working with large data sets. Since generators do not store their values in memory, they can be used to process very large data sets that would not fit in memory as a list.

Generators are created using a special type of function called a generator function. When a generator function is called, it returns a generator object, which can be used to generate values on the fly. The most common way to create a generator function is to use the yield keyword to generate a series of values.

Here is an example of a simple generator function that generates the first 10 even numbers:


def even_numbers(): for i in range(10): yield i*2

To use this generator function, you can iterate over it using a for loop or use the next() function to generate the values one at a time:


# using a for loop for num in even_numbers(): print(num) # using next() g = even_numbers() print(next(g)) # 0 print(next(g)) # 2 print(next(g)) # 4

Generators can also be used to generate an infinite sequence of values. For example, the following generator function generates an infinite sequence of Fibonacci numbers:


def fibonacci(): a, b = 0, 1 while True: yield a a, b = b, a + b

To use this generator function, you can use the next() function to generate the values one at a time:


g = fibonacci() print(next(g)) # 0 print(next(g)) # 1 print(next(g)) # 1 print(next(g)) # 2 print(next(g)) # 3 # and so on...

2.2 Example Usage:


# Define a generator function

def squares(n):

    for i in range(n):

        yield i**2

# Create a generator object

s = squares(10)

# Iterate over the generator object

for x in s:


3. Defaultdict

3.1 What is Defaultdict?

defaultdict is a dictionary-like object provided by Python’s collections module. It is a subclass of the built-in dictionary type that allows the user to set a default value for a key that does not exist yet in the dictionary. This makes it more convenient to perform operations on the dictionary without having to check if a key exists, and create a new entry if it does not.

To use defaultdict, you need to import it from the collections module. You can create a new instance of defaultdict by passing a default value to the constructor, which will be returned when a non-existent key is accessed. For example, to create a defaultdict that returns the integer value 0 for new keys:


from collections import defaultdict d = defaultdict(int)

Now, you can use this dictionary just like a regular dictionary, but with the added benefit that you don’t need to check if a key exists before you access it. If the key doesn’t exist, the default value of 0 will be returned. For example:


d['a'] += 1 d['b'] += 2 print(d['a']) # 1 print(d['b']) # 2 print(d['c']) # 0 (default value)

You can also pass a function to the defaultdict constructor instead of a value. This function will be called with no arguments whenever a new key is accessed, and the value it returns will be used as the default value. For example, to create a defaultdict that returns an empty list for new keys:


from collections import defaultdict d = defaultdict(list)

Now, you can append values to the lists in the same way as before:


d['a'].append(1) d['b'].append(2) print(d['a']) # [1] print(d['b']) # [2] print(d['c']) # [] (empty list)

3.2 Example Usage:


from collections import defaultdict

# Create a defaultdict

d = defaultdict(int)

# Increment a key

d['key'] += 1

4. Enumerate

4.1 What is Enumerate?

enumerate() is a built-in function in Python that is used to iterate over a sequence and keep track of the index of the current item. It returns an enumerate object which consists of pairs of the index and the corresponding item in the sequence.

Here’s an example:


fruits = ['apple', 'banana', 'orange'] for index, fruit in enumerate(fruits): print(index, fruit)

This will output:

0 apple 1 banana 2 orange

As you can see, enumerate() makes it easy to loop over a sequence and keep track of the index at the same time. This can be useful in a variety of situations, such as when you need to access both the index and the item in a loop.

4.2 Example Usage:


# Iterate over a list with index

for i, x in enumerate(['a', 'b', 'c']):

    print(i, x)

5. Zip

5.1 What is Zip?

zip() is a built-in Python function that takes iterables as inputs and returns an iterator of tuples where the i-th tuple contains the i-th element from each of the input iterables.

For example, suppose we have two lists:


list1 = [1, 2, 3] list2 = ['a', 'b', 'c']

We can use zip() to create a new list of tuples like this:

result = list(zip(list1, list2)) print(result)

The output will be:


result = list(zip(list1, list2)) print(result)

The output will be:

[(1, 'a'), (2, 'b'), (3, 'c')]

Note that zip() will stop iterating when the shortest input iterable is exhausted. If one input iterable is shorter than the others, zip() will only return tuples up to the length of the shortest iterable.

zip() can take any number of input iterables. If you have more than two iterables, the resulting tuples will contain elements from all of the input iterables, in order.

5.2 Example Usage:


# Iterate over two lists in parallel

for x, y in zip([1, 2, 3], ['a', 'b', 'c']):

    print(x, y)

6. Any and All

6.1 What are Any and All?

any() and all() are built-in Python functions used to evaluate boolean values of iterables.

any() returns True if any element of an iterable is True, and False otherwise. If the iterable is empty, any() returns False.

all() returns True if all elements of an iterable are True, and False otherwise. If the iterable is empty, all() returns True.

Both functions are often used in conjunction with list comprehension or generator expressions to filter and evaluate elements of a collection.

Here is an example usage of any():


numbers = [1, 2, 3, 4, 5] result = any(num > 5 for num in numbers) print(result) # False

In this example, any() returns False because there are no elements in numbers that are greater than 5.

Here is an example usage of all():


numbers = [2, 4, 6, 8, 10] result = all(num % 2 == 0 for num in numbers) print(result) # True

In this example, all() returns True because all elements in numbers are even.

Both any() and all() can also be used with other iterables such as sets, tuples, and dictionaries.

6.2 Example Usage:


# Check if any elements are even

if any([i % 2 == 0 for i in range(10)]):

    print('At least one element is even')

# Check if all elements are even

if all([i % 2 == 0 for i in range(10)]):

    print('All elements are even')

7. Decorators

7.1 What are Decorators?

Decorators are a powerful feature of Python that allow programmers to modify the behavior of functions or classes without changing their source code. Decorators are implemented as functions that take a function or class as input and return a new function or class with modified behavior.

The syntax for using decorators is to prefix the function or class with the decorator function name and the “@” symbol. For example, to use a decorator named “my_decorator” to modify the behavior of a function named “my_function”, we would write:


@my_decorator def my_function(): # function code here

When the code is executed, the decorator function is called with the original function as its input, and it returns a new function object with modified behavior. The new function object is then assigned to the original function name, so that subsequent calls to the function will use the modified behavior.

Decorators can be used to implement a wide range of functionality, such as logging, authentication, caching, and performance profiling. They can also be stacked together to provide multiple layers of functionality.

Python provides a number of built-in decorators, such as “@staticmethod” and “@classmethod”, which modify the behavior of class methods. Programmers can also create their own custom decorators to implement specific functionality.

7.2 Example Usage:


# Define a decorator

def my_decorator(func):

    def wrapper():




    return wrapper

# Apply the decorator


def my_function():


# Call the decorated function


8. Context Managers

8.1 What are Context Managers?

Context managers are a Python language feature that help manage resources and handle errors when working with code that requires setup or teardown procedures. A context manager is a Python object that defines the methods __enter__ and __exit__. These methods are called when the context manager is entered and exited, respectively.

The with statement is used to create a context and manage the resources allocated to it. When a block of code is wrapped in a with statement, the context manager’s __enter__ method is called, which can be used to set up resources. Once the block of code is executed, the context manager’s __exit__ method is called, which can be used to release resources.

Context managers are commonly used for file I/O, database connections, and network connections, among other things. They provide a clean and easy-to-use way to ensure that resources are properly managed and errors are handled. Python provides several built-in context managers, such as open() for file I/O and socket.socket() for network connections. Additionally, it is easy to define custom context managers using the @contextmanager decorator from the contextlib module.

8.2 Example Usage:


# Define a context manager

class MyContext:

    def __enter__(self):


        return self

    def __exit__(self, exc_type, exc_value, traceback):


    def do_something(self):

        print('Do something')

# Use the context manager

with MyContext() as c:


9. Underscores in Numeric Literals

9.1 What are Underscores in Numeric Literals?

In Python, underscores can be used in numeric literals to make them more readable. They are ignored by the interpreter and have no impact on the value of the literal.

For example, instead of writing:


million = 1000000

You can write:


million = 1_000_000

Both of these assignments have the same value of 1,000,000, but the second one is easier to read.

Underscores can be added anywhere in a numeric literal except at the beginning or end, or immediately before or after the decimal point in a floating-point literal. They can be used with integers, floating-point numbers, and complex numbers.

Underscores can also be used in binary, octal, and hexadecimal literals:


binary = 0b_1010_1101 octal = 0o_755 hexadecimal = 0x_1a_f2_39_bc

Using underscores in numeric literals is purely optional, but it can make the code more readable, especially when dealing with large numbers or numbers with many digits.

9.2 Example Usage:


# Define a large number

num = 10_000_000_000

# Print the number


10. Walrus Operator

10.1 What is Walrus Operator?

The Walrus Operator, denoted by :=, is a new feature introduced in Python 3.8 that allows you to assign a value to a variable as part of an expression. The operator gets its name from the resemblance of its shape to that of a walrus, with the colon representing the tusks and the equal sign representing the eyes.

The Walrus Operator provides a way to simplify code and make it more readable by reducing the number of lines required to achieve the same result. It allows you to combine the process of checking a condition and assigning a value to a variable in a single line of code, thereby reducing the amount of code you need to write.

Here’s an example of how the Walrus Operator can be used in Python:


# Without Walrus Operator input_str = input("Enter a string: ") while input_str != "": print(input_str) input_str = input("Enter another string: ") # With Walrus Operator while (input_str := input("Enter a string: ")) != "": print(input_str)

As you can see, the Walrus Operator is used to assign the result of input() to the input_str variable, and also check whether input_str is not equal to an empty string, all in a single line of code.

The Walrus Operator is a powerful tool for writing concise and readable code, but it should be used with care, as it can make code harder to understand if overused or used inappropriately. It is generally recommended to use the Walrus Operator only when it makes code more readable, and not to use it just because it’s available.

10.2 Example Usage:


# Assign a value to a variable in an expression

if (n := len(my_list)) > 10:

    print(f'The list has {n} elements')

11. Conclusion

Python is a rich language with many features that can greatly improve the efficiency and readability of your code. By mastering these 10 lesser-known features, you can become a more effective Python developer and write code that is more concise, readable, and maintainable.

Hire top vetted developers today!