How to find the factorial of a number in Python

Learn how to find the factorial of a number in Python. Explore different methods, real-world applications, and common debugging tips.

How to find the factorial of a number in Python
Published on: 
Thu
Feb 12, 2026
Updated on: 
Tue
Feb 24, 2026
The Replit Team Logo Image
The Replit Team

The factorial of a number is a common mathematical operation. Python offers several straightforward ways to calculate it, from simple loops to built-in functions for efficient and clean code.

In this article, you'll explore different techniques to find a factorial. You will also get practical tips, see real-world applications, and learn how to debug common errors for robust factorial 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 simple loop to build the result step by step. It starts by setting a result variable to 1. This is the essential base case for the multiplication, as any number multiplied by 1 remains unchanged, ensuring the calculation begins correctly.

The function then iterates from 1 up to the given number n. With each pass through the loop, it multiplies the current result by the loop counter i. This process continues until all numbers in the sequence have been multiplied, returning the final factorial value.

Standard factorial methods

While a for loop gets the job done, Python’s standard library provides more concise and specialized methods for calculating factorials.

Using the built-in math.factorial() function

import math

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

The math.factorial() function is Python's dedicated tool for this calculation. It's part of the standard math module, so you just need to import it to get started. This function is highly optimized and generally faster than a custom loop since it's implemented in C.

  • It accepts only non-negative integers.
  • Providing a negative number will raise a ValueError.
  • Using a non-integer, like a float, will result in a TypeError.

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

The factorial_recursive function offers an elegant, alternative approach. It works by calling itself with a smaller version of the problem until it reaches a base case, which prevents an infinite loop.

  • The base case is when n is 0 or 1. Here, the function simply returns 1 and stops the recursion.
  • For any other number, the function multiplies n by the result of calling itself with n - 1.

This creates a chain of calls that unwinds once the base case is hit, multiplying the numbers from n down to 1.

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

The reduce() function provides a functional programming approach to this problem. It works by applying a function cumulatively to a sequence of items, effectively reducing the sequence to a single value.

  • The operator.mul function is used to perform the multiplication.
  • It’s applied to the sequence of numbers generated by range(1, n + 1).
  • The calculation begins with an initial value of 1, which is the third argument passed to reduce().

Advanced factorial techniques

Building on the standard methods, you can also use advanced techniques to optimize performance, generate factorial sequences, or write more compact code.

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 previously computed values and avoid redundant calculations.

  • Before computing, it checks if the result for n is already in the cache.
  • If a value is found, it’s returned immediately, skipping the calculation.
  • Otherwise, the function calculates the factorial, stores the new result in factorial_cache, and then returns it.

This approach makes subsequent calls with the same number much faster. It also reuses intermediate results for new calculations, like using the stored value of factorial(6) to compute factorial(7).

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 creates a generator, which is a memory-efficient way to produce a sequence of values. Instead of returning a single result, it uses the yield keyword to send back each factorial one at a time as it's calculated.

  • This approach is useful when you need a series of factorials without storing them all in memory.
  • Each time the generator is called in a loop, it computes the next value in the sequence and pauses, preserving its state until the next call.

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 lets you define a function in a single, compact line. The factorial_lambda function uses this concise syntax to implement recursion, relying on a ternary operator (if...else) to manage its logic.

  • If n is 1 or less, it returns 1—this is the base case that stops the recursion.
  • Otherwise, it multiplies n by the result of calling itself with n - 1.

It's functionally identical to the standard recursive method but written in a more condensed style.

Move faster with Replit

Replit is an AI-powered development platform that transforms natural language into working applications. Describe what you want to build, and Replit Agent creates it, complete with databases, APIs, and deployment.

For the factorial methods explored in this article, Replit Agent can turn them into production-ready tools. For example, you could build:

  • A probability calculator that computes permutations and combinations for statistical analysis.
  • A developer utility that benchmarks the performance of different factorial implementations, like the recursive factorial_recursive versus the memoized factorial_memo function.
  • An educational tool that visualizes how factorials grow exponentially, using a generator like factorial_generator.

Describe your app idea, and the agent will write the code, test it, and fix issues automatically. Try Replit Agent to turn your concept into a working application.

Common errors and challenges

While factorial functions are powerful, you can run into a few common pitfalls, from handling invalid inputs to managing memory and recursion limits.

Handling negative inputs in factorial() functions

Factorials are mathematically defined only for non-negative integers. Python’s built-in math.factorial() function enforces this by raising a ValueError if you pass it a negative number. However, custom implementations can fail in less obvious ways.

  • A simple recursive function like factorial_recursive() without a specific check for negative numbers will enter an infinite loop, as n - 1 will never reach the base case of 0 or 1.
  • This infinite recursion eventually leads to a stack overflow error, crashing the program. It’s crucial to add input validation to your custom functions to handle negative numbers gracefully.

Avoiding stack overflow in recursive factorial() implementation

