5 Ideas for Writing Higher Python Features

Date:

Share post:


Picture by Writer

 

All of us write features when coding in Python. However will we essentially write good features? Effectively, let’s discover out.

Features in Python allow you to write modular code. When you may have a process you have to carry out at a number of locations, you’ll be able to wrap the logic of the duty right into a Python operate. And you may name the operate each time you have to carry out that particular process. So simple as it appears to get began with Python features, writing maintainable and performant features isn’t so easy.

And that’s why we’ll discover a number of practices that’ll assist you write cleaner and easy-to-maintain Python features. Let’s get began…

 

1. Write Features That Do Solely One Factor

 

When writing features in Python, it is usually tempting to place all associated duties right into a single operate. Whereas this might help you code issues up shortly, it’ll solely make your code a ache to take care of within the close to future. Not solely will this make understanding what a operate does harder but in addition results in different points reminiscent of too many parameters (extra on that later!).

As an excellent follow, you need to at all times attempt to make your operate do just one factor—one process—and try this nicely. However generally, for a single process, you could must work by means of a collection of subtasks. So how do you determine if and the way the operate ought to be refactored?

Relying on what the operate is making an attempt to do and the way advanced the duty is, you’ll be able to work out the separation of considerations between subtasks. After which establish an acceptable stage at which you’ll be able to refactor the operate into a number of features—every specializing in a selected subtask.

 

refactor-func
Refactor features | Picture by Writer

 

Right here’s an instance. Have a look at the operate analyze_and_report_sales:

# fn. to research gross sales information, calculate gross sales metrics, and write it to a file
def analyze_and_report_sales(information, report_filename):
	total_sales = sum(merchandise['price'] * merchandise['quantity'] for merchandise in information)
	average_sales = total_sales / len(information)
    
	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 is fairly straightforward to see that it may be refactored into two features: one calculating the gross sales metrics and one other on writing the gross sales metrics to a file like so:

# refactored into two funcs: one to calculate metrics and one other to put in writing gross sales report
def calculate_sales_metrics(information):
	total_sales = sum(merchandise['price'] * merchandise['quantity'] for merchandise in information)
	average_sales = total_sales / len(information)
	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 simpler to debug any considerations with the calculation of gross sales metrics and file operations individually. And right here’s a pattern operate name:

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

 

You need to be capable of see the ‘sales_report.txt’ file in your working listing with the gross sales metrics. This can be a easy instance to get began, however that is useful particularly once you’re engaged on extra advanced features.

 

2. Add Kind Hints to Enhance Maintainability

 

Python is a dynamically typed language. So you do not want to declare varieties for the variables you create. However you’ll be able to add kind hints to specify the anticipated information kind for variables. Whenever you outline the operate, you’ll be able to add the anticipated information varieties for the parameters and the return values.

As a result of Python doesn’t implement varieties at runtime, including kind hints has no impact at runtime. However there nonetheless are advantages to utilizing kind hints, particularly on the maintainability entrance:

  • Including kind hints to Python features serves as inline documentation and offers a greater thought of what the operate does and what values it consumes and returns.
  • Whenever you add kind hints to your features, you’ll be able to configure your IDE to leverage these kind hints. So that you’ll get useful warnings should you attempt to cross an argument of invalid kind in a number of operate calls, implement features whose return values don’t match the anticipated kind, and the like. So you’ll be able to decrease errors upfront.
  • You may optionally use static kind checkers like mypy to catch errors earlier quite than letting kind mismatches introduce delicate bugs which might be troublesome to debug.

Right here’s a operate that processes order particulars:

# fn. to course of 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 kind hints to the operate like so:

# modified with kind hints
from typing import Listing, Dict

