Thursday, May 8, 2025
News PouroverAI
Visit PourOver.AI
No Result
View All Result
  • Home
  • AI Tech
  • Business
  • Blockchain
  • Data Science & ML
  • Cloud & Programming
  • Automation
  • Front-Tech
  • Marketing
  • Home
  • AI Tech
  • Business
  • Blockchain
  • Data Science & ML
  • Cloud & Programming
  • Automation
  • Front-Tech
  • Marketing
News PouroverAI
No Result
View All Result

5 Tips for Writing Better Python Functions

June 4, 2024
in Data Science & ML
Reading Time: 10 mins read
0 0
A A
0
Share on FacebookShare on Twitter


py-funcImage by Author
 

We all write functions when coding in Python. But do we necessarily write good functions? Well, let’s find out.

Functions in Python let you write modular code. When you have a task you need to perform at multiple places, you can wrap the logic of the task into a Python function. And you can call the function every time you need to perform that specific task. As simple as it seems to get started with Python functions, writing maintainable and performant functions is not so straightforward.

And that’s why we’ll explore a few practices that’ll help you write cleaner and easy-to-maintain Python functions. Let’s get started…

 

1. Write Functions That Do Only One Thing

 

When writing functions in Python, it’s often tempting to put all related tasks into a single function. While this can help you code things up quickly, it’ll only make your code a pain to maintain in the near future. Not only will this make understanding what a function does more difficult but also leads to other issues such as too many parameters (more on that later!).

As a good practice, you should always try to make your function do only one thing—one task—and do that well. But sometimes, for a single task, you may need to work through a series of subtasks. So how do you decide if and how the function should be refactored?

Depending on what the function is trying to do and how complex the task is, you can work out the separation of concerns between subtasks. And then identify a suitable level at which you can refactor the function into multiple functions—each focusing on a specific subtask.

 

refactor-funcrefactor-funcRefactor functions | Image by Author
 

Here’s an example. Look at the function analyze_and_report_sales:

# fn. to analyze sales data, calculate sales metrics, and write it to a file
def analyze_and_report_sales(data, report_filename):
total_sales = sum(item[‘price’] * item[‘quantity’] for item in data)
average_sales = total_sales / len(data)

with open(report_filename, ‘w’) as report_file:
report_file.write(f”Total Sales: {total_sales}\n”)
report_file.write(f”Average Sales: {average_sales}\n”)

return total_sales, average_sales

 

It’s quite easy to see that it can be refactored into two functions: one calculating the sales metrics and another on writing the sales metrics to a file like so:

# refactored into two funcs: one to calculate metrics and another to write sales report
def calculate_sales_metrics(data):
total_sales = sum(item[‘price’] * item[‘quantity’] for item in data)
average_sales = total_sales / len(data)
return total_sales, average_sales

def write_sales_report(report_filename, total_sales, average_sales):
with open(report_filename, ‘w’) as report_file:
report_file.write(f”Total Sales: {total_sales}\n”)
report_file.write(f”Average Sales: {average_sales}\n”)

 

Now it’s easier to debug any concerns with the calculation of sales metrics and file operations separately. And here’s a sample function call:

data = [{‘price’: 100, ‘quantity’: 2}, {‘price’: 200, ‘quantity’: 1}]
total_sales, average_sales = calculate_sales_metrics(data)
write_sales_report(‘sales_report.txt’, total_sales, average_sales)

 

You should be able to see the ‘sales_report.txt’ file in your working directory with the sales metrics. This is a simple example to get started, but this is helpful especially when you’re working on more complex functions.

 

2. Add Type Hints to Improve Maintainability

 

Python is a dynamically typed language. So you do not need to declare types for the variables you create. But you can add type hints to specify the expected data type for variables. When you define the function, you can add the expected data types for the parameters and the return values.

Because Python does not enforce types at runtime, adding type hints has no effect at runtime. But there still are benefits to using type hints, especially on the maintainability front:

Adding type hints to Python functions serves as inline documentation and gives a better idea of what the function does and what values it consumes and returns.
When you add type hints to your functions, you can configure your IDE to leverage these type hints. So you’ll get helpful warnings if you try to pass an argument of invalid type in one or more function calls, implement functions whose return values do not match the expected type, and the like. So you can minimize errors upfront.
You can optionally use static type checkers like mypy to catch errors earlier rather than letting type mismatches introduce subtle bugs that are difficult to debug.

