How to find specific metadata¶
This guide shows you how to find metadata items by type within a MetadataCollection. You'll learn to locate single items, collect all matches, check for existence, and handle missing metadata gracefully.
Quick reference¶
| Goal | Method | Returns |
|---|---|---|
| Find first item of a type | find() |
T \| None |
| Find first of several types | find_first() |
object \| None |
| Get all items matching types | find_all() |
MetadataCollection |
| Check if type exists | has() |
bool |
| Count matching items | count() |
int |
| Get with fallback | get() |
T |
| Get or raise error | get_required() |
T |
| Check if empty | is_empty |
bool |
Prerequisites
This guide assumes you know how to create MetadataCollection instances.
See Working with metadata for construction basics.
Finding the first matching item¶
find() for exact type match¶
Use find() to get the first item matching a specific type:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Gt:
value: int
@dataclass(frozen=True)
class Lt:
value: int
coll = MetadataCollection(_items=(Gt(0), Lt(100), Gt(10), "doc")) # (1)!
# Find first item of type
constraint = coll.find(Gt) # (2)!
print(constraint) # Gt(value=0)
# Returns None if not found
missing = coll.find(float)
print(missing) # None
- Creates an immutable collection with two
Gtinstances, oneLt, and a string. - Returns the first
Gtfound (Gt(0)), notGt(10). Stops searching at first match.
The find() method uses isinstance semantics, so subclasses also match:
from typing_graph import MetadataCollection
class Animal:
pass
class Dog(Animal):
pass
coll = MetadataCollection(_items=(Dog(), "doc"))
result = coll.find(Animal) # Returns the Dog instance
Performance
Prefer find() over find_all() when you only need the first match. find() stops at the first match, while find_all() scans the entire collection.
find_first() for multiple candidate types¶
Use find_first() to find the first item matching any of several types:
from typing_graph import MetadataCollection
coll = MetadataCollection(_items=("doc", 42, True))
# Find first item matching any type
result = coll.find_first(int, float)
print(result) # 42
# Returns None if no types match
result = coll.find_first(list, dict)
print(result) # None
Handling None results¶
Both find() and find_first() return None when no match is found. Use conditional checks or the get() method with defaults:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Description:
text: str
coll = MetadataCollection(_items=(42, "doc"))
# Conditional check
desc = coll.find(Description)
if desc is not None:
print(desc.text)
else:
print("No description")
Safe None handling with walrus operator
Use Python's walrus operator (:=) for concise None-safe patterns:
Collecting all items of a type¶
find_all() with single type¶
Use find_all() to get all items matching a type:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Gt:
value: int
coll = MetadataCollection(_items=(Gt(0), "doc", Gt(10), Gt(5)))
# Find all items of a type
all_gt = coll.find_all(Gt)
print(list(all_gt)) # [Gt(value=0), Gt(value=10), Gt(value=5)]
find_all() with multiple types¶
Pass multiple types to find items matching any of them:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Gt:
value: int
@dataclass(frozen=True)
class Lt:
value: int
coll = MetadataCollection(_items=(Gt(0), Lt(100), Gt(10), "doc"))
# Find all items matching any of multiple types
constraints = coll.find_all(Gt, Lt)
print(list(constraints)) # [Gt(value=0), Lt(value=100), Gt(value=10)]
Empty results¶
When no items match, find_all() returns an empty collection:
from typing_graph import MetadataCollection
coll = MetadataCollection(_items=("a", "b", "c"))
floats = coll.find_all(float)
print(len(floats)) # 0
print(floats is MetadataCollection.EMPTY) # True
With no arguments, find_all() returns a copy of all items:
from typing_graph import MetadataCollection
coll = MetadataCollection(_items=(1, 2, 3))
all_items = coll.find_all()
print(list(all_items)) # [1, 2, 3]
Checking existence¶
has() vs count()¶
Use has() to check if any item matches given types:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Gt:
value: int
coll = MetadataCollection(_items=(Gt(0), "doc", 42))
# Check for a single type
print(coll.has(Gt)) # True
print(coll.has(float)) # False
# Check for any of multiple types
print(coll.has(float, list)) # False
print(coll.has(str, int)) # True
Performance
Prefer has() over count() when you only need to know if something exists. has() stops at the first match, while count() scans the entire collection.
is_empty property¶
Use the is_empty property for readable empty checks:
from typing_graph import MetadataCollection
empty = MetadataCollection.EMPTY
non_empty = MetadataCollection(_items=(1, 2))
print(empty.is_empty) # True
print(non_empty.is_empty) # False
# More readable than len() == 0
coll = MetadataCollection(_items=("a",))
if coll.is_empty:
print("No metadata")
Providing fallback values for missing metadata¶
get() with default values¶
Use get() to retrieve an item with a default value if not found:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Gt:
value: int
@dataclass(frozen=True)
class Lt:
value: int
coll = MetadataCollection(_items=(Gt(0), "doc"))
# Get with no default returns None if not found
result = coll.get(Lt)
print(result) # None
# Get with default value
result = coll.get(Lt, Lt(100))
print(result) # Lt(value=100)
# Get returns the matched item, not the default
result = coll.get(Gt, Gt(999))
print(result) # Gt(value=0)
get() falsy value handling¶
The get() method correctly handles falsy values like 0, False, and empty strings:
from typing_graph import MetadataCollection
coll = MetadataCollection(_items=(0, False, ""))
# Falsy values are returned correctly
print(coll.get(int, -1)) # 0 (not -1)
print(coll.get(str, "x")) # '' (not 'x')
get_required() for mandatory metadata¶
Use get_required() when metadata must exist:
Use sparingly
get_required() raises an exception when metadata is missing. Prefer get() with a sensible default unless the absence of metadata is truly exceptional and indicates a programming error.
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class MaxLen:
value: int
coll = MetadataCollection(_items=("doc",))
# Safe: returns default if not found
max_len = coll.get(MaxLen, MaxLen(255)) # (1)!
print(max_len.value) # 255
MaxLennot in collection, so returns the defaultMaxLen(255).
from dataclasses import dataclass
from typing_graph import MetadataCollection, MetadataNotFoundError
@dataclass(frozen=True)
class MaxLen:
value: int
coll = MetadataCollection(_items=("doc",))
# Raises MetadataNotFoundError if not found
try:
max_len = coll.get_required(MaxLen) # (1)!
except MetadataNotFoundError as e:
print(f"Missing: {e.requested_type.__name__}")
- Raises
MetadataNotFoundErrorbecauseMaxLenis not in the collection.
Counting matches¶
count() for single type¶
Use count() to count items matching a type:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Gt:
value: int
coll = MetadataCollection(_items=(Gt(0), "doc", Gt(10), Gt(5)))
print(coll.count(Gt)) # 3
count() for multiple types¶
Pass multiple types to count items matching any of them:
from dataclasses import dataclass
from typing_graph import MetadataCollection
@dataclass(frozen=True)
class Gt:
value: int
@dataclass(frozen=True)
class Lt:
value: int
coll = MetadataCollection(_items=(Gt(0), Lt(100), Gt(10), "doc"))
# Count items matching any of multiple types
print(coll.count(Gt, Lt)) # 3
Result¶
You can now find metadata by type using find() and find_first(), collect all matches with find_all(), check existence with has() and is_empty, and handle missing metadata gracefully with get() and get_required().
See also¶
- Working with metadata - Tutorial introduction
- Filtering metadata - Predicate and protocol-based filtering
- Transforming metadata - Combining, sorting, and mapping
- Metadata recipes - Real-world patterns
- Metadata and Annotated - Design rationale and concepts
- MetadataCollection - Glossary definition