Get AI summaries of any video or article — Sign up free
Python Tutorial: Duck Typing and Asking Forgiveness, Not Permission (EAFP) thumbnail

Python Tutorial: Duck Typing and Asking Forgiveness, Not Permission (EAFP)

Corey Schafer·
5 min read

Based on Corey Schafer's video on YouTube. If you like this content, support the original creators by watching, liking and subscribing to their content.

TL;DR

Duck typing focuses on whether an object can perform required methods, not on whether it matches a specific class.

Briefing

Pythonic code in this lesson comes down to two closely linked habits: treat objects by what they can do (duck typing) and prefer “try it and handle failure” over “check first” (EAFP: easier to ask forgiveness than permission). Together, these approaches make code cleaner and often more robust, because they focus on behavior rather than rigid type checks.

Duck typing is introduced with a simple contrast. A “non-Pythonic” function first checks whether an incoming object is an instance of a specific class (a “duck”). If the check fails, it refuses to run. That means a “person” object—despite having the same methods—gets rejected purely because it isn’t the expected type. The Pythonic rewrite removes the type check entirely. Instead, the function directly calls the required methods (quack and fly). If the object supports those methods, the calls succeed; if not, an error occurs. The key idea is summarized as: if it walks like a duck and quacks like a duck, treat it like a duck—because the code only cares whether the object can perform the requested actions.

That leads into EAFP. The “look before you leap” style is shown as a step-by-step permission-granting approach: check whether an attribute exists, verify it’s callable, then call it—repeated for every method. The alternative is to attempt the operation immediately and catch the exception if it fails. In the example, the code tries to call quack and fly, and when a third method (bark) doesn’t exist, Python raises an AttributeError. The error is handled, and the program continues with a clear message. The same pattern is then demonstrated with dictionaries: instead of checking for each key (name, age, job), the code tries to access them and catches a KeyError when a key like job is missing.

A list example reinforces the pattern with IndexError. Rather than checking list length before accessing a specific index, the code attempts the index lookup and catches IndexError if the index doesn’t exist. The lesson also cautions that EAFP isn’t a universal rule—there are cases where targeted checks can be appropriate.

Why EAFP is often preferred: it can be faster when exceptions are rare because it avoids repeated object lookups, and it can be more readable because the “happy path” stays uncluttered by preconditions. The lesson also highlights a practical correctness issue—race conditions. A file-access example from Python documentation shows the danger of checking “can I access the file?” and then opening it later: the file’s availability can change between the check and the open. The safer approach is to try opening immediately and catch the resulting IO error if access fails.

Overall, the takeaway is pragmatic: write code that assumes the needed behavior is present, attempt the operation, and handle failures with exceptions—while using duck typing to keep interfaces flexible around capabilities instead of explicit types.

Cornell Notes

Duck typing treats objects based on behavior: if an object supports the methods a function needs, it works, regardless of its class. The lesson contrasts this with rigid type checks that reject compatible objects. EAFP (easier to ask forgiveness than permission) pairs naturally with duck typing by encouraging “try the operation, catch exceptions” instead of “check everything first.” Examples with methods, dictionaries, and lists show how AttributeError, KeyError, and IndexError can be handled cleanly. The approach is often faster and more readable, and it helps avoid race conditions by removing time gaps between checks and actions.

What does duck typing mean in practice, and how does it differ from checking an object’s type?

Duck typing means code should focus on whether an object can perform the required actions. In the example, a function needs quack() and fly(). A non-Pythonic version checks whether the object is an instance of a specific “duck” class; passing a “person” object fails even though it has quack() and fly(). The Pythonic version removes the instance check and directly calls quack() and fly(). If those methods exist, both objects work; if a method is missing, an exception occurs.

Why is “asking forgiveness” (EAFP) often preferred over “asking permission” (look before you leap)?

EAFP tries the operation first and handles failure via exceptions. The permission-based approach checks attribute existence and callability before each call, which becomes verbose and repetitive. In the method example, the EAFP version attempts quack() and fly() immediately; when bark() doesn’t exist, Python raises an AttributeError that can be printed or handled. This keeps the main logic readable and avoids repeated checks.

How do the dictionary and list examples demonstrate the same EAFP principle?

For dictionaries, the code avoids checking for each key (name, age, job). Instead, it tries to access them and catches a KeyError when a key like job is missing. For lists, it avoids checking length before accessing a specific index. Instead, it attempts the index lookup and catches IndexError if the index doesn’t exist. In both cases, the code attempts the operation and uses exceptions as the control mechanism for missing data.

What performance and readability reasons are given for EAFP?

The lesson notes that EAFP can be slightly faster when exceptions are uncommon because permission-based code may access objects multiple times during checks. It also argues that EAFP can be more readable because the “happy path” isn’t buried under repeated conditional checks for attributes, keys, or indices.

How does EAFP help avoid race conditions in the file-access example?

A race condition occurs when a check and a later action are separated in time. The unsafe pattern checks whether a file can be accessed, then opens it afterward; between those steps, the file’s availability can change, causing an error during open that may not be handled as expected. The safer EAFP pattern skips the pre-check and directly tries to open the file; if access fails, an IO error is caught and handled immediately.

Review Questions

  1. In the duck typing example, what changes when the instance-of check is removed, and what determines whether the function succeeds?
  2. Compare the permission-based approach and EAFP for calling methods: what kinds of checks are eliminated, and what replaces them?
  3. Why can a pre-check for file access lead to a race condition, and how does the EAFP approach change the timing?

Key Points

  1. 1

    Duck typing focuses on whether an object can perform required methods, not on whether it matches a specific class.

  2. 2

    Removing rigid type checks can make functions work with any object that provides the needed behavior (e.g., quack and fly).

  3. 3

    EAFP (try first, handle exceptions) replaces verbose “look before you leap” attribute/key/index checks.

  4. 4

    AttributeError, KeyError, and IndexError become the natural signals for missing methods, missing dictionary keys, and out-of-range list indices.

  5. 5

    EAFP can be faster when failures are rare because it avoids repeated lookups during permission checks.

  6. 6

    EAFP often improves readability by keeping the main logic uncluttered and handling failures in exception blocks.

  7. 7

    Pre-checks can introduce race conditions when the world changes between the check and the action; direct attempts with exception handling avoid that gap.

Highlights

Duck typing turns compatibility into a behavior question: if quack() and fly() exist, the object works—whether it’s a “duck” or a “person.”
EAFP keeps code cleaner by trying operations first and catching AttributeError/KeyError/IndexError when something is missing.
A file-access race condition can happen when “check then open” is separated in time; “try open then catch IO error” is safer.

Topics

Mentioned

  • EAFP