Here’s a function that processes order details:

# fn. to process orders
def process_orders(orders):
total_quantity = sum(order[‘quantity’] for order in orders)
total_value = sum(order[‘quantity’] * order[‘price’] for order in orders)
return {
‘total_quantity’: total_quantity,
‘total_value’: total_value
}

 

Now let’s add type hints to the function like so:

# modified with type hints
from typing import List, Dict

def process_orders(orders: List[Dict[str, float | int]]) -> Dict[str, float | int]:
total_quantity = sum(order[‘quantity’] for order in orders)
total_value = sum(order[‘quantity’] * order[‘price’] for order in orders)
return {
‘total_quantity’: total_quantity,
‘total_value’: total_value
}

 

With the modified version, you get to know that the function takes in a list of dictionaries. The keys of the dictionary should all be strings and the values can either be integers or floating point values. The function also returns a dictionary. Let’s take a sample function call:

# Sample data
orders = [
{‘price’: 100.0, ‘quantity’: 2},
{‘price’: 50.0, ‘quantity’: 5},
{‘price’: 150.0, ‘quantity’: 1}
]

# Sample function call
result = process_orders(orders)
print(result)

 

Here’s the output:

{‘total_quantity’: 8, ‘total_value’: 600.0}

 

In this example, type hints help us get a better idea of how the function works. Going forward, we’ll add type hints for all the better versions of Python functions we write.

 

3. Accept Only the Arguments You Actually Need

 

If you are a beginner or have just started your first dev role, it’s important to think about the different parameters when defining the function signature.

It’s quite common to introduce additional parameters in the function signature that the function never actually processes.

Ensuring that the function takes in only the arguments that are actually necessary keeps function calls cleaner and more maintainable in general. On a related note, too many parameters in the function signature also make it a pain to maintain. So how do you go about defining easy-to-maintain functions with the right number of parameters?

If you find yourself writing a function signature with a growing number of parameters, the first step is to remove all unused parameters from the signature. If there are too many parameters even after this step, go back to tip #1: break down the task into multiple subtasks and refactor the function into multiple smaller functions. This will help keep the number of parameters in check.

 

num-paramsnum-paramsKeep num_params in check | Image by Author
 

It’s time for a simple example. Here the function definition to calculate student grades contains the instructor parameter that’s never used:

# takes in an arg that’s never used!
def process_student_grades(student_id, grades, course_name, instructor’):
average_grade = sum(grades) / len(grades)
return f”Student {student_id} achieved an average grade of {average_grade:.2f} in {course_name}.”

 

You can rewrite the function without the instructor parameter like so:

# better version!
def process_student_grades(student_id: int, grades: list, course_name: str) -> str:
average_grade = sum(grades) / len(grades)
return f”Student {student_id} achieved an average grade of {average_grade:.2f} in {course_name}.”

# Usage
student_id = 12345
grades = [85, 90, 75, 88, 92]
course_name = “Mathematics”
result = process_student_grades(student_id, grades, course_name)
print(result)

 

Here’s the output of the function call:

Student 12345 achieved an average grade of 86.00 in Mathematics.

 

 

4. Enforce Keyword-Only Arguments to Minimize Errors

 

In practice, most Python functions take in multiple arguments. You can pass in arguments to Python functions as positional arguments, keyword arguments, or a mix of both. Read Python Function Arguments: A Definitive Guide for a quick review of function arguments.

Some arguments are naturally positional. But sometimes having function calls containing only positional arguments can be confusing. This is especially true when the function takes in multiple arguments of the same data type, some required and some optional.

If you recall, with positional arguments, the arguments are passed to the parameters in the function signature in the same order in which they appear in the function call. So change in order of arguments can introduce subtle bugs type errors.

It’s often helpful to make optional arguments keyword-only. This also makes adding optional parameters much easier—without breaking existing calls.

Here’s an example. The process_payment function takes in an optional description string:

# example fn. for processing transaction
def process_payment(transaction_id: int, amount: float, currency: str, description: str = None):
print(f”Processing transaction {transaction_id}…”)
print(f”Amount: {amount} {currency}”)
if description:
print(f”Description: {description}”)

 

