How to find the factorial of a number in Python

Discover multiple ways to calculate a number's factorial in Python. Get tips, see real-world applications, and learn to debug common errors.

How to find the factorial of a number in Python
Published on: 
Thu
Feb 12, 2026
Updated on: 
Mon
Apr 13, 2026
The Replit Team

To find a number's factorial is a fundamental operation in mathematics and programming. Python offers several methods for this task, from simple loops to the built-in math.factorial() function.

In this guide, you'll explore these techniques with practical examples. You will also learn about real-world applications, performance tips, and how to debug common errors in your calculations.

Basic iterative approach with a loop

def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result

print(factorial(5))--OUTPUT--120

The factorial() function uses a for loop to build the result incrementally. It initializes a result variable to 1, which is crucial because it's the multiplicative identity. This provides a clean starting point for the calculation, ensuring the first multiplication is accurate.

The loop iterates from 1 up to the input number n. In each step, the result *= i operation updates the total by multiplying it with the current number. This process effectively computes the factorial by multiplying each integer in the sequence.

Standard factorial methods

Building on the iterative method, you can also tackle factorials using Python's built-in math library, recursion, or functional programming with functools.reduce().

Using the built-in math.factorial() function

import math

print(math.factorial(5))
print(math.factorial(10))--OUTPUT--120
3628800

Python's math module provides a straightforward function, math.factorial(), for this exact purpose. It's the standard, recommended approach because it’s implemented in C, making it highly efficient and reliable. You just need to import the math library to use it.

  • It's generally faster than writing your own loop or recursive function.
  • The function handles validation by raising a ValueError if you pass it a negative number.
  • It only accepts integers, so you'll get a TypeError if you try using a float.

Implementing factorial with recursion

