set.copy()

Return a shallow copy of the set. New container, same element references — but since set elements must be hashable, the copy is effectively independent for the usual cases.

Set methodPython 2.3+Live demo
Common call
backup = seen.copy()
Returns
a new set — mutations to either do not affect the other
Replaces
`set(s)` and `{*s}` — all three are equivalent shallow copies
Watch out
shallow only — but hashable elements make this an effectively deep copy for typical set contents
set.copy()
set

Demo

Live evaluation
Try:
Inputs
setsetcomma-separated elements
Output
{'a', 'b', 'c'}.copy()
[]

The demo shows the copied set — for the hashable values passed here, the copy is functionally independent of the original. Set elements must be hashable, and hashables are almost always immutable, so the shallow / deep distinction that bites dict and list users rarely bites set users. The important guarantee: the copy is a DIFFERENT set object; mutating one does not affect the other.

Common patterns

Snapshot before mutating
Keep the original for comparison or rollback.
before = state.copy()
state.update(new_items)
added = state - before
Independent per-caller copies
Give each recipient its own set so they can mutate freely.
default_tags = {"prod", "cache"}
def make_tags():
    return default_tags.copy()
Three equivalent forms
All three shallow-copy — pick one and stay consistent.
s.copy()
set(s)
{*s}

Examples

1. Basic
s = {1, 2} t = s.copy() t.add(3) s
Returns
{1, 2} # original untouched
2. Empty
set().copy()
Returns
set()
3. Equivalent forms
s.copy() == set(s) == {*s}
Returns
True # all three are shallow copies
4. Identity is fresh
s = {1, 2} t = s.copy() t is s
Returns
False # equal but not the same object
5. Frozenset copy
fs = frozenset({1, 2}) fs.copy()
Returns
frozenset({1, 2})

Pitfalls

1. copy() vs the `=` assignment
Assignment does NOT copy — it makes another name for the same set. Mutating either name mutates the same object. A common bug when passing sets between functions.
Alias, not copy
s = {1, 2}
t = s
t.add(3)
s
{1, 2, 3} # s changed too
Explicit copy
s = {1, 2}
t = s.copy()
t.add(3)
s
{1, 2} # s untouched
2. Still shallow — but rarely matters for sets
Elements are shared references, but set elements must be hashable, and hashables are almost always immutable. There is no practical mutable-element case for sets like there is for dict values.
Cannot store a list
{[1, 2]}.copy()
TypeError: unhashable type: 'list'
Use tuples inside
{(1, 2)}.copy()
shallow is deep enough
3. Three ways to shallow-copy — pick one for consistency
`s.copy()`, `set(s)`, and `{*s}` all produce shallow copies. They differ in the reader's intent and in some subclass edge cases: set(s) always returns a plain set; s.copy() preserves the subclass in most cases.
Random mix
a = s.copy()
b = set(s)
c = {*s}
# same result, three styles
inconsistent house style
Pick one
copy_of_s = s.copy()   # or {*s}, whichever your style guide picks
consistent
4. frozenset.copy() may return SELF
Because frozenset is immutable, CPython optimizes `.copy()` to return the same object — no new allocation. `is` returns True. This is not a bug and does not matter for correctness, but can surprise identity-checking tests.
Same object
fs = frozenset({1, 2})
fs.copy() is fs
True # optimization
Test equality not identity
fs.copy() == fs
True

When to use

Use it
  • Snapshotting a set before mutating for rollback or diff
  • Giving each caller its own set to mutate freely
  • Any "same members, different object" workflow
  • Composing with update() or `-=` to build modified copies without touching the source
Reach for something else
  • Rebinding is enough — you never mutate → `=` assignment is cheaper
  • Duplicating for read-only iteration only → no copy needed at all
  • Very large sets where the shallow copy is a hot-path allocation

Notes

Complexity
O(n) — one pass over the elements
Return
A new set of the same type; elements are the same object references
CPython impl
Objects/setobject.c :: set_copy — frozenset optimizes to return self
Memory
Allocates a new set; elements are not deep-copied (usually irrelevant since they must be hashable)
Thread-safe
The copy operation is safe against concurrent reads; not safe under concurrent writes

FAQ

Effectively yes for a plain set — both are shallow copies. set(s) always returns a plain set; s.copy() and {*s} may preserve the subclass. Pick one style and stay consistent.

History

2.3
set type added with copy() method.