set.copy()

set.copy()
Returns: set · Updated March 15, 2026 · Set Methods
sets methods copy

The .copy() method returns a shallow copy of a set. The returned set contains references to the same elements as the original, not deep copies of them. Since set elements are typically immutable (numbers, strings, tuples), this distinction rarely matters in practice.

Syntax

set.copy()

This method takes no parameters.

Examples

Basic usage

Create a copy of a set:

original = {1, 2, 3}
copied = original.copy()

print(copied)
# {1, 2, 3}

print(original is copied)
# False  # Different objects

Independent modifications

Modifying the copy does not affect the original:

fruits = {"apple", "banana", "cherry"}
fruits_copy = fruits.copy()

fruits_copy.add("orange")
print(fruits)
# {"apple", "banana", "cherry"}
print(fruits_copy)
# {"apple", "banana", "cherry", "orange"}

Copy vs constructor

The .copy() method and the set constructor produce equivalent results:

source = {1, 2, 3}

copy_method = source.copy()
copy_constructor = set(source)

print(copy_method == copy_constructor)
# True

The .copy() method is slightly more readable and explicit about intent.

Preserving set type

Both approaches preserve the set type:

numbers = {10, 20, 30}
copied = numbers.copy()

print(type(copied))
# <class 'set'>

Note that .copy() does not work on frozensets — use the frozenset constructor instead:

frozen = frozenset([1, 2, 3])
copied = set(frozen)  # Convert to regular set
print(copied)
# {1, 2, 3}

Common Patterns

Passing a set to a function without mutation

Avoid unintended modifications by copying before passing:

def process_set(s):
    s.add(4)  # Modifies the set
    return s

original = {1, 2, 3}

# Safe - original is unchanged
result = process_set(original.copy())
print(original)
# {1, 2, 3}
print(result)
# {1, 2, 3, 4}

# Unsafe - original is modified
result = process_set(original)
print(original)
# {1, 2, 3, 4}  # Changed!

Creating a modifiable version from frozenset

Convert an immutable frozenset to a regular set:

immutable = frozenset(["a", "b", "c"])
mutable = immutable.copy()  # Still a frozenset!
# TypeError: 'frozenset' object is not callable

# Correct approach
mutable = set(immutable)
mutable.add("d")
print(mutable)
# {"a", "b", "c", "d"}

Backup before batch operations

Create a backup before performing destructive operations:

data = {1, 2, 3, 4, 5}
backup = data.copy()

# Perform operations
data.discard(1)
data.discard(2)
data.difference_update({3})

print(data)
# {4, 5}

# Restore if needed
if some_condition:
    data = backup

This pattern is useful when you need to explore different branches of data processing.

Set difference for rollback

Compare the copy with the modified set to track changes:

current = {1, 2, 3, 4, 5}
original_snapshot = current.copy()

current.discard(2)
current.discard(4)

added = current - original_snapshot
removed = original_snapshot - current
print(f"Removed: {removed}")  # {2, 4}
print(f"Added: {added}")  # set()

Deep Copy Consideration

For sets containing mutable objects, you might need a deep copy:

import copy

# Shallow copy - nested lists share references
sets_with_lists = {[1, 2], [3, 4]}
shallow = sets_with_lists.copy()
shallow[0].append(99)
print(sets_with_lists)  # [[1, 2, 99], [3, 4]]  # Original modified!

# Deep copy - truly independent
deep = copy.deepcopy(sets_with_lists)
deep[0].append(100)
print(sets_with_lists)  # [[1, 2, 99], [3, 4]]  # Unchanged

However, this is rare since set elements should be hashable (typically immutable).

Performance

The .copy() method has:

  • Time complexity: O(n) where n is the number of elements
  • Space complexity: O(n) for the new set

The operation must iterate through all elements to create the new set. For large sets, consider whether you actually need a full copy or can use a different approach.

See Also