Author: Amber Colvin

  • How to Check for an Empty Object in JavaScript

    How to Check for an Empty Object in JavaScript

    To check for an empty object in JavaScript, first define empty as “no own enumerable string-keyed properties.” For a value already known to be a plain object, Object.keys(obj).length === 0 is the direct test. To safely check an empty object in JavaScript when input may be null or another type, add guards before calling Object.keys().

    A JavaScript empty object can still have inherited, symbol, or non-enumerable properties, so the result depends on which properties your application considers.

    How to check for an empty object in JavaScript with Object.keys(obj).length === 0

    Object.keys() returns an array containing an object’s own enumerable string keys. An object literal with no such keys produces an empty array:

    const value = {};
    Object.keys(value).length === 0; // true

    An object with an own enumerable property produces a nonzero length:

    const value = { name: “Ada” };
    Object.keys(value).length === 0; // false

    When the input is expected to be an object but may be null, use a type and null guard:

    const isEmpty = value =>
      value !== null &&
      typeof value === “object” &&
      Object.keys(value).length === 0;

    This condition also returns true for arrays, dates, maps, sets, and some class instances that have no enumerable own string properties. Use the stricter plain-object check below when the value’s type is not already controlled.

    Guard an empty JavaScript object check against null, arrays, Map, Set, Date, and class instances

    typeof null is “object”, but Object.keys(null) throws a TypeError. Check for null before the type test. Arrays also have object types, so a separate array check or a plain-object prototype check is needed.

    This function accepts object literals and objects created with a null prototype, while rejecting arrays and built-in or custom instance types:

    const isEmptyPlainObject = value =>
      value !== null &&
      typeof value === “object” &&
      (Object.getPrototypeOf(value) === Object.prototype ||
       Object.getPrototypeOf(value) === null) &&
      Object.keys(value).length === 0;

    An array should usually be tested with value.length === 0. A Map and a Set store data in internal collection slots, so use map.size === 0 or set.size === 0. A Date is a date value rather than an empty record. A class instance may have no own fields while still representing a meaningful domain object; test its required fields or use an instanceof check before applying an emptiness rule.

    What does a JavaScript empty object mean for own, inherited, symbol, and non-enumerable properties?

    Object.keys() counts only own, enumerable, string-named properties. Each category changes the result:

    • Own properties: properties stored directly on the object. Enumerable own properties appear in Object.keys().
    • Inherited properties: properties found on the object’s prototype. They do not appear in Object.keys(), and this is normally desirable for configuration or data objects.
    • Symbol properties: own symbol-keyed properties are not returned by Object.keys(), even when they are enumerable.
    • Non-enumerable properties: own string or symbol properties marked non-enumerable are also excluded.

    For example, an object can have hidden data while appearing empty to Object.keys():

    const value = {};
    Object.defineProperty(value, “id”, { value: 7 });
    Object.keys(value).length === 0; // true

    To count all own string and symbol keys, including non-enumerable keys, use Reflect.ownKeys(value).length === 0. Object.getOwnPropertyNames() includes all own string keys, while Object.getOwnPropertySymbols() returns own symbol keys. A for…in loop is not equivalent to Object.keys() because it can include inherited enumerable properties.

    Choose a definition of emptiness when checking whether an object is empty in JavaScript

    • For a plain data object, define empty as having no own enumerable string keys and use the guarded Object.keys(value).length === 0 test.
    • For an object where symbols or non-enumerable metadata matter, use Reflect.ownKeys(value).length === 0 after validating the value’s type.
    • For collections, use their native size or length: Map.size, Set.size, or array length.
    • For inherited settings, decide explicitly whether the prototype contributes data. Avoid treating inherited properties as own record fields by accident.

    Do not use JSON.stringify(value) === “{}” as a universal emptiness test. Serialization omits symbols, non-enumerable properties, and several values that do not serialize as ordinary object keys. It also says nothing about whether the value is a plain object, so property inspection and type validation are more reliable.

  • JavaScript Not-Equal Operators: !== vs !=

    JavaScript Not-Equal Operators: !== vs !=

    Use !== for most not-equal operators in JavaScript. It performs a strict comparison, so values must have the same type and value to be considered equal. Use != only when you deliberately want JavaScript to convert values before comparing them.

    These operators can produce different results for the same pair. Choosing the right one prevents unexpected matches between numbers, strings, booleans, and nullish values.

    JavaScript Not-Equal Operators: Choose !== or !=

    !== is the strict inequality operator. It is the negation of strict equality: a !== b produces the same result as !(a === b). It returns true when the operands have different types or different values.

    != is the loose inequality operator. It is the negation of loose equality: a != b produces the same result as !(a == b). Before deciding whether values differ, JavaScript may convert one operand to another type.

    For predictable JavaScript not-equal comparisons, prefer !==. Reserve != for cases where that conversion is an intentional part of the condition.

    How does JavaScript strict inequality with !== work?

    Strict inequality checks type and value without coercion. Two numbers with different values are not equal, and a number is not strictly equal to a string containing the same digits.

    • 7 !== 4 is true because the number values differ.
    • 7 !== “7” is true because one operand is a number and the other is a string.
    • “ready” !== “ready” is false because both operands are the same string.
    • null !== undefined is true because they are different types.

    Strict inequality also follows JavaScript’s strict equality rules for special numeric values. In particular, NaN !== NaN is true, while 0 !== -0 is false.

    How does JavaScript not-equal comparison with != handle type coercion?

    Loose inequality applies the abstract equality rules before returning the opposite result. A numeric string can be converted to a number, and boolean values can be converted to numbers. This means operands that look different in source code may compare as equal.

    • 7 != “7” is false. The string is converted to the number 7.
    • 0 != false is false. The boolean is converted to 0.
    • “” != 0 is false. The empty string is converted to 0.
    • null != undefined is false. These two values are treated as equal by loose equality.

    Coercion can be useful when an input is expected to contain a numeric or boolean representation. It can also hide invalid data, so a loose condition should document or clearly reflect that intent.

    Which results change for numbers, strings, null, and NaN?

    Which value pairs make != and !== disagree?

    The operators disagree whenever loose equality converts the operands into matching values or applies its special nullish rule.

    • 5 and “5”: 5 != “5” is false, but 5 !== “5” is true.
    • 0 and false: 0 != false is false, but 0 !== false is true.
    • null and undefined: null != undefined is false, but null !== undefined is true.

    For ordinary unequal values such as 3 and 8, both operators return true. The difference appears when types or special values affect the comparison.

    Why does NaN need an explicit check?

    NaN means “Not-a-Number,” but it is still a number value according to typeof. It is not equal to itself, so both NaN != NaN and NaN !== NaN return true. Testing whether two values differ does not identify NaN specifically.

    Use Number.isNaN(value) for an explicit check. Unlike the global isNaN() function, Number.isNaN() does not first coerce unrelated values such as strings.

    How do you write clear JavaScript not-equal checks?

    • Use value !== expected when both type and value must match.
    • Use typeof value !== “string” when rejecting values of a particular type.
    • Use value !== null when only the literal null should be excluded.
    • Use value != null only when intentionally treating both null and undefined as missing.
    • Convert input explicitly when conversion is required, then compare strictly: Number(input) !== 10.

    In most not-equal checks in JS, !== communicates the intended rule directly and avoids implicit conversion. Choose != only when its coercive behavior is known, tested, and desirable.

  • JavaScript filter: Array.filter() With Practical Examples

    JavaScript filter: Array.filter() With Practical Examples

    JavaScript filter keeps the elements in an array that satisfy a condition. Its main method, Array.filter(), calls a callback for each existing element and returns a new array containing every element whose callback result is truthy.

    Use it for collections with zero, one, or many matches. The original array remains unchanged, so the method is suitable for creating filtered views of data without removing elements from the source.

    How JavaScript filter works

    The JavaScript filter function receives up to three callback arguments: the current element, its index, and the complete array. Most callbacks use only the element:

    Example: const numbers = [3, 8, 12, 15]; const multiples = numbers.filter((element) => element % 3 === 0);

    The returned value is [3, 12, 15]. To access all callback arguments, use this form:

    Example: const firstHalf = numbers.filter((element, index, array) => index < array.length / 2 && element > 5);

    A callback does not need to return the words true or false. Any truthy result keeps the element; false, 0, “”, null, and undefined exclude it. Returning a boolean expression makes the intended rule clearest.

    filter() does not mutate the original array. It creates a new array, although object elements inside it still reference the same objects. If no elements pass the condition, the result is an empty array: [].

    How does the JavaScript filter function select numbers and strings?

    For numbers, write a predicate that describes the allowed value. This example keeps values at least 10:

    Example: const scores = [7, 10, 14, 18]; const passing = scores.filter(score => score >= 10);

    The result is [10, 14, 18], while scores is unchanged. The same pattern works with strings. Combine typeof with a text condition when an array may contain different data types:

    Example: const values = [“JavaScript”, 42, “filter”, null]; const words = values.filter(value => typeof value === “string” && value.length > 5);

    This returns [“JavaScript”, “filter”]. The filter function JavaScript developers use can therefore select by type, length, range, pattern, or any other callback condition.

    How JavaScript Array.filter() handles objects, missing values, and multiple conditions

    For objects, inspect a property inside the callback. This example keeps active users:

    Example: const users = [{ name: “Ana”, active: true }, { name: “Bo”, active: false }, { name: “Cy”, active: true }]; const activeUsers = users.filter(user => user.active === true);

    The result contains Ana and Cy. A missing property evaluates to undefined, so an explicit comparison such as user.active === true safely excludes an object without an active property.

    Filter out missing or invalid values by checking the value before using it:

    Example: const values = [10, undefined, 25, null, 40]; const validNumbers = values.filter(value => typeof value === “number”);

    The result is [10, 25, 40]. For a sparse array with genuinely empty slots, filter() skips those unassigned slots rather than calling the callback for them.

    Use logical operators for compound conditions. This example keeps in-stock products costing no more than 50:

    Example: const products = [{ name: “Pen”, price: 3, inStock: true }, { name: “Bag”, price: 45, inStock: false }, { name: “Lamp”, price: 38, inStock: true }]; const affordable = products.filter(product => product.inStock && product.price <= 50);

    The && operator requires both conditions to pass. Use || when either condition is acceptable, such as selecting products in the “office” or “school” category.

    When should you use the filter function in JavaScript instead of find or map?

    Choose filter() when the required result is an array containing every match. Its result always has array shape, including an empty array when nothing matches.

    Choose find() when you need one element: specifically, the first match. It returns that element or undefined, not an array. Do not use filter() when a single first match is the required result.

    Choose map() when every element should produce a transformed value. It normally returns an array with the same number of elements, whereas filter() removes elements that fail its predicate.

  • How to Exit a Python Script: Graceful Ways to Stop It

    How to Exit a Python Script: Graceful Ways to Stop It

    To exit Python script execution, choose the least forceful option that matches the situation. Let the script reach the end for normal completion, use return or break for local control flow, and use sys.exit() when the whole process must report a deliberate status. Reserve subprocess termination for a separate process that cannot finish cooperatively.

    These choices are not interchangeable: returning from a function does not stop the interpreter, while killing a process does not provide normal function cleanup.

    How do you exit a Python script normally?

    A Python script exits normally when its top-level code reaches the end. The interpreter then closes, and the operating system usually receives exit code 0. This is the cleanest way to close a Python program after all required work succeeds.

    Put reusable work in a function and return from that function when its job is complete. A return value goes to the caller; it does not automatically exit the entire script. At the top level, reaching the end is equivalent to completing normally, but a top-level return is not valid Python syntax.

    Use a context manager for resources that need predictable cleanup. For example, with open(“output.txt”, “w”) as file: closes the file when the block ends, including when an exception interrupts it. For custom cleanup, place it in a finally block:

    try: perform_work()
    finally: release_resource()

    How do sys.exit() and SystemExit set exit codes while cleanup runs?

    Call sys.exit() when a function needs to stop the whole interpreter intentionally. It raises the SystemExit exception, allowing Python to run active finally blocks and context-manager cleanup while unwinding the stack.

    sys.exit(0) indicates success. A nonzero integer, such as sys.exit(2), signals an error or another meaningful failure state to the shell, scheduler, or calling process. Passing a string prints that message and normally produces a nonzero exit status.

    SystemExit is an exception, but it inherits directly from BaseException rather than Exception. Code that catches SystemExit explicitly can prevent the process from ending, so only intercept it when an embedding application genuinely needs that behavior. Cleanup still belongs in finally or a context manager, not after a call that may exit.

    How do return, break, Ctrl+C, and KeyboardInterrupt affect how you close a Python program?

    • return leaves the current function and gives a value to its caller. It does not terminate the script unless the caller uses that result to end execution.
    • break leaves the nearest loop only. Execution continues with the first statement after that loop.
    • Ctrl+C sends an interrupt from the terminal. Python normally represents it as KeyboardInterrupt in the main thread.
    • KeyboardInterrupt can be handled to log a message, save state, or perform an orderly shutdown. Without a handler, Python stops with an interrupt traceback and a nonzero status.

    Handle interruption around the operation that needs protection: try the work, except KeyboardInterrupt to choose a response, and use finally for cleanup. Do not use break when an exception or process-wide exit is required.

    How do you terminate or kill a subprocess, and when might you kill a Python program?

    For a child process created with subprocess.Popen, call process.terminate() first. It requests termination and may allow the child to handle the signal and release resources. Then call process.wait() and inspect process.returncode to verify the final state.

    If the child ignores termination or exceeds a shutdown timeout, call process.kill(), then call wait() again. On Unix-like systems, terminate commonly sends SIGTERM and kill sends SIGKILL; platform behavior differs, but kill is the forceful option. It can prevent application-level cleanup.

    Use kill a Python program only when cooperative shutdown has failed or the process is unsafe or irreparably stuck. A parent process can check process.poll() before and after termination: None means the child is still running, while a numeric return code confirms that it has exited.

  • Python file existence: check paths with pathlib and os.path

    Python file existence: check paths with pathlib and os.path

    A Python file exists check is easiest with pathlib. To check if file exists in Python, create a Path object and call exists(). This works with both relative and absolute paths.

    Use a pre-check when you need to choose a response for a missing path. When you immediately need to open the file, catch FileNotFoundError as well, because a path can change after the check.

    Python file existence: check a path with pathlib

    Import Path, then call exists() on the path you want to test:

    from pathlib import Path

    relative_path = Path(“reports/today.txt”)

    absolute_path = Path(“/var/data/reports/today.txt”)

    if relative_path.exists():

        print(“The path exists”)

    Path(“reports/today.txt”) is a relative path. Python resolves it against the process’s current working directory, which may differ from the directory containing your script. Use Path.cwd() to see that directory:

    print(Path.cwd())

    An absolute path identifies its location from the filesystem root, such as /var/data/reports/today.txt on Linux and macOS. Windows paths can use a raw string such as Path(r”C:\data\reports\today.txt”).

    How do you distinguish files and directories?

    exists() returns True for both files and directories. Use is_file() when the path must identify a regular file, and is_dir() when it must identify a directory:

    path = Path(“reports/today.txt”)

    if path.is_file():

        print(“A file is available”)

    elif path.is_dir():

        print(“A directory is available”)

    else:

        print(“The path is missing”)

    For a missing path, all three methods return False. A directory makes exists() and is_dir() true, but is_file() false. A file produces the opposite file-versus-directory result.

    What are the os.path equivalents?

    The older os.path functions provide the same basic tests and accept string paths:

    import os

    path = “reports/today.txt”

    os.path.exists(path)

    os.path.isfile(path)

    os.path.isdir(path)

    Use os.path.exists() for either type, os.path.isfile() for a file, and os.path.isdir() for a directory. os.path.abspath(path) converts a relative path to an absolute string. For new code, pathlib usually keeps path construction and file operations more readable.

    What if a file disappears after the check?

    An existence check is not a guarantee that the path will still exist when you use it. Another process can delete, replace, or rename the file between exists() and open(). This check-then-use race can also occur if a directory in the path changes.

    For actual file access, open the path and handle the failure directly. This is how to check if a file exists while safely attempting to read it:

    try:

        with path.open(“r”, encoding=”utf-8″) as file:

            contents = file.read()

    except FileNotFoundError:

        contents = “”

    Use exists() for validation or user-facing messages, but rely on the exception when opening the file is the operation that matters.

  • Python dict to JSON: Serialize Text or a File

    Python dict to JSON: Serialize Text or a File

    For a Python dict to JSON conversion, use json.dumps() when you need JSON text in memory. Use json.dump() when you need to write the dictionary directly to a file. Both functions serialize Python data, but they produce different outputs.

    To convert a dict to JSON in Python, import the standard-library json module and pass your dictionary to the appropriate function. JSON uses double-quoted strings and standardized values, so str() or repr() is not a substitute for serialization.

    Python dict to JSON with json.dumps(): Get JSON Text

    json.dumps() returns a JSON-formatted Python string. It does not create a file or modify the original dictionary.

    import json
    data = {“name”: “Ada”, “active”: True, “roles”: [“admin”, “editor”]}
    json_text = json.dumps(data)

    The value of json_text is a string such as {“name”: “Ada”, “active”: true, “roles”: [“admin”, “editor”]}. Notice that Python’s True becomes JSON’s true. Use this approach when sending a payload through an API, storing serialized text in a database, or passing JSON to another component.

    For readable output, add formatting options directly to the call:

    json_text = json.dumps(data, indent=2)

    The return type remains str, whether the output is compact or indented.

    Write a dict to a JSON file with json.dump() using UTF-8 encoding

    json.dump() writes JSON to an open, file-like object and returns None. Open the file with write mode and explicit UTF-8 encoding:

    with open(“data.json”, “w”, encoding=”utf-8″) as file:
        json.dump(data, file, indent=2, ensure_ascii=False)

    This creates or replaces data.json. The file contains JSON, while data remains a Python dictionary in memory. The with statement closes the file even if an error occurs. Unlike json.dumps(), json.dump() does not return the serialized document as a string.

    Use encoding=”utf-8″ for predictable handling of names, labels, and other non-ASCII characters. The encoding controls how Python writes characters to the file; it is separate from JSON’s formatting options.

    Format dict to JSON in Python with indent, Unicode, and sorted keys

    • indent=2 adds two spaces per nesting level. Use indent=4 for more spacing, or omit it for compact output.
    • ensure_ascii=False keeps characters such as é and 東京 readable. The default, True, escapes them with Unicode sequences such as \u00e9.
    • sort_keys=True writes object keys in alphabetical order. This makes generated files easier to compare and produces stable output for tests.

    For a consistently formatted JSON string, combine the options: json.dumps(data, indent=2, ensure_ascii=False, sort_keys=True). Key ordering affects presentation, not the meaning of a JSON object.

    Convert a Python dict to JSON with supported types and a default handler

    The built-in encoder supports dictionaries, lists, tuples, strings, integers, floating-point numbers, booleans, and None. These become JSON objects, arrays, strings, numbers, true, false, and null. JSON object keys should be strings; Python also accepts some simple non-string keys and converts them to strings.

    Values such as sets, bytes, dates, decimals, and most custom objects are not serializable by default. The encoder raises TypeError when it encounters one. Supply a default function that converts a known type into a JSON-supported value:

    from datetime import date, datetime
    def encode_value(value):
        if isinstance(value, (date, datetime)):
            return value.isoformat()
        raise TypeError(f”Unsupported type: {type(value).__name__}”)

    Then pass it to either serializer: json.dumps(data, default=encode_value) or json.dump(data, file, default=encode_value). This strategy stores dates as ISO 8601 strings and fails clearly for types you have not deliberately mapped.

  • How to Print Variables in Python: A Practical Guide

    How to Print Variables in Python: A Practical Guide

    To learn how to print variables in Python, start with the built-in print() function. It displays a value in your program’s output, adds a newline by default, and can show strings, numbers, lists, and other objects.

    The basic way to print a variable in Python is to pass its name to print(). Python evaluates the variable and displays its current value.

    How do you print a variable in Python?

    Assign a value to a variable, then provide that variable as the argument to print():

    name = ‘Mina’; print(name)

    Output: Mina

    This works with numeric values as well:

    age = 29; print(age)

    Output: 29

    When the variable contains a string, print() displays the characters without quotation marks. For numbers, it displays the numeric value directly. You do not need to convert either value before passing it to print().

    How do you print labels and several values?

    Pass several arguments to print(), separated by commas. Python converts each value for display and places a space between arguments by default:

    name = ‘Mina’; age = 29; print(‘Name:’, name, ‘Age:’, age)

    Output: Name: Mina Age: 29

    This approach is useful for quick status messages because it safely displays text and non-string values together. It also avoids trying to combine text and numbers with an incompatible string operation.

    Use the sep parameter to replace the default space between arguments:

    print(‘2025′, ’03’, ’08’, sep=’-‘)

    Output: 2025-03-08

    The end parameter controls what follows the printed value. Its default is a newline. Set it to another string when output should continue on the same line:

    print(‘Loading’, end=’…’); print(‘done’)

    Output: Loading…done

    How does printing variables in Python work with f-strings?

    F-strings place variable values inside a text template. Add the letter f before the opening quote, then put each variable or expression inside braces:

    name = ‘Mina’; score = 94; print(f'{name} scored {score} points.’)

    Output: Mina scored 94 points.

    F-strings are usually the clearest choice when a message contains labels and several values. They also support conversion and formatting specifiers. Add a colon inside the braces to format a number:

    price = 19.5; print(f’Price: ${price:.2f}’)

    Output: Price: $19.50

    Here, :.2f displays a floating-point number with two digits after the decimal point. Use a comma to group large numbers:

    total = 1234567; print(f'{total:,}’)

    Output: 1,234,567

    Expressions can also appear inside an f-string:

    items = 3; price = 4.5; print(f’Total: ${items * price:.2f}’)

    Output: Total: $13.50

    How do str and repr help when debugging?

    For ordinary output, print() uses a value’s human-readable string form, called str. This keeps displayed text easy to read. The repr form is designed for debugging and aims to reveal the value more precisely.

    The difference is especially visible with strings containing escape characters:

    message = ‘A\nB’; print(message)

    This displays A and B on separate lines. By contrast:

    print(repr(message))

    Output: ‘A\nB’

    The repr() result shows quotation marks and the literal escape sequence, making hidden characters easier to identify. It is also useful for distinguishing a string from a number or spotting extra spaces.

    Use the !r conversion inside an f-string when you need that debug-friendly representation:

    print(f’message={message!r}’)

    Output: message=’A\nB’

    Use !s when you explicitly want the normal string form. For routine messages, use standard interpolation or comma-separated arguments; reserve repr() and !r for inspecting values and diagnosing unexpected output.

  • Python __init__.py: What Does It Do?

    Python __init__.py: What Does It Do?

    In Python, __init__.py is the package’s initialization module. A directory containing it is recognized as a regular package, and the file can be empty. If you are asking what is __init__.py, it is the place where a package can define setup code, metadata, and convenient package-level names.

    What does __init__.py do? Python runs its top-level code when the package is imported. It also lets a package expose a smaller, clearer public API instead of making users import every name from an internal module.

    How Python __init__.py Defines Package Behavior

    A regular package is a directory that normally contains __init__.py. The file tells Python to treat that directory as one package and gives the package a module body that can configure its behavior. It is unrelated to a class’s __init__ method, which initializes an object instance.

    For example, this small package tree contains a pricing module and a nested web package:

    • shop/
      • __init__.py
      • pricing.py
      • web/
        • __init__.py
        • routes.py

    With this structure, users can write import shop, from shop import pricing, or from shop.web.routes import home, assuming the corresponding names exist. An empty initializer is enough when the package only needs this structure and does not need package-level setup.

    What Is __init__.py? Package Discovery and Import Timing

    When Python evaluates import shop.web.routes, it loads shop/__init__.py first, then shop/web/__init__.py, and finally routes.py. Each module’s top-level statements execute as that module is loaded. Python normally caches the resulting modules in sys.modules, so a second import in the same process does not repeat ordinary initialization.

    This timing makes imports inside an initializer useful for setup and composition. For example, shop/__init__.py might contain from .pricing import TaxRule. Importing shop then loads pricing.py and binds TaxRule on the package object. Code elsewhere can use from shop import TaxRule without knowing where the class is implemented.

    Initialization code also runs when a user imports a submodule, because Python must initialize each parent package first. An exception raised in __init__.py causes the package import to fail. Importing the package alone does not automatically execute every other module in its directory; those modules run only when imported directly or by the initializer.

    What Does __init__.py Do for Re-Exports and __all__?

    A re-export imports a name from an internal module and makes it available through the package’s top-level namespace. A concise initializer might contain:

    from .pricing import TaxRule
    from .pricing import calculate_total

    Users can then write from shop import TaxRule, calculate_total. This creates a stable public entry point even if the implementation later moves from pricing.py to another module. Re-export only names that belong to the supported API; importing too much can increase startup time and create circular-import problems.

    The special variable __all__ declares names for wildcard imports such as from shop import *. For the example above, an initializer could define __all__ = [“TaxRule”, “calculate_total”]. The listed names should be available in the package namespace, usually through imports in the same file.

    __all__ does not prevent explicit imports and is not a security boundary. It documents the intended wildcard surface and controls which names that form imports. An initializer can also define metadata such as __version__, package-level constants, or a small configuration value.

    When Is an Empty __init__.py Enough?

    Leave the file empty when the package needs only a regular package boundary and direct submodule imports. This choice avoids unnecessary work during every package import and reduces the risk of circular imports or surprising side effects. Add code only when package-level re-exports, metadata, registration, or lightweight setup provides a clear benefit.

    Not every Python package needs an __init__.py file. Since Python 3.3, namespace packages can be formed from directories without an initializer. Their portions can be distributed across multiple directories or installed distributions and combined under one package name. Because there is no initializer, there is also no package-specific file in which to run initialization code or define re-exports.

    Use a regular package when you want explicit initialization and a controlled top-level API. Use an empty initializer when that boundary is useful but no setup is needed. Avoid network calls, expensive I/O, and unrelated application work in __init__.py; imports should remain predictable and lightweight.

  • Python Web Server: Run a Simple HTTP Server Locally

    Python Web Server: Run a Simple HTTP Server Locally

    Run a Python web server from the directory you want to share with this command:

    python -m http.server

    Python starts a simple HTTP server on port 8000. Open http://localhost:8000/ in a browser to view the directory contents. On systems where the launcher is named differently, use python3 -m http.server on macOS or Linux, or py -m http.server on Windows.

    Start a Python web server with python -m http.server (or python3/py)

    The command serves files from your current working directory: the folder selected in the terminal when you start the process. For example, to serve a site stored in a folder named website:

    cd website

    python -m http.server

    Then visit http://localhost:8000/. If the directory contains an index.html file, the server displays it as the default page. Otherwise, the built-in handler shows a directory listing that lets you open individual files.

    This simple HTTP server with Python is useful for previewing static HTML, CSS, JavaScript, images, and other files on your own computer. It does not execute server-side Python, PHP, or JavaScript application code.

    Choose the directory, port, and bind address with –directory and –bind

    Use –directory to serve a folder without changing the terminal’s working directory:

    python -m http.server –directory ./website

    A relative path starts from the current working directory. You can also provide an absolute path. Combine it with a port number when 8000 is already in use:

    python -m http.server 9000 –directory ./website

    Open http://localhost:9000/ for that server. A port conflict means another process is already listening on the selected port. Stop that process or choose another unused port, such as 9000 or 8080. The same options work with python3 and py:

    py -m http.server 9000 –directory .\website

    By default, the server can listen beyond the local machine, depending on the platform and network configuration. Use –bind 127.0.0.1 to restrict access to the computer running the server:

    python -m http.server 8000 –directory ./website –bind 127.0.0.1

    To make the server reachable from other devices on the same network, bind it to the computer’s LAN address or use –bind 0.0.0.0, then browse to the host computer’s local IP address and port. Only expose files you intend to share.

    Open http://localhost:8000, read request logs, and stop the server

    With the default command running, browse to http://localhost:8000/. Each browser request appears in the terminal, typically with the client address, method, path, protocol, and status code:

    127.0.0.1 – – “GET /index.html HTTP/1.1” 200 –

    A 200 status means the file was served successfully. A 404 indicates that the requested path was not found. Browsers may make additional requests for stylesheets, scripts, icons, or source maps, so several log lines can appear for one page load.

    Keep the terminal process running while you use the server. Press Ctrl+C to stop it cleanly. Refreshing the browser after changing a file displays the updated version, although browser caching can sometimes require a hard refresh.

    Create a simple Python server with a custom handler—and know its limits

    Use a custom handler when you need a fixed response or a small change to request behavior. Save the following as server.py:

    from http.server import BaseHTTPRequestHandler, HTTPServer

    class Handler(BaseHTTPRequestHandler):

    def do_GET(self):

    body = b”Hello from Python\n”

    self.send_response(200)

    self.send_header(“Content-Type”, “text/plain; charset=utf-8”)

    self.send_header(“Content-Length”, str(len(body)))

    self.end_headers()

    self.wfile.write(body)

    server = HTTPServer((“127.0.0.1”, 8000), Handler)

    server.serve_forever()

    Start it with python server.py, python3 server.py, or py server.py, then open http://localhost:8000/. This simple Python server responds to GET requests with plain text instead of serving files. You can add path checks, headers, or different response bodies inside do_GET.

    The built-in server is intended for local development, testing, and quick file sharing. It provides basic HTTP handling and request logging, but it is not a production application server: it lacks the routing, security controls, concurrency features, deployment tooling, and application framework integration required for a public service.

  • Import Python File: A Practical Guide to Local Modules

    Import Python File: A Practical Guide to Local Modules

    Python loads a local file as a module when its directory is available on Python’s module search path. To load a local module, use its filename without the .py extension. A Python import of another Python file works the same way whether the file contains functions, classes, or constants.

    The simplest setup keeps both files in one directory, then expands to packages when the project grows.

    Import Python file from the same directory

    Suppose a directory contains main.py and helpers.py. The module name is helpers, not helpers.py.

    • helpers.py: GREETING = “Hello” and def greet(name): return f”{GREETING}, {name}”.
    • main.py: import helpers, from helpers import greet, print(helpers.GREETING), and greet(“Mina”).

    The statement import helpers imports the module and keeps its names under the helpers namespace. Use helpers.GREETING or helpers.greet() to access them. The statement from helpers import greet binds only greet in main.py, so you can call greet() directly.

    Do not include the .py extension, and avoid filenames containing spaces, hyphens, or names that conflict with standard-library modules.

    How to import another Python file selectively

    Use from module import name when you need specific functions, classes, or constants:

    • from helpers import greet, GREETING imports two names.
    • from helpers import greet as say_hello assigns an alias.
    • import helpers as h shortens the module reference while retaining its namespace.

    Selective imports make calls shorter, but they can make the source of a name less obvious. Use the module form when a file exposes many similarly named objects or when clarity matters. Python executes a module’s top-level code during its first import, then normally reuses the loaded module.

    Import another Python file from a package

    A package is a directory containing related modules. A predictable structure might look like this:

    • app/__init__.py
    • app/main.py
    • app/tools/__init__.py
    • app/tools/formatters.py

    Inside app/main.py, an absolute import can be from app.tools.formatters import clean. A package-relative import can be from .tools.formatters import clean. The leading dot means “from this package”; two dots refer to the parent package, as in from ..shared import settings.

    The parent directory of app must be on the search path for the absolute form to work. An __init__.py file makes package boundaries explicit and supports consistent behavior across tools, even though modern Python also supports namespace packages without one.

    Fix module search paths, entry points, and circular imports

    Python searches sys.path, which commonly includes the directory containing the launched script, the current directory for interactive or module execution, configured PYTHONPATH entries, the standard library, and installed packages. A ModuleNotFoundError usually means the module’s directory or the package’s parent is missing from that list.

    1. Run a package module from the project’s parent directory: python -m app.main. This gives relative imports the package context they require.
    2. Use if __name__ == “__main__”: to separate reusable definitions from script-only behavior. Put startup code in a main() function, then call it beneath the guard. Importing the file will define its functions without launching the script.
    3. Check spelling, capitalization, package names, and the directory from which the command runs. Also check that a local file is not shadowing a standard-library or installed module.

    Running python app/main.py directly can break a relative import because Python treats the file as a standalone script rather than as part of app. Prefer python -m app.main or configure the project as an installed package. Arbitrary sys.path mutation may hide the underlying structure problem and should not be the default fix.

    Circular imports occur when a.py imports b.py while b.py imports a.py. Typical symptoms include “cannot import name,” a “partially initialized module” message, or missing attributes during startup. Move shared functions or constants into a third module, make imports flow in one direction, or defer a genuinely optional import inside a function.