def factorial_recursive(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial_recursive(n - 1)

print(factorial_recursive(5))--OUTPUT--120

Recursion offers an elegant, if less performant, alternative. The factorial_recursive function works by calling itself with a smaller number until it reaches a base condition. This creates a chain of multiplication that unwinds once the base case is met. Understanding recursion in Python is essential for mastering this technique.

  • The base case, if n == 0 or n == 1, is crucial. It returns 1 and stops the function from calling itself infinitely.
  • The recursive step, n * factorial_recursive(n - 1), breaks the problem down into smaller pieces until the base case is reached.

Using functools.reduce() for factorial calculation

from functools import reduce
import operator

def factorial_reduce(n):
return reduce(operator.mul, range(1, n + 1), 1)

print(factorial_reduce(6))--OUTPUT--720

For a more functional approach, you can use functools.reduce(). This function cumulatively applies an operation to a sequence, boiling it down to a single result. It’s a concise way to express complex accumulations.

  • The function takes operator.mul, which tells it to multiply items together.
  • It applies this multiplication across the range(1, n + 1).
  • The final argument, 1, is the initializer—it sets the starting value for the calculation.

Advanced factorial techniques

With the fundamentals covered, you can now explore more advanced techniques like memoization for optimization, sequence generators, and concise lambda function implementations.

Optimizing with memoization

factorial_cache = {}

def factorial_memo(n):
if n in factorial_cache:
return factorial_cache[n]
if n == 0 or n == 1:
return 1
factorial_cache[n] = n * factorial_memo(n - 1)
return factorial_cache[n]

print(factorial_memo(7))--OUTPUT--5040

Memoization is an optimization technique that speeds up functions by caching their results. The factorial_memo function uses a dictionary, factorial_cache, to store values it has already computed, avoiding redundant work in recursive calls.

  • Before calculating, the function checks if the result for n is already in the cache.
  • If a value is found, it's returned instantly.
  • If not, the function computes the factorial, stores the new result in the cache, and then returns it. This makes future calls for the same number significantly faster.

Creating a factorial sequence generator

def factorial_generator(n):
result = 1
for i in range(1, n + 1):
result *= i
yield result

for i, fact in enumerate(factorial_generator(5), 1):
print(f"{i}! = {fact}")--OUTPUT--1! = 1
2! = 2
3! = 6
4! = 24
5! = 120

The factorial_generator function uses the yield keyword to create a generator. Instead of calculating all factorials and returning a list, it produces each value in the sequence one at a time. This approach is highly memory-efficient, especially for large ranges.

  • The yield result statement pauses the function, returns the current factorial, and saves its state. When the generator is called again, it resumes right where it left off.

Implementing factorial as a lambda function

factorial_lambda = lambda n: 1 if n <= 1 else n * factorial_lambda(n - 1)

print(factorial_lambda(4))
print(factorial_lambda(8))--OUTPUT--24
40320

A lambda function offers a compact way to define a single-expression function without a formal def statement. This example, factorial_lambda, packs a recursive factorial calculation into one line using a ternary operator for its logic. For more details on using lambda in Python, explore the various applications of these anonymous functions.

  • The expression 1 if n <= 1 serves as the base case, which stops the recursion.
  • The recursive step, else n * factorial_lambda(n - 1), calls the function itself with a decremented value.

While it’s a concise alternative, this approach can be less readable than a standard function, especially for those unfamiliar with recursive lambdas.

Move faster with Replit

Replit is an AI-powered development platform that comes with all Python dependencies pre-installed, so you can skip setup and start coding instantly. Instead of piecing together techniques, you can use Agent 4 to build complete applications. Describe the app you want to build, and the Agent will handle the code, databases, APIs, and deployment.

  • A statistical analysis tool that uses factorial calculations for probability modeling.
  • A performance dashboard that compares the speed of recursive versus iterative functions with memoization.
  • A data sequence utility that uses a generator to process large datasets without consuming excess memory.

Simply describe your app, and Replit will write the code, test it, and fix issues automatically, all within your browser.

Common errors and challenges

When calculating factorials, you'll need to handle negative inputs, recursion limits, and potential memory issues, but each has a straightforward solution.

Handling negative inputs in factorial() functions

Factorials are mathematically defined only for non-negative integers, so passing a negative number should raise an error. While Python’s built-in math.factorial() automatically raises a ValueError for negative inputs, your custom functions won't do this by default. It's good practice to add a check at the beginning of your function to validate the input and raise an error if n < 0, making your code more robust and predictable.

Avoiding stack overflow in recursive factorial() implementation

Recursive functions can be elegant, but they come with a risk of stack overflow. Each time a function calls itself, it adds a new layer to the call stack. Python has a recursion depth limit to prevent infinite loops from consuming all available memory, so calling a recursive factorial function with a large number can trigger a RecursionError. For calculations involving large numbers, iterative methods or the highly optimized math.factorial() are safer and more efficient choices.

Preventing memory leaks in memoized factorial() functions

Memoization speeds up calculations by caching results, but this can lead to high memory consumption if not managed carefully. In our example, the factorial_cache dictionary grows indefinitely, storing every result it computes. While this isn't a problem for a few calls, in a long-running application or a function called with many different large numbers, the cache can consume a significant amount of memory. To prevent this, you could implement a more advanced caching strategy, such as a fixed-size cache that discards the least recently used items.

Handling negative inputs in factorial() functions

Factorials are only defined for non-negative integers, but a basic iterative function won't handle negative inputs correctly by default. Instead of raising an error, it can return a misleading result. Our simple factorial() function, for instance, returns 1. See what happens in the code below.

def factorial(n):
result = 1
for i in range(1, n + 1):
result *= i
return result

# This will create an empty range and return 1, which is incorrect
print(factorial(-5))

The range(1, n + 1) function doesn't work as expected with a negative n. The loop is skipped entirely, causing the function to return its initial value. See how to handle this case in the code below.

def factorial(n):
if n < 0:
raise ValueError("Factorial is not defined for negative numbers")
result = 1
for i in range(1, n + 1):
result *= i
return result

try:
print(factorial(-5))
except ValueError as e:
print(f"Error: {e}")

To fix this, you can add a simple check at the start of your function. By adding an if n < 0: condition, you can immediately raise ValueError("Factorial is not defined for negative numbers"). This approach makes your function's behavior clear and prevents it from returning a misleading 1. It's good practice to validate inputs this way, especially when a function's mathematical definition has specific constraints, ensuring your code is robust and predictable.

Avoiding stack overflow in recursive factorial() implementation

Recursive functions are elegant, but they aren't built for large numbers. Each call to factorial_recursive() adds a new frame to Python's call stack. When the stack gets too deep, you'll hit the recursion limit, triggering a RecursionError. See it happen below.

def factorial_recursive(n):
if n == 0 or n == 1:
return 1
else:
return n * factorial_recursive(n - 1)

# This will cause a RecursionError for large inputs
print(factorial_recursive(1000))

The call factorial_recursive(1000) creates a chain of nearly 1,000 nested function calls. This exceeds Python's default recursion depth, triggering a RecursionError. While one solution is to increase recursion limit in Python, the code below shows how to restructure the logic to avoid this.

def factorial_iterative(n):
result = 1
for i in range(1, n + 1):
result *= i
return result

# Using iteration instead of recursion for large numbers
print(factorial_iterative(1000))

The iterative solution, factorial_iterative, sidesteps the recursion limit entirely. Instead of creating a deep call stack, it uses a simple for loop to multiply numbers one by one. This approach keeps memory usage flat and avoids the RecursionError that plagues recursive functions with large inputs. It's the go-to method when you need to calculate factorials for numbers that would otherwise cause a stack overflow, ensuring your code remains stable and efficient.

Preventing memory leaks in memoized factorial() functions

Memoization is a powerful optimization, but its cache can grow indefinitely and consume significant memory. The factorial_memo function stores every result it computes, which becomes a problem in long-running applications. See how this unbounded growth happens in the code below.

factorial_cache = {}

def factorial_memo(n):
if n in factorial_cache:
return factorial_cache[n]
if n == 0 or n == 1:
return 1
factorial_cache[n] = n * factorial_memo(n - 1)
return factorial_cache[n]

for i in range(1000):
factorial_memo(i)

The for loop populates the factorial_cache with a thousand different results, causing it to grow without bounds and consume excess memory. The following example demonstrates a more controlled approach to caching.

from functools import lru_cache

@lru_cache(maxsize=128)
def factorial_memo(n):
if n == 0 or n == 1:
return 1
return n * factorial_memo(n - 1)

for i in range(1000):
factorial_memo(i)

The @lru_cache decorator from Python's functools library offers a clean fix. It automatically handles memoization, so you don't need to manage a cache dictionary yourself.

  • The key is the maxsize argument, which limits how many results are stored.
  • Once the cache is full, it discards the least recently used items to make room for new ones. This prevents unbounded memory growth in long-running applications.

Real-world applications

With those common challenges addressed, you can apply factorials to practical problems in probability and mathematical approximations.

Using factorial() to calculate combinations for lottery odds

The factorial() function is fundamental to calculating combinations, a common task in probability that lets you figure out the odds of events like winning the lottery. Once you have the odds, you can convert them to probabilities by calculating percentages in Python.

def combinations(n, r):
return factorial(n) // (factorial(r) * factorial(n - r))

# Calculate the odds of winning a lottery (choosing 6 numbers from 49)
lottery_odds = combinations(49, 6)
print(f"Odds of winning the lottery (6 from 49): 1 in {lottery_odds}")

This code implements the standard formula for combinations, which calculates how many ways you can choose a subset of items from a larger set. The combinations() function uses the factorial() function you've already seen to determine the odds.

  • The parameters n and r represent the total number of items and the number you want to choose, respectively.
  • It uses integer division // because the result of a combination calculation is always a whole number.
  • The example calculates lottery odds by finding all possible combinations of 6 numbers from 49.

Approximating e^x using Taylor series with factorials

Beyond probability, factorials are a key component of the Taylor series, a powerful mathematical tool for approximating the value of functions like e^x.

def exp_approximation(x, terms=10):
result = 0
for n in range(terms):
result += x**n / factorial(n)
return result

# Compare our approximation with math.exp
import math
x = 2
print(f"Our approximation: {exp_approximation(x)}")
print(f"Math.exp result: {math.exp(x)}")

The exp_approximation function builds a sum by adding several calculated values together. The number of values it sums is controlled by the terms parameter—more terms lead to a more accurate result. The function starts with a result of 0 and adds to it in each loop.

  • Each value added is the result of the expression x**n / factorial(n).
  • The loop runs from n = 0 up to the number of terms, accumulating the total.
  • This method uses factorials as part of a series to approximate a complex mathematical value.

Get started with Replit

Turn your new skills into a real tool with Replit Agent. Try prompts like, “Build a probability calculator for combinations,” or “Create a tool to compare factorial function performance.”

The Agent writes the code, tests for errors, and deploys your app automatically. Start building with Replit.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.

Build your first app today

Describe what you want to build, and Replit Agent writes the code, handles the infrastructure, and ships it live. Go from idea to real product, all in your browser.