def process_orders(orders: Listing[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 model, you get to know that the operate takes in an inventory of dictionaries. The keys of the dictionary ought to all be strings and the values can both be integers or floating level values. The operate additionally returns a dictionary. Let’s take a pattern operate name:

# Pattern information
orders = [
	{'price': 100.0, 'quantity': 2},
	{'price': 50.0, 'quantity': 5},
	{'price': 150.0, 'quantity': 1}
]

# Pattern operate name
consequence = process_orders(orders)
print(consequence)

 

This is the output:

{'total_quantity': 8, 'total_value': 600.0}

 

On this instance, kind hints assist us get a greater thought of how the operate works. Going ahead, we’ll add kind hints for all the higher variations of Python features we write.

 

3. Settle for Solely the Arguments You Truly Want

 

If you’re a newbie or have simply began your first dev function, it’s vital to consider the totally different parameters when defining the operate signature. It is fairly widespread to introduce extra parameters within the operate signature that the operate by no means truly processes.

Guaranteeing that the operate takes in solely the arguments which might be truly vital retains operate calls cleaner and extra maintainable basically. On a associated notice, too many parameters within the operate signature additionally make it a ache to take care of. So how do you go about defining easy-to-maintain features with the proper variety of parameters?

If you end up writing a operate signature with a rising variety of parameters, step one is to take away all unused parameters from the signature. If there are too many parameters even after this step, return to tip #1: break down the duty into a number of subtasks and refactor the operate into a number of smaller features. This can assist preserve the variety of parameters in verify.

 

num-params
Maintain num_params in verify | Picture by Writer

 

It’s time for a easy instance. Right here the operate definition to calculate scholar grades accommodates the teacher parameter that’s by no means used:

# takes in an arg that is by no means used!
def process_student_grades(student_id, grades, course_name, teacher'):
	average_grade = sum(grades) / len(grades)
	return f"Student {student_id} achieved an average grade of {average_grade:.2f} in {course_name}."


 

You may rewrite the operate with out the teacher parameter like so:

# higher model!
def process_student_grades(student_id: int, grades: listing, 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}."

# Utilization
student_id = 12345
grades = [85, 90, 75, 88, 92]
course_name = "Mathematics"
consequence = process_student_grades(student_id, grades, course_name)
print(consequence)

 

This is the output of the operate name:

Pupil 12345 achieved a median grade of 86.00 in Arithmetic.

 

 

4. Implement Key phrase-Solely Arguments to Decrease Errors

 

In follow, most Python features soak up a number of arguments. You may cross in arguments to Python features as positional arguments, key phrase arguments, or a mixture of each. Learn Python Operate Arguments: A Definitive Information for a fast overview of operate arguments.

Some arguments are naturally positional. However generally having operate calls containing solely positional arguments may be complicated. That is very true when the operate takes in a number of arguments of the identical information kind, some required and a few non-compulsory.

In the event you recall, with positional arguments, the arguments are handed to the parameters within the operate signature within the identical order through which they seem within the operate name. So change so as of arguments can introduce delicate bugs kind errors.

It’s usually useful to make non-compulsory arguments keyword-only. This additionally makes including non-compulsory parameters a lot simpler—with out breaking present calls.

Right here’s an instance. The process_payment operate takes in an non-compulsory description string:

# instance fn. for processing transaction
def process_payment(transaction_id: int, quantity: float, foreign money: str, description: str = None):
	print(f"Processing transaction {transaction_id}...")
	print(f"Amount: {amount} {currency}")
	if description:
    		print(f"Description: {description}")

 

Say you wish to make the non-compulsory description a keyword-only argument. Right here’s how you are able to do it:

# implement keyword-only arguments to reduce errors
# make the non-compulsory `description` arg keyword-only
def process_payment(transaction_id: int, quantity: float, foreign money: 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 pattern operate name:

process_payment(1234, 100.0, 'USD', description='Fee for companies')

 

This outputs:

Processing transaction 1234...
Quantity: 100.0 USD
Description: Fee for companies

 

Now strive passing in all arguments as positional:

# throws error as we attempt to cross in additional positional args than allowed!
process_payment(5678, 150.0, 'EUR', 'Bill cost') 

 

You’ll get an error as proven:

Traceback (most up-to-date name final):
  File "/home/balapriya/better-fns/tip4.py", line 9, in 
	process_payment(1234, 150.0, 'EUR', 'Bill cost')
TypeError: process_payment() takes 3 positional arguments however 4 got

 

5. Don’t Return Lists From Features; Use Turbines As an alternative

 

It is fairly widespread to put in writing Python features that generate sequences reminiscent of an inventory of values. However as a lot as doable, you need to keep away from returning lists from Python features. As an alternative you’ll be able to rewrite them as generator features. Turbines use lazy analysis; in order that they yield components of the sequence on demand quite than computing all of the values forward of time. Learn Getting Began with Python Turbines for an introduction to how mills work in Python.

For instance, take the next operate that generates the Fibonacci sequence as much as a sure higher restrict:

# returns an inventory of Fibonacci numbers
def generate_fibonacci_numbers_list(restrict):
	fibonacci_numbers = [0, 1]
	whereas fibonacci_numbers[-1] + fibonacci_numbers[-2] 

 

It’s a recursive implementation that’s computationally costly and populating the listing and returning it appears extra verbose than vital. Right here’s an improved model of the operate that makes use of mills:

# use mills as a substitute
from typing import Generator

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

 

On this case, the operate returns a generator object which you’ll be able to then loop by means of to get the weather of the sequence:

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

 

Right here’s the output:

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

 

As you’ll be able to see, utilizing mills may be way more environment friendly particularly for big enter sizes. Additionally, you’ll be able to chain a number of mills collectively, so you’ll be able to create environment friendly information processing pipelines with mills.

 

Wrapping Up

 

And that’s a wrap. Yow will discover all of the code on GitHub. Right here’s a overview of the totally different ideas we went over:

  • Write features that do just one factor
  • Add kind hints to enhance maintainability
  • Settle for solely the arguments you really need
  • Implement keyword-only arguments to reduce errors
  • Do not return lists from features; use mills as a substitute

I hope you discovered them useful! In the event you aren’t already, check out these practices when writing Python features. Blissful coding!
 
 

Bala Priya C is a developer and technical author from India. She likes working on the intersection of math, programming, information science, and content material creation. Her areas of curiosity and experience embody DevOps, information science, and pure language processing. She enjoys studying, writing, coding, and occasional! Presently, she’s engaged on studying and sharing her data with the developer neighborhood by authoring tutorials, how-to guides, opinion items, and extra. Bala additionally creates partaking useful resource overviews and coding tutorials.

Related articles

AI Job Affect: Robots vs. Human Potential

Let’s be actual: Synthetic intelligence (AI) is all over the place, and its job impression is altering how...

Can AI Assist You Get Wealthy Fast

Ever puzzled if AI might make it easier to earn a living quick? You’re not alone! “Ask ChatGPT”...

Discover Low cost Vacation Flights & Save

Think about this: You’re all settled in for the night, your thoughts wandering to the considered a comfortable...

LLM-as-a-Decide: A Scalable Answer for Evaluating Language Fashions Utilizing Language Fashions

The LLM-as-a-Decide framework is a scalable, automated various to human evaluations, which are sometimes pricey, gradual, and restricted...