Say you want to make the optional description a keyword-only argument. Here’s how you can do it:

# enforce keyword-only arguments to minimize errors
# make the optional `description` arg keyword-only
def process_payment(transaction_id: int, amount: float, currency: str, *, description: str = None):
print(f”Processing transaction {transaction_id}:”)
print(f”Amount: {amount} {currency}”)
if description:
print(f”Description: {description}”)

 

Let’s take a sample function call:

process_payment(1234, 100.0, ‘USD’, description=’Payment for services’)

 

This outputs:

Processing transaction 1234…
Amount: 100.0 USD
Description: Payment for services

 

Now try passing in all arguments as positional:

# throws error as we try to pass in more positional args than allowed!
process_payment(5678, 150.0, ‘EUR’, ‘Invoice payment’)

 

You’ll get an error as shown:

Traceback (most recent call last):
File “/home/balapriya/better-fns/tip4.py”, line 9, in
process_payment(1234, 150.0, ‘EUR’, ‘Invoice payment’)
TypeError: process_payment() takes 3 positional arguments but 4 were given

 

5. Don’t Return Lists From Functions; Use Generators Instead

 

It’s quite common to write Python functions that generate sequences such as a list of values. But as much as possible, you should avoid returning lists from Python functions. Instead you can rewrite them as generator functions. Generators use lazy evaluation; so they yield elements of the sequence on demand rather than computing all the values ahead of time. Read Getting Started with Python Generators for an introduction to how generators work in Python.

As an example, take the following function that generates the Fibonacci sequence up to a certain upper limit:

# returns a list of Fibonacci numbers
def generate_fibonacci_numbers_list(limit):
fibonacci_numbers = [0, 1]
while fibonacci_numbers[-1] + fibonacci_numbers[-2]

 

It’s a recursive implementation that’s computationally expensive and populating the list and returning it seems more verbose than necessary. Here’s an improved version of the function that uses generators:

# use generators instead
from typing import Generator

def generate_fibonacci_numbers(limit: int) -> Generator[int, None, None]:
a, b = 0, 1
while a

 

In this case, the function returns a generator object which you can then loop through to get the elements of the sequence:

limit = 100
fibonacci_numbers_generator = generate_fibonacci_numbers(limit)
for num in fibonacci_numbers_generator:
print(num)

 

Here’s the output:

0
1
1
2
3
5
8
13
21
34
55
89

 

As you can see, using generators can be much more efficient especially for large input sizes. Also, you can chain multiple generators together, so you can create efficient data processing pipelines with generators.

 

Wrapping Up

 

And that’s a wrap. You can find all the code on GitHub. Here’s a review of the different tips we went over:

Write functions that do only one thing
Add type hints to improve maintainability
Accept only the arguments you actually need
Enforce keyword-only arguments to minimize errors
Don’t return lists from functions; use generators instead

I hope you found them helpful! If you aren’t already, try out these practices when writing Python functions. Happy coding!  

Bala Priya C is a developer and technical writer from India.

She enjoys working at the intersection of math, programming, data science, and content creation. Her areas of interest and expertise include DevOps, data science, and natural language processing. She loves reading, writing, coding, and coffee! Currently, she is focused on learning and sharing her knowledge with the developer community by creating tutorials, how-to guides, opinion pieces, and more. Bala also develops engaging resource overviews and coding tutorials.



Source link

Tags: functionsPythonTipsWriting
Previous Post

Technological Advancements Driving Efficiency and Safety in Fleet Management

Next Post

Jharkhand Election Results 2024: NDA leads in 9 seats, INDIA bloc in five; here is full list of winners

Related Posts

AI Compared: Which Assistant Is the Best?
Data Science & ML

AI Compared: Which Assistant Is the Best?

June 10, 2024
5 Machine Learning Models Explained in 5 Minutes
Data Science & ML

5 Machine Learning Models Explained in 5 Minutes

June 7, 2024
Cohere Picks Enterprise AI Needs Over ‘Abstract Concepts Like AGI’
Data Science & ML

Cohere Picks Enterprise AI Needs Over ‘Abstract Concepts Like AGI’

June 7, 2024
How to Learn Data Analytics – Dataquest
Data Science & ML