Python has a built-in recursion limit to prevent infinite loops from consuming too much memory. A recursive function like factorial_recursive() can easily hit this limit when calculating the factorial of a large number.

  • Each recursive call adds a new layer to the call stack. With a sufficiently large input, the stack depth exceeds Python's limit, and you'll get a RecursionError.
  • For large numbers, it's safer to use an iterative approach, such as a for loop or the highly optimized math.factorial() function, as they don't have this limitation.

Preventing memory leaks in memoized factorial() functions

Memoization, as used in factorial_memo(), is great for performance but can introduce memory issues if not managed carefully. The cache stores every result it computes, and it can grow indefinitely in a long-running application.

  • If the function is called with many different numbers, the factorial_cache dictionary can consume a significant amount of memory, creating a memory leak.
  • To prevent this, you might need a more advanced caching strategy. For example, you could limit the cache's size or implement a policy to discard older, less-used entries.

Handling negative inputs in factorial() functions

While Python's built-in function flags negative numbers with an error, a custom iterative function can fail silently. Instead of alerting you to the invalid input, it may return an incorrect result, leading to subtle bugs that are hard to trace.

The following code shows how passing a negative number to a function using a for loop produces a misleading output because the range() function creates an empty sequence.

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 loop inside the factorial() function never runs for negative numbers because its range() is empty. This causes the function to return its default value of 1, hiding the error. The updated code below shows how to catch this issue.

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}")

The corrected factorial() function adds an upfront check to validate its input. If n is negative, it raises a ValueError, which is the standard way to signal an inappropriate argument. This prevents the function from failing silently and returning an incorrect value. You can then use a try...except block to catch this specific error, allowing your application to handle invalid inputs gracefully instead of producing a hard-to-trace bug.

Avoiding stack overflow in recursive factorial() implementation

While recursion is elegant, it has its limits—literally. Python sets a maximum recursion depth to prevent stack overflows. With a large enough number, a function like factorial_recursive() will hit this ceiling, triggering a RecursionError. The following code demonstrates this exact scenario.

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 function factorial_recursive(1000) triggers a chain of nearly a thousand self-calls. Each one waits for the next to finish, quickly overwhelming Python's call stack. The following code reframes the problem to avoid this error.

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 factorial_iterative function sidesteps the recursion limit by using a loop. This iterative approach is more robust for large numbers because it doesn't add new frames to the call stack with each multiplication.

  • It calculates the result within a single function call, keeping memory usage flat.
  • This makes it a safer choice for any task that could involve deep recursion, preventing unexpected crashes when inputs grow.

Preventing memory leaks in memoized factorial() functions

While memoization speeds up calculations, it can also create memory leaks. The cache used by a function like factorial_memo stores every result it computes. Over time, this cache can grow without bounds, consuming more and more memory. The code below shows this in action.

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 fills the global factorial_cache with a thousand unique results. Since the cache has no size limit, it grows with each call, consuming more memory. The following code introduces a more robust caching strategy.

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 functools.lru_cache decorator offers a simple fix. It automatically manages the cache size, discarding the least recently used items once the maxsize limit is reached. This prevents the cache from growing indefinitely, which is crucial in long-running applications or when calling the function with a wide range of inputs. By setting maxsize=128, you ensure memory usage stays predictable, avoiding leaks while still getting the performance benefits of memoization.

Real-world applications

Factorial calculations are fundamental to solving complex problems, from figuring out lottery odds to approximating mathematical constants.

Using factorial() to calculate combinations for lottery odds

The factorial() function is key to solving problems in combinatorics, as it lets you calculate the number of ways to choose a subset of items from a larger group, such as picking winning lottery numbers.

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 uses the classic mathematical formula for combinations, which relies heavily on the factorial() function. The combinations(n, r) function takes two arguments: the total number of items, n, and the number of items to choose, r. It uses integer division with the // operator to ensure the final output is a clean, whole number.

  • The lottery example applies this by setting n to 49 and r to 6.
  • The result, lottery_odds, reveals the total number of unique combinations possible, giving you a clear picture of your chances.

Approximating e^x using Taylor series with factorials

In calculus, factorials are a key ingredient in the Taylor series, a powerful formula that approximates mathematical 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 calculates an estimate of e raised to the power of x. It iterates a set number of times, controlled by the terms parameter, to build the result. In each loop, it adds a new value to the sum based on the formula x**n / factorial(n).

  • The terms argument determines the approximation's accuracy—more terms yield a more precise result.
  • The code then compares the function's output for x=2 with Python's highly accurate math.exp() to show how close the approximation gets.

Get started with Replit

Put these factorial methods to work by building a real tool. Tell Replit Agent: "Build a probability calculator for permutations" or "Create a utility to benchmark factorial function performance."

The agent writes the code, tests for errors, and deploys your application. Start building with Replit.

Get started free

Create and deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.

Get started for free

Create & deploy websites, automations, internal tools, data pipelines and more in any programming language without setup, downloads or extra tools. All in a single cloud workspace with AI built in.