ANI

Stop writing Messy Python: The crash course of a clean code


Photo by writer | Ideogram

If you have put codes in the Python for a while, you may well know the foundations, build a few projects. And now you are looking for your code to think: “This works, but … it's not what I can proudly show about the code review. We're all there.

But as you save codes, writing clean code becomes a very important thing as the writing code. In this article, I have integrated practical strategies that can help you travel from “running, and do not be kept. “

🔗 Link to the code in Githubub

1. Model Past is clearly. Don't pass around around

Dictionaries are flexible with python and is the problem. When you pass the green dictionary throughout your code, inviting Typos, key errors, and confusion about which data should be.

Instead of:

def process_user(user_dict):
    if user_dict['status'] == 'active':  # What if 'status' is missing?
        send_email(user_dict['email'])   # What if it's 'mail' in some places?
        
        # Is it 'name', 'full_name', or 'username'? Who knows!
        log_activity(f"Processed {user_dict['name']}")

This code is not strong because it takes dictionary buttons are without verification. Does not provide protection from typos or lined keys, which will create KeyError different at the time to start. None of the documents of what sectors are expected.

Do this:

from dataclasses import dataclass
from typing import Optional

@dataclass
class User:
    id: int
    email: str
    full_name: str
    status: str
    last_login: Optional[datetime] = None

def process_user(user: User):
    if user.status == 'active':
        send_email(user.email)
        log_activity(f"Processed {user.full_name}")

Python's @dataclass Decorations to give a clean, clear of a small boeelplate. Your ide is now able to automatically provide qualifications, and you will find errors immediately if the required fields are lost.

MORE CONCLUSION MORE, Consider the Pydantic:

from pydantic import BaseModel, EmailStr, validator

class User(BaseModel):
    id: int
    email: EmailStr  # Validates email format
    full_name: str
    status: str
    
    @validator('status')
    def status_must_be_valid(cls, v):
        if v not in {'active', 'inactive', 'pending'}:
            raise ValueError('Must be active, inactive or pending')
        return v

Now your data is compatible with it, it holds shortcomes in advance, as well as clear-expected documents.

2. Use ENUMS for known options

String is stretched strings tend to typos and provide exception of EDOCOCOMMPLETE EDO. Confirmation happens only during the start time.

Instead of:

def process_order(order, status):
    if status == 'pending':
        # process logic
    elif status == 'shipped':
        # different logic
    elif status == 'delivered':
        # more logic
    else:
        raise ValueError(f"Invalid status: {status}")
        
# Later in your code...
process_order(order, 'shiped')  # Typo! But no IDE warning

Do this:

from enum import Enum, auto

class OrderStatus(Enum):
    PENDING = 'pending'
    SHIPPED = 'shipped'
    DELIVERED = 'delivered'
    
def process_order(order, status: OrderStatus):
    if status == OrderStatus.PENDING:
        # process logic
    elif status == OrderStatus.SHIPPED:
        # different logic
    elif status == OrderStatus.DELIVERED:
        # more logic
    
# Later in your code...
process_order(order, OrderStatus.SHIPPED)  # IDE autocomplete helps!

If you are facing a fixed set of options, ENUM makes your code stronger and have a test.

With eNums:

  • Your ide is providing automatic suggestions
  • Typos become (approximately) impossible
  • You can in every price possible when required

ENUM creates a collection of combined objects. Icon of type status: OrderStatus Documents type parameter is expected. Use OrderStatus.SHIPPED Instead of a literal thread allows an autoComplete Idel and holds Typos during development.

3. Use only keywords clear

The Python system variables the system is powerful, but it can lead to confusion when Work calls have many options packages.

Instead of:

def create_user(name, email, admin=False, notify=True, temporary=False):
    # Implementation
    
# Later in code...
create_user("John Smith", "[email protected]", True, False)

Wait, what do those booleans mean and?

When called the entrack, it is not clear what the boolean prices represent without regard for the operation. True with treatment, notify, or something else?

Do this:

def create_user(name, email, *, admin=False, notify=True, temporary=False):
    # Implementation

# Now you must use keywords for optional args
create_user("John Smith", "[email protected]", admin=True, notify=False)

*, Syntax will force all conflicts after the keyword. This makes your work calling and preventing the problem of “boolean mystery” when students can determine whether that is true or false by reading the work description.

This method is very useful for APIs calls and more, where you want to confirm the clarification on the call site.

4. Use Pathlib on top of OS.PATH

Os.path Path of Python is active but Clunky. The new pathlib module provides a prioristic manner and has little errors.

Instead of:

import os

data_dir = os.path.join('data', 'processed')
if not os.path.exists(data_dir):
    os.makedirs(data_dir)

filepath = os.path.join(data_dir, 'output.csv')
with open(filepath, 'w') as f:
    f.write('resultsn')
    
# Check if we have a JSON file with the same name
json_path = os.path.splitext(filepath)[0] + '.json'
if os.path.exists(json_path):
    with open(json_path) as f:
        data = json.load(f)

