Search code examples
pythonduck-typingpython-3.10structural-pattern-matching

How to express hasattr() duck typing logic with structural pattern matching?


I have code that checks for named tuples and dataclasses by looking for a _fields attribute:

if hasattr(candidate, '_fields'):
    do_action()

How can I express this with Python 3.10's match/case structural pattern matching?


Solution

  • Understanding the documentation

    PEP 634 for structural pattern matching documents this capability as a class pattern:

    • Writing cls() will do an isinstance() test.
    • Adding a keyword pattern cls(attr=variable) tests for the presence of an attribute and binds the value to the variable.

    To emulate a hasattr() for duck typing:

    • Set cls to object so that any class can match.
    • Set attr to _fields, the attribute that must be present.
    • Set variable to _ if you don't need to capture the value or to some other variable name if you do want to capture the value.

    This specific example

    Your specific example, if hasattr(candidate, '_fields'): do_action(), translates to:

    match candidate:
       case object(_fields=_):
           do_action()
    

    Complete worked-out example

    This shows how all the parts fit together:

    from typing import NamedTuple
    from dataclasses import dataclass
    
    class Whale(NamedTuple):
        name: str
        num_fins: int
    
    @dataclass
    class Vehicle:
        name: str
        num_wheels: int
    
    subject = Vehicle('bicycle', 2)
        
    match subject:
        case object(num_fins=n):
            print(f'Found {n} fins')
        case object(num_wheels=_):
            print(f'Found wheeled object')
        case _:
            print('Unknown')
    

    This script outputs:

    Found wheeled object