The set.symmetric_difference(other) method returns a brand-new set containing elements present in either the primary set or the specified iterable, but not in both.
Exclusive Disjunction (XOR): Mathematically denoted as (A \ B) ∪ (B \ A), this operation leaves all source inputs unmodified. When working strictly between set objects, you can also use the caret operator (^). To mutate the primary set in-place instead of creating a new set, use symmetric_difference_update(). For related operations, see union(), intersection(), and difference().
The symmetric difference can be invoked as an object method or using Python's bitwise XOR operator (^):
new_set = set_instance.symmetric_difference(other)
new_set = set_a ^ set_b
| Syntax Form | Accepted Types | Behavioral Description |
|---|---|---|
other |
Any single iterable (set, list, tuple, dict, string) | The target collection compared against the calling set. Exactly one iterable is accepted. |
set_a ^ set_b |
set or frozenset only |
Binary operator syntax. Operands must be sets; other types raise a TypeError. |
| Return Value | set |
A new set containing elements belonging to either collection, but not both. |
When comparing two sets, elements common to both sets are stripped out, while unique elements from each side are unified into the result:
A = {1, 2, 3}
B = {3, 4, 5}
# Caret operator (common element 3 is excluded)
res_op = A ^ B
res_meth = A.symmetric_difference(B)
print("A ^ B:", res_op)
print("A.symmetric_difference(B):", res_meth)
# Source sets remain unmodified
print("Set A:", A)
print("Set B:", B)
Output:
A ^ B: {1, 2, 4, 5}
A.symmetric_difference(B): {1, 2, 4, 5}
Set A: {1, 2, 3}
Set B: {3, 4, 5}
Verifying the resulting data type and memory identity using type():
x = A ^ B
print("Type of x:", type(x))
print("New set instance:", id(x) != id(A))
Output:
Type of x: <class 'set'>
New set instance: True
^ Operator vs. symmetric_difference() Method 🔝While both syntax forms compute the exact same mathematical exclusive disjunction, they differ in argument handling:
symmetric_difference() method: Accepts any Python iterable (such as lists, tuples, or strings).^ operator: Strictly requires both operands to be set instances. Other types trigger a TypeError.A = {"a", "b", "c"}
B_list = ["a", "x", "y"]
# 1. Method accepts list directly
print("Method with list:", A.symmetric_difference(B_list))
# 2. Operator requires set operand
try:
invalid_op = A ^ B_list
except TypeError as err:
print("Operator TypeError caught:", err)
Output:
Method with list: {'b', 'x', 'c', 'y'}
Operator TypeError caught: unsupported operand type(s) for ^: 'set' and 'list'
Unlike union() or intersection(), symmetric_difference() takes only one argument. To chain multiple sets, use the ^ operator: A ^ B ^ C.
In multi-set XOR, an element is retained if and only if it appears in an odd number of sets:
A = {"a", "b", "c"}
B = {"a", "y", "z"}
C = {"a", "k", "l"}
# 'a' is in all 3 sets (odd count) -> included in final result
res_chain = A ^ B ^ C
print("Chained XOR (A ^ B ^ C):", sorted(res_chain))
# Method chaining equivalent
res_meth_chain = A.symmetric_difference(B).symmetric_difference(C)
print("Method chained:", sorted(res_meth_chain))
Output:
Chained XOR (A ^ B ^ C): ['a', 'b', 'c', 'k', 'l', 'y', 'z']
Method chained: ['a', 'b', 'c', 'k', 'l', 'y', 'z']
Passing a raw string unpacks it into individual characters. When comparing whole words, wrap strings in a collection:
# Character-level symmetric difference
chars = {"a", "b", "c", "x", "y"}
print("With 'Alex' string:", sorted(chars.symmetric_difference("Alex")))
# Word-level symmetric difference
roles = {"Alex", "Maria", "David"}
print("Bare string (trap):", roles.symmetric_difference("Alex"))
print("Wrapped list (correct):", roles.symmetric_difference(["Alex"]))
Output:
With 'Alex' string: ['A', 'a', 'b', 'c', 'e', 'l', 'y']
Bare string (trap): {'David', 'e', 'Maria', 'l', 'Alex', 'A'}
Wrapped list (correct): {'Maria', 'David'}
["Alex"]) or set (e.g. {"Alex"}).
symmetric_difference() vs. symmetric_difference_update() 🔝Choosing between symmetric_difference() and symmetric_difference_update() depends on immutability vs in-place optimization:
| Feature | symmetric_difference() / A ^ B |
symmetric_difference_update() / A ^= B |
|---|---|---|
| Mutation | Non-destructive (allocates new set) | In-place mutation (modifies caller directly) |
| Return Value | New set instance |
None |
| Time Complexity | O(len(A) + len(B)) average | O(len(B)) average |
| Auxiliary Space | O(len(result)) memory allocation | O(1) space (no allocation) |
Compare local in-memory records with remote catalog state to isolate discrepancies needing updates:
def find_catalog_discrepancies(local: set[str], remote: list[str]) -> set[str]:
return local.symmetric_difference(remote)
local_items = {"SKU-100", "SKU-200", "SKU-300"}
remote_items = ["SKU-200", "SKU-300", "SKU-400"]
print("Out-of-sync SKUs:", sorted(find_catalog_discrepancies(local_items, remote_items)))
Output:
Out-of-sync SKUs: ['SKU-100', 'SKU-400']
Find feature toggles enabled in staging but disabled in production (or vice versa):
def audit_env_mismatches(staging: set[str], production: set[str]) -> set[str]:
return staging ^ production
staging_flags = {"dark_mode", "new_checkout", "beta_dashboard"}
prod_flags = {"dark_mode", "legacy_billing"}
print("Flag mismatches:", sorted(audit_env_mismatches(staging_flags, prod_flags)))
Output:
Flag mismatches: ['beta_dashboard', 'legacy_billing', 'new_checkout']
A ^ A == set().A ^ B == A | B.A ^ set() == A.base = {10, 20}
# 1. Self XOR yields empty set
print("Self XOR (A ^ A):", base ^ base)
# 2. Disjoint XOR equals union
disjoint = {30, 40}
print("Disjoint XOR:", base ^ disjoint)
# 3. Empty set XOR
empty_xor = base.symmetric_difference(set())
print("Empty set XOR:", empty_xor, "Distinct copy:", empty_xor is not base)
Output:
Self XOR (A ^ A): set()
Disjoint XOR: {10, 20, 30, 40}
Empty set XOR: {10, 20} Distinct copy: True
Practice hands-on drills, multi-set XOR chaining, and real-world inventory reconciliation in our companion Jupyter Notebook:
Explore interactive code cells, benchmark execution speeds, and run self-validating assertions.
View on GitHub Run in Google ColabRun these unit checks in Python to verify symmetric difference operations:
def test_set_symmetric_difference():
s1 = {1, 2, 3}
s2 = {3, 4, 5}
assert s1.symmetric_difference(s2) == {1, 2, 4, 5}
assert s1 ^ s2 == {1, 2, 4, 5}
assert s1 == {1, 2, 3}
assert s1.symmetric_difference([2, 3, 9]) == {1, 9}
assert s1 ^ s1 == set()
print("All set.symmetric_difference() assertion tests passed! 100% verified.")
test_set_symmetric_difference()
Output:
All set.symmetric_difference() assertion tests passed! 100% verified.
Mutates the calling set in-place to retain only elements found in either collection but not both.
Combines distinct elements from all specified collections into a new set.
Computes common elements found across collections, returning a new set object.
Computes elements unique to the calling set, excluding items found in other iterables.
Author & Instructor at plus2net
I write and maintain practical tutorials on Python, PHP, SQL, JavaScript, HTML, jQuery, and web development at plus2net. The tutorials focus on clear explanations, working examples, and code that readers can test and adapt while learning.