Python: Hidden Features — part 2

Advance techniques for better programming

Pravash
4 min readMar 26, 2024

Hi Everyone, In this article I will go throw couple of techniques or features which anyone can use in day to day coding.

In my journey through these Python features, I’ve uncovered techniques that go beyond the ordinary adding a touch of magic to our code.

Lets get started -

1. use next()

Lets see below example -

list_of_dicts = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}
]

# Target name to search
target_name = 'Bob'

# Using a for-each loop to find the dictionary
for person in list_of_dicts:
if person['name'] == target_name:
print(f"Found: {person}")
break # Exit the loop once the target is found
else:
print("Target not found.")

So, here we are using for each loop to find that target in the dictionary and else statement if the target is not present.

There’s a much concise way of doing this is using next().

The next()function in Python is a built-in function that is used to retrieve the next item from an iterator. It’s part of Python’s iterator protocol, which also includes the iter() function to obtain an iterator from an iterable (like lists, tuples, or strings).

Here’s, the modified code -

list_of_dicts = [
{'name': 'Alice', 'age': 30},
{'name': 'Bob', 'age': 25},
{'name': 'Charlie', 'age': 35}
]
person = next((item for item in list_of_dicts if item.get('name') == 'Bob'), "person not found." )
print(f"Found: {person}")

When you call next()on an iterator, it returns the next available item. If there are no more items, it raises the StopIteration exception, unless a default value is provided, in which it returns the value specified by default.

It’s also handy in situations where you want to fetch the next()item from an iterator without using a loop, or when dealing with streams of data where you might want to handle the end of the stream gracefully by
providing a default value.

However, understanding next()and iterators is crucial for working with more advanced Python features and for certain use cases like custom iteration patterns, working with large or infinite sequences, or dealing with data streams.

2. Use StringIO instead of (+) operator

Since concatenating string with (+), copies string.

string_ex = ""
for i in range(50):
string_ex += str(i)

So in the above example, the for loop will end up making 50 copies of string. It’s better to use StringIO object.

import io
string_ex = io.StringIO()
for i in range(50):
string_ex.write(str(i))
new_s = string_ex.getvalue()

So this uses a in-memory buffer.
getValue() — Returns a str containing the entire contents of the buffer.

3. use properties

stop using getter and setter for variables.

class ObjectDef:
def __init__(self, x):
self._x = x

def get_x(self):
return self._x

def set_(self, x):
self._x = x

def print_values():
obj = ObjectDef(0)
obj.set_x(42)
print(obj.get_x)

One way of doing it in pythonic way is to only use the __init__.

But if you really want to do something with related to that variable then having set and get method make sense.
So in order to access the values you have to call set_x and get_x. So using properties will be a nice way of handling this

class ObjectDef:
def __init__(self, x):
self._x = x

@property
def x(self):
return self._x

@x.setter
def x(self, val):
self._x = val

def print_values():
obj = ObjectDef(0)
obj.x = 42
print(obj.x)

So now you only have to use `obj.x = 42`, and that will call the setter with the value 42.

4. list.reverse() vs list[::-1]

Both methods are used to reverse the order of elements in a list, but they achieve this result in different ways.
Let’s understand the difference -

list.reverse()

- In-place modification.
- This method directly modifies the original list by reversing its elements in place.
- It does not return a new list.

list[::-1] (Slicing)

- Creates a new list.
- This method uses slicing syntax to create a copy of the original list in reversed order.
- It returns a new list, leaving the original list intact.

Memory Usage.
- list_reverse() is memory-efficient as it works directly on the list.
- list[::-1] creates a copy, So it can use more memory, especially for large lists.

So its better to use the methods wisely.

5. using else in for/while loop

Lets just look at the below example, where we are using flag to find the item. So we need to set the flag as true and at the end of the loop. we can use the flag to do some operation.

items = [1, 2, 3, 4, 5, 6]
i = 0
found = False

while i < len(items):
item = items [i]
if item == "2":
found = True
break
i += 1
if not found:
print("call some function")

However we don’t need to include the flag because of the for-else/while-else syntax. Let me show you the alternative of the above code -

items = [1, 2, 3, 4, 5, 6]
i = 0

while i < len(items):
item = items [i]
if item == "2":
break
i += 1
else:
print("call some function")

So here we will go to the else part and execute the statement if we don’t exit the loop from the break statement, i:e the else statement will run if don’t exit the loop from the break statement And the same goes for the for loop as well.

Conclusion

Above are the few features or you can techniques which reinforce the importance of exploring beyond the basics and continuously learning in the ever-evolving Python ecosystem.

Happy coding !!

--

--

Pravash

I am a passionate Data Engineer and Technology Enthusiast. Here I am using this platform to share my knowledge and experience on tech stacks.