This uses the string of the string with os.path.join() including os.path.splitext() by treating the way. The performance of how to disperse with different activities. Code is Vermase and accurate.

Do this:

from pathlib import Path

data_dir = Path('data') / 'processed'
data_dir.mkdir(parents=True, exist_ok=True)

filepath = data_dir / 'output.csv'
filepath.write_text('resultsn')

# Check if we have a JSON file with the same name
json_path = filepath.with_suffix('.json')
if json_path.exists():
    data = json.loads(json_path.read_text())

Why is pathlib is better:

  • How to join with / more accurate
  • Ways such as mkdir(), exists()beside read_text() are attached to the object object
  • Performance such as transforming extensions (with a_affix) is semantics

Pathlib deals trickling trick How to work in practical programs. This makes your code more affected and strong.

5. Fail quickly with Guard Clauses

The statements are well organized when it is often difficult to understand and save. Using early back – monitor phrases – resulting in a more readable code.

Instead of:

def process_payment(order, user):
    if order.is_valid:
        if user.has_payment_method:
            payment_method = user.get_payment_method()
            if payment_method.has_sufficient_funds(order.total):
                try:
                    payment_method.charge(order.total)
                    order.mark_as_paid()
                    send_receipt(user, order)
                    return True
                except PaymentError as e:
                    log_error(e)
                    return False
            else:
                log_error("Insufficient funds")
                return False
        else:
            log_error("No payment method")
            return False
    else:
        log_error("Invalid order")
        return False

The deep nest is hard to follow. Each conditional block needs to track many branches at the same time.

Do this:

def process_payment(order, user):
    # Guard clauses: check preconditions first
    if not order.is_valid:
        log_error("Invalid order")
        return False
        
    if not user.has_payment_method:
        log_error("No payment method")
        return False
    
    payment_method = user.get_payment_method()
    if not payment_method.has_sufficient_funds(order.total):
        log_error("Insufficient funds")
        return False
    
    # Main logic comes after all validations
    try:
        payment_method.charge(order.total)
        order.mark_as_paid()
        send_receipt(user, order)
        return True
    except PaymentError as e:
        log_error(e)
        return False

Choisers Clashes deals with the errors that are facing earlier, reducing teaching standards. Each situation is checked in a row, making the flow easy to follow. The main logic comes in the end, clearly divided into handling error.

This approach measures very better as your logic grows with difficulty.

6. Don't override to understand

The list storage is one of the best Python things, but they are not readable when they are loaded highly by complex circumstances or changes.

Instead of:

# Hard to parse at a glance
active_premium_emails = [user['email'] for user in users_list 
                         if user['status'] == 'active' and 
                         user['subscription'] == 'premium' and 
                         user['email_verified'] and
                         not user['email'] in blacklisted_domains]

This listing list is very parked in one line. It is difficult to study and to correct an error. Many situations are tied together, making it difficult to understand sorting methods.

Do this:
Here are some better ways.

Option 1: Work with descriptive name

Produce a complex situation to work whose name is a descriptive word. The recognition of the list now is clear, focusing on what you do (to remove e-emails) rather than sort.

def is_valid_premium_user(user):
    return (user['status'] == 'active' and
            user['subscription'] == 'premium' and
            user['email_verified'] and
            not user['email'] in blacklisted_domains)

active_premium_emails = [user['email'] for user in users_list if is_valid_premium_user(user)]

Option 2: Traditional LOOP when Logic is complicated

Uses traditional loop with clarity. Each situation is inserted separately, making it easier to repair the situation that may be failing. Transformation Logic is also clearly divided.

active_premium_emails = []
for user in users_list:
    # Complex filtering logic
    if user['status'] != 'active':
        continue
    if user['subscription'] != 'premium':
        continue
    if not user['email_verified']:
        continue
    if user['email'] in blacklisted_domains:
        continue
        
    # Complex transformation logic
    email = user['email'].lower().strip()
    active_premium_emails.append(email)

Listing stability should make your code more readable, not below. When logic becomes complicated:

  • Break out complex situations in the names of name
  • Consider using regular loop with continuing
  • Divide complex tasks into many steps

Remember, goal is to read.

7. Write the practical pure activities

Work is a pure job when producing the same effect of the same installation. Also, they do not have side effects.

Instead of:

total_price = 0  # Global state

def add_item_price(item_name, quantity):
    global total_price
    # Look up price from global inventory
    price = inventory.get_item_price(item_name)
    # Apply discount 
    if settings.discount_enabled:
        price *= 0.9
    # Update global state
    total_price += price * quantity
    
# Later in code...
add_item_price('widget', 5)
add_item_price('gadget', 3)
print(f"Total: ${total_price:.2f}")

This is using global conditions (total_price) Why Exercise It's Hard.

An employee has side effects (changing the international country) and depends on the external form of (inventory and settings). This makes it unpredictable and difficult to use.

Do this:

def calculate_item_price(item, price, quantity, discount=0):
    """Calculate final price for a quantity of items with optional discount.
    
    Args:
        item: Item identifier (for logging)
        price: Base unit price
        quantity: Number of items
        discount: Discount as decimal 
        
    Returns:
        Final price after discounts
    """
    discounted_price = price * (1 - discount)
    return discounted_price * quantity