How to Learn Data Analytics – Dataquest

June 6, 2024
Adobe Terms Of Service Update Privacy Concerns
Data Science & ML

Adobe Terms Of Service Update Privacy Concerns

June 6, 2024
Build RAG applications using Jina Embeddings v2 on Amazon SageMaker JumpStart
Data Science & ML

Build RAG applications using Jina Embeddings v2 on Amazon SageMaker JumpStart

June 6, 2024
Next Post
Jharkhand Election Results 2024: NDA leads in 9 seats, INDIA bloc in five; here is full list of winners

Jharkhand Election Results 2024: NDA leads in 9 seats, INDIA bloc in five; here is full list of winners

Industry Leaders Embrace NVIDIA Isaac for AI-Powered Robotics Development

Industry Leaders Embrace NVIDIA Isaac for AI-Powered Robotics Development

Understanding KAN: The Latest Alternative to MLP

Understanding KAN: The Latest Alternative to MLP

Leave a Reply Cancel reply

Your email address will not be published. Required fields are marked *

  • Trending
  • Comments
  • Latest
Is C.AI Down? Here Is What To Do Now

Is C.AI Down? Here Is What To Do Now

January 10, 2024
Porfo: Revolutionizing the Crypto Wallet Landscape

Porfo: Revolutionizing the Crypto Wallet Landscape

October 9, 2023
A Complete Guide to BERT with Code | by Bradney Smith | May, 2024

A Complete Guide to BERT with Code | by Bradney Smith | May, 2024

May 19, 2024
A faster, better way to prevent an AI chatbot from giving toxic responses | MIT News

A faster, better way to prevent an AI chatbot from giving toxic responses | MIT News

April 10, 2024
Part 1: ABAP RESTful Application Programming Model (RAP) – Introduction

Part 1: ABAP RESTful Application Programming Model (RAP) – Introduction

November 20, 2023
Saginaw HMI Enclosures and Suspension Arm Systems from AutomationDirect – Library.Automationdirect.com

Saginaw HMI Enclosures and Suspension Arm Systems from AutomationDirect – Library.Automationdirect.com

December 6, 2023
Can You Guess What Percentage Of Their Wealth The Rich Keep In Cash?

Can You Guess What Percentage Of Their Wealth The Rich Keep In Cash?

June 10, 2024
AI Compared: Which Assistant Is the Best?

AI Compared: Which Assistant Is the Best?

June 10, 2024
How insurance companies can use synthetic data to fight bias

How insurance companies can use synthetic data to fight bias

June 10, 2024
5 SLA metrics you should be monitoring

5 SLA metrics you should be monitoring

June 10, 2024
From Low-Level to High-Level Tasks: Scaling Fine-Tuning with the ANDROIDCONTROL Dataset

From Low-Level to High-Level Tasks: Scaling Fine-Tuning with the ANDROIDCONTROL Dataset

June 10, 2024
UGRO Capital: Targeting to hit milestone of Rs 20,000 cr loan book in 8-10 quarters: Shachindra Nath

UGRO Capital: Targeting to hit milestone of Rs 20,000 cr loan book in 8-10 quarters: Shachindra Nath

June 10, 2024
Facebook Twitter LinkedIn Pinterest RSS
News PouroverAI

The latest news and updates about the AI Technology and Latest Tech Updates around the world... PouroverAI keeps you in the loop.

CATEGORIES

  • AI Technology
  • Automation
  • Blockchain
  • Business
  • Cloud & Programming
  • Data Science & ML
  • Digital Marketing
  • Front-Tech
  • Uncategorized

SITEMAP

  • Disclaimer
  • Privacy Policy
  • DMCA
  • Cookie Privacy Policy
  • Terms and Conditions
  • Contact us

Copyright © 2023 PouroverAI News.
PouroverAI News

No Result
View All Result
  • Home
  • AI Tech
  • Business
  • Blockchain
  • Data Science & ML
  • Cloud & Programming
  • Automation
  • Front-Tech
  • Marketing

Copyright © 2023 PouroverAI News.
PouroverAI News

Welcome Back!

Login to your account below

Forgotten Password? Sign Up

Create New Account!

Fill the forms bellow to register

All fields are required. Log In

Retrieve your password

Please enter your username or email address to reset your password.

Log In