[Python] Get the first non-null value for a key in multiple mappings/dictionaries

Discussão em 'Python' iniciado por Stack, Agosto 19, 2025.

  1. Stack

    Stack Membro Participativo

    I am wondering if there are cleaner ways to retrieve the first non-null value for a key in an iterable of mappings/dictionaries than what I am currently doing.

    Use case


    I have an object which stores its internal attributes in a TypedDict. I am implementing a flyweight pattern by allowing subclasses to freeze values and/or define defaults at the class-level to save memory.

    from typing import ClassVar, TypedDict


    class SomeBaseClass:
    """Some class that can define some behaviour."""

    class State(TypedDict, total=False):
    """Defines the fields that the base class can have."""

    a: int
    b: str

    state: State
    """Instance-level field values."""
    frozen: ClassVar[State] = State()
    """Class-level frozen field values."""
    defaults: ClassVar[State] = State()
    """Class-level default field values."""

    def __init__(self, state: State) -> None:
    """Initialize the object with a state."""
    self.state = state
    if set(self.state).intersection(self.frozen):
    msg = "Attempted to override frozen values."
    raise ValueError(msg)

    Current implementation(s)


    To access an instance's attribute value, I need to return the first non-null value found in state, frozen, or default, and raise an error if no such value exists.

    I have found two implementations that work (see property a and b). But I have a feeling that there might be a cleaner/slicker way to achieve this behaviour.

    Option a: use .get()


    @property
    def a(self) -> int:
    """Return the first non-null value using `.get()`."""
    if (
    output := self.state.get("a", self.frozen.get("a", self.defaults.get("a")))
    ) is None:
    msg = "`a` not found in state, frozen or defaults"
    raise ValueError(msg)
    return output

    • Pros: retrieving the value (or lack thereof) can be achieved as a one-liner.
    • Cons: the nested structure feels clunky and not incredibly readable
    Option b: use next()


    @property
    def b(self) -> str:
    """Return the first non-null value of `b` using `next`."""
    if (
    output := next(
    (
    value
    for mapping in (self.state, self.frozen, self.defaults)
    if (value := mapping.get("b")) is not None
    ),
    None,
    )
    ) is None:
    msg = "`b` not found in state, frozen or defaults"
    raise ValueError(msg)
    return output

    • Pros: Extendable to more mappings (though not needed in my use case)
    • Cons: takes more lines of code
    Notes


    I thought of using the or syntax:

    return self.state.get("a") or self.frozen.get("a") or self.defaults.get("a")


    This won't work as my values are allowed to be falsy (i.e. 0 for an int variable, "" for a str variable, an empty collection etc.)

    Continue reading...

Compartilhe esta Página