def calculate_order_total(items, discount=0):
    """Calculate total price for a collection of items.
    
    Args:
        items: List of (item_name, price, quantity) tuples
        discount: Order-level discount
        
    Returns:
        Total price after all discounts
    """
    return sum(
        calculate_item_price(item, price, quantity, discount)
        for item, price, quantity in items
    )

# Later in code...
order_items = [
    ('widget', inventory.get_item_price('widget'), 5),
    ('gadget', inventory.get_item_price('gadget'), 3),
]

total = calculate_order_total(order_items, 
                             discount=0.1 if settings.discount_enabled else 0)
print(f"Total: ${total:.2f}")

The next version uses pure activities that take all the dependence on like parameters.

8. Write public activity documents and classes

The Scriptures are not (and should not) then. An important part of the final code. Good Doctrings do not specify what tasks do, but why exists and how to use them well.

Instead of:

def celsius_to_fahrenheit(celsius):
    """Convert Celsius to Fahrenheit."""
    return celsius * 9/5 + 32

This is a small docstring repeating the work name only. It provides information about parameters, return prices, or edge cases.
Do this:

def celsius_to_fahrenheit(celsius):
	"""
	Convert temperature from Celsius to Fahrenheit.
	The formula used is: F = C × (9/5) + 32
	Args:
    	celsius: Temperature in degrees Celsius (can be float or int)
	Returns:
    	Temperature converted to degrees Fahrenheit
	Example:
    	>>> celsius_to_fahrenheit(0)
    	32.0
    	>>> celsius_to_fahrenheit(100)
    	212.0
    	>>> celsius_to_fahrenheit(-40)
    	-40.0
	"""
	return celsius * 9/5 + 32

Good Doctring:

  • Parameters of documents and return prices
  • Notes any other different can be awakened
  • Provides examples of use

Your documents act as visible texts that are always synced with your code.

9. Automatically make formatting and formatting

Do not rely on hand tests to catch style stories and regular bugs. Automatic tools can manage a heated work to ensure code and agree.

You can try to set these formatting tools and formatting:

  1. Dark – The format code
  2. Fuffe – Fast Lterter
  3. gotten – Static Type Type Checker
  4. isort – Central Editor

Mix using configuration hooks to automatically check and format code before each commitment:

  1. Enter Pre-Off: Code Style = “Background: # F5f5f5;”> PIP Install Pre-Ourn
  2. Create code style = “Background: # F5f5f5;”>. The File Pre-Commitment.Yaml and Tools
  3. Code Style = “Background: # F5f5f5;”> Previous installation to activate

This setup guarantees the fixed code style and holds errors before the hands of hand.

You can check 7 tools to help write a better Python Code to know more about this.

10. Avoid holding everything out

Generic administrators who removes they hide bugs and make adjustments. They catch everything, including syntax errors, memory errors, and keyboard disorders.

Instead of:

try:
    user_data = get_user_from_api(user_id)
    process_user_data(user_data)
    save_to_database(user_data)
except:
    # What failed? We'll never know!
    logger.error("Something went wrong")

This is a different manner of administration:

  • Syntax errors)
  • System errors (such as Mentererror)
  • Keyboard interferes (CTRL + c)
  • Unexpected Mistakes (such as Network Timeouts)

This makes the defense very difficult, as all errors are treated the same.

Do this:

try:
    user_data = get_user_from_api(user_id)
    process_user_data(user_data)
    save_to_database(user_data)
except ConnectionError as e:
    logger.error(f"API connection failed: {e}")
    # Handle API connection issues
except ValueError as e:
    logger.error(f"Invalid user data received: {e}")
    # Handle validation issues
except DatabaseError as e:
    logger.error(f"Database error: {e}")
    # Handle database issues
except Exception as e:
    # Last resort for unexpected errors
    logger.critical(f"Unexpected error processing user {user_id}: {e}", 
                  exc_info=True)
    # Possibly re-raise or handle generically
    raise

Different holding unpredictable and properly treated. Each different kind consists of its error message and managing a plan.

Lastly without trapping unexpected mistakes, including a full track (exc_info=True), and rehabilitate them to avoid satisfying great news.

If you need to catch all the holdings for some reason, use except Exception as e: Instead of inadequate except:and always enter perfect full details with exc_info=True.

Rolling up

I hope you will use at least some of these practices in your code. Start using them in your projects.

You will find your code more better, more tested, and easy to think about.

Next time you are tempted to take a shortcut, remember: The code reads again and more times than what is written. Clean edible editing?

Count Priya c He is the writer and a technical writer from India. He likes to work in mathematical communication, data science and content creation. His areas of interest and professionals includes deliefs, data science and natural language. She enjoys reading, writing, coding, and coffee! Currently, he works by reading and sharing his knowledge and engineering society by disciples of teaching, how they guide, pieces of ideas, and more. Calculate and create views of the resources and instruction of codes.

Source link

Related Articles

Leave a Reply

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

Back to top button