Built-in Python features, like decorators and list comprehensions, enable developers to convert messy code into clear and readable solutions without having to recreate functionality.
Learning to code is quite a challenging journey. On one hand, you have the technical aspects of coding, while on the other, there’s the focus on crafting that code with elegance. Personally, I found the latter to be the most difficult. I could successfully tackle problems in a brute-force manner, but when it came to producing a sophisticated solution, I would inevitably opt for nested loops every time. However, that approach has its drawbacks, as effective code should adhere to the principles of DRY (Don’t Repeat Yourself), be memory-efficient, and be understandable to others.
Fortunately, Google was a valuable resource for me, and I gradually discovered tools that enabled me to create cleaner solutions more easily without having to start from scratch. Below are some of Python’s built-in features that enhance both code clarity and simplicity.
*args and **kwargs
*args and **kwargs enhance the versatility of functions. By utilizing *args as a parameter, a function can accept any number of arguments. If *args were not included, developers would need to handle integer and string arguments individually.
With *args:
def add(*args):
results = []
for n in args:
results.append(n)
return results
print(add())
print(add(1,"Cat"))
print(add(1,"Lion",3))
# []
# [1, 'Cat']
# [1, 'Lion', 3]
And without it just throws an error…
**kwargs
**kwargs functions similarly to *args, but it is used for key-value pairs. You can utilize **kwargs in a function that either doesn’t have a fixed number of parameters or can accept an indefinite number of key-value pairs.
def dictionary_builder(**kwargs):
for x,y in kwargs.items():
print(f'key: {x}, value: {y}')
dictionary_builder(topic = 'Python')
dictionary_builder(city = 'Jalgaon', state = 'Maharashtra')
dictionary_builder(month = 'July', day = 7, year = 2024)
# key: topic, value: Python
# key: city, value: Jalgaon
# key: state, value: Maharashtra
# key: month, value: July
# key: day, value: 7
# key: year, value: 2024
List Comprehension
List comprehensions are a fantastic way for developers to whip up lists in just a single line! If we were to build a list of numbers without using a list comprehension, we could do it with the following code:
numbers = []
for i in range (1, 7):
numbers.append(i)
A list comprehension turns that code into a single line. The basic syntax is:
[expression for item in iterable]
and in simple code, it looks like this:
numbers= [i for i in range(5)]
List comprehensions can also include filtering functionality.
even_numbers = [i for i in range(1,5) if i % 2 == 0]
print(even_numbers) # [2, 4]
Did you know that there’s more than just lists when it comes to creating data structures? You can also make a dictionary in a similar way, using the same creation pattern! The basic syntax for dictionaries looks like this:
{key_expression: value_expression for item in iterable}
.
We can multiply each number in our numbers list by 10 to a by_tens dictionary.
numbers = [1, 2, 3, 4, 5, 6]
by_tens = {num: num*10 for num in numbers}
print(by_tens) #{1: 10, 2: 20, 3: 30, 4: 40, 5: 50, 6: 60}
We can also do this with a set. The basic syntax is
{expression for item in iterable}
. The code looks like this:
numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
even_set = {num for num in numbers if num % 2 == 0}
print(even_set)# {2, 4, 6, 8, 10}
zip()
zip()
function is a fantastic way to work with multiple lists at the same time, allowing you to easily create tuples from their corresponding elements. It really transforms what could be a lengthy process into a quick and tidy one-liner, making your code feel cleaner and more efficient!
zip()
The code goes through the length of the smallest list, so if the lists you’re working with happen to be of different lengths, the resulting tuples will match the size of the shortest list. Below is a lovely example that clearly demonstrates both of these important points:
pets = ["lion", "Bear", "Donkey", "Rabbit", "Pig"]
names = ["Maxie", "Spot", "Dusty", "Swift", "Speedy"]
ages = [6, 5, 5, 16]
# Create a zipped object combining the pets, names, and ages lists
result = zip(pets, names, ages)
# Print the zipped object
print("Zipped result as a list:")
for i in list(result):
print(i)
# Zipped result as a list:
# ('lion', 'Maxie', 6)
# ('Bear', 'Spot', 5)
# ('Donkey', 'Dusty', 5)
# ('Rabbit', 'Swift', 16)
Merging Dictionaries
You can merge dictionaries using the update()
functionality or the dictionary unpacking syntax (**
).
land_pets = {'dog': 'Rocky', 'cat': 'Luna', 'horse': 'Atlas'}
water_pets = {'fish': 'Neptune', 'turtle': 'Coral'}
all_pets = {**land_pets, **water_pets}
print(all_pets)
# Output:{'dog': 'Rocky', 'cat': 'Luna', 'horse': 'Atlas', 'fish': 'Neptune', 'turtle': 'Coral'}
or you could use the update function to add the water_pets
object to the land_pets
object: land_pets.update(water_pets).
Chaining Comparison Operators
Chaining comparison operators enables the combination of several comparisons within a single expression. This chaining eliminates the necessity for the explicit `and` operator. Additionally, it enhances both the readability and efficiency of the code by decreasing the number of individual comparisons.
The example below compares the variable miles to determine if the distance is in range. The code looks like this without chaining comparison operators:
def distance_checker(miles):
if miles > 0 and miles <10:
print("in range")
else:
print("out of range")
distance_checker(3) # in range
When the comparison is expressed as a compound condition, it looks like this:
def distance_checker(miles):
if 0 < miles <10:
print("in range")
else:
print("out of range")
distance_checker(13) # out of range
Ternary Operator
Ternary operators allow developers to write if conditionals in a single line. The basic syntax is:
result = true_value if condition else false_value
If the condition evaluates to True
the expression returns the true value. If the condition evaluates to False
, the expression returns the false value. Here’s an example without the turnery operator
x = 5
if x % 2 == 0:
result = "even"
else:
result = "odd"
print(result) #odd
Versus the if conditional with the ternary operator:
x = 5
result = "even" if x % 2 == 0 else "odd"
print(result)
Decorators
Decorators are wonderful tools that allow us to enhance our functions without altering their original code. Essentially, a decorator is a charming little function that receives the original function as its input, spruces it up with new features, and then graciously hands back the updated function for us to enjoy.
Let’s start with a basic division function.
def division(x,y):
print(x/y)
division(10,5) # 2
division(9, 3) # 3
Let’s imagine for a moment that this function always needs to divide the larger number by the smaller one. There are plenty of reasons why changing the source code might not be the best option, and that’s where a decorator can really shine! If you’ve got a bit of familiarity with closures, this might feel quite familiar to you. For those who are new to the concept, think of a decorator as a delightful function that builds, modifies, and returns another function. The basic structure of the decorator will look something like this:
#decorator function definition, function passed in as an argument
def decorated_division(func):
#inner function where the modifications will take place, has the same arguments as the function it's modifying
def inner(a,b):
#return the inner function
return inner
Inside the inner function is where we’ll check to see if the arguments are in the correct order and make the necessary swaps (another Python trick) if not.
def decorated_division(func):
def inner(a,b):
if a < b:
a,b = b,a
return func(a,b)
return inner
The inner function looks and behaves like any basic function does.
Now there are a few different ways we can use the decorator function to modify the division function. The first way to do this is to use @decorator
and it looks like this:
@decorated_division
def division(x,y):
print(x/y)
division(5, 10) # 2
Another way to do this is to assign the function as a variable:
new_division = decorated_division(division)
new_division(5,10) # 2
Happy Coding!
Those helpful tips are bound to take your code from simple to truly elegant. The more you dive into Python, the more enjoyable and effortless it becomes!