[{"data":1,"prerenderedAt":782},["ShallowReactive",2],{"blog-\u002Fblog\u002Fpython-abc-protocols-explained":3},{"id":4,"title":5,"body":6,"description":768,"difficulty":769,"extension":770,"framework":771,"frameworkSlug":53,"meta":772,"navigation":89,"order":131,"path":773,"qaPath":774,"seo":775,"stem":776,"subtopic":777,"topic":778,"topicSlug":779,"updated":780,"__hash__":781},"blog\u002Fblog\u002Fpython-abc-protocols-explained.md","Python Abstract Base Classes and Protocols Explained — ABCs, Duck Typing, and Protocol",{"type":7,"value":8,"toc":758},"minimark",[9,14,37,41,48,180,187,191,206,378,385,389,411,415,441,520,524,543,670,677,681,705,719,723,754],[10,11,13],"h2",{"id":12},"python-abcs-and-protocols-explained","Python ABCs and Protocols, explained",[15,16,17,18,22,23,27,28,32,33,36],"p",{},"Python is famous for ",[19,20,21],"strong",{},"duck typing"," — \"if it walks like a duck...\". So why does it also have\nabstract base classes and ",[24,25,26],"code",{},"Protocol","? Because sometimes you want to ",[29,30,31],"em",{},"declare"," and ",[29,34,35],{},"enforce","\nan interface. This guide covers the three ways to express \"must support these methods\".",[10,38,40],{"id":39},"duck-typing-the-baseline","Duck typing — the baseline",[15,42,43,44,47],{},"By default, Python doesn't check types; it checks ",[19,45,46],{},"capabilities at runtime",". If an object\nhas the method you call, it works:",[49,50,55],"pre",{"className":51,"code":52,"language":53,"meta":54,"style":54},"language-python shiki shiki-themes github-light github-dark","def make_it_quack(thing):\n    thing.quack()      # works for ANY object that has .quack()\n\nclass Duck:\n    def quack(self): print(\"Quack\")\nclass Person:\n    def quack(self): print(\"I'm quacking\")\n\nmake_it_quack(Duck())     # fine\nmake_it_quack(Person())   # also fine — no shared base needed\n","python","",[24,56,57,74,84,91,103,129,139,157,162,171],{"__ignoreMap":54},[58,59,62,66,70],"span",{"class":60,"line":61},"line",1,[58,63,65],{"class":64},"szBVR","def",[58,67,69],{"class":68},"sScJk"," make_it_quack",[58,71,73],{"class":72},"sVt8B","(thing):\n",[58,75,77,80],{"class":60,"line":76},2,[58,78,79],{"class":72},"    thing.quack()      ",[58,81,83],{"class":82},"sJ8bj","# works for ANY object that has .quack()\n",[58,85,87],{"class":60,"line":86},3,[58,88,90],{"emptyLinePlaceholder":89},true,"\n",[58,92,94,97,100],{"class":60,"line":93},4,[58,95,96],{"class":64},"class",[58,98,99],{"class":68}," Duck",[58,101,102],{"class":72},":\n",[58,104,106,109,112,115,119,122,126],{"class":60,"line":105},5,[58,107,108],{"class":64},"    def",[58,110,111],{"class":68}," quack",[58,113,114],{"class":72},"(self): ",[58,116,118],{"class":117},"sj4cs","print",[58,120,121],{"class":72},"(",[58,123,125],{"class":124},"sZZnC","\"Quack\"",[58,127,128],{"class":72},")\n",[58,130,132,134,137],{"class":60,"line":131},6,[58,133,96],{"class":64},[58,135,136],{"class":68}," Person",[58,138,102],{"class":72},[58,140,142,144,146,148,150,152,155],{"class":60,"line":141},7,[58,143,108],{"class":64},[58,145,111],{"class":68},[58,147,114],{"class":72},[58,149,118],{"class":117},[58,151,121],{"class":72},[58,153,154],{"class":124},"\"I'm quacking\"",[58,156,128],{"class":72},[58,158,160],{"class":60,"line":159},8,[58,161,90],{"emptyLinePlaceholder":89},[58,163,165,168],{"class":60,"line":164},9,[58,166,167],{"class":72},"make_it_quack(Duck())     ",[58,169,170],{"class":82},"# fine\n",[58,172,174,177],{"class":60,"line":173},10,[58,175,176],{"class":72},"make_it_quack(Person())   ",[58,178,179],{"class":82},"# also fine — no shared base needed\n",[15,181,182,183,186],{},"This is flexible but offers no early guarantee — the failure (",[24,184,185],{},"AttributeError",") only shows up\nwhen the missing method is actually called.",[10,188,190],{"id":189},"abstract-base-classes","Abstract base classes",[15,192,193,194,197,198,201,202,205],{},"An ",[19,195,196],{},"ABC"," defines an interface that subclasses ",[19,199,200],{},"must"," implement. Mark required methods with\n",[24,203,204],{},"@abstractmethod",", and the class can't be instantiated until they're all provided:",[49,207,209],{"className":51,"code":208,"language":53,"meta":54,"style":54},"from abc import ABC, abstractmethod\n\nclass Shape(ABC):\n    @abstractmethod\n    def area(self):\n        ...\n\nShape()                  # TypeError — can't instantiate abstract class\n\nclass Circle(Shape):\n    def __init__(self, r):\n        self.r = r\n    def area(self):      # must implement, or Circle is abstract too\n        return 3.14159 * self.r ** 2\n\nCircle(2).area()         # 12.566...\n",[24,210,211,228,232,246,251,261,266,270,278,282,296,307,322,335,358,363],{"__ignoreMap":54},[58,212,213,216,219,222,225],{"class":60,"line":61},[58,214,215],{"class":64},"from",[58,217,218],{"class":72}," abc ",[58,220,221],{"class":64},"import",[58,223,224],{"class":117}," ABC",[58,226,227],{"class":72},", abstractmethod\n",[58,229,230],{"class":60,"line":76},[58,231,90],{"emptyLinePlaceholder":89},[58,233,234,236,239,241,243],{"class":60,"line":86},[58,235,96],{"class":64},[58,237,238],{"class":68}," Shape",[58,240,121],{"class":72},[58,242,196],{"class":117},[58,244,245],{"class":72},"):\n",[58,247,248],{"class":60,"line":93},[58,249,250],{"class":68},"    @abstractmethod\n",[58,252,253,255,258],{"class":60,"line":105},[58,254,108],{"class":64},[58,256,257],{"class":68}," area",[58,259,260],{"class":72},"(self):\n",[58,262,263],{"class":60,"line":131},[58,264,265],{"class":117},"        ...\n",[58,267,268],{"class":60,"line":141},[58,269,90],{"emptyLinePlaceholder":89},[58,271,272,275],{"class":60,"line":159},[58,273,274],{"class":72},"Shape()                  ",[58,276,277],{"class":82},"# TypeError — can't instantiate abstract class\n",[58,279,280],{"class":60,"line":164},[58,281,90],{"emptyLinePlaceholder":89},[58,283,284,286,289,291,294],{"class":60,"line":173},[58,285,96],{"class":64},[58,287,288],{"class":68}," Circle",[58,290,121],{"class":72},[58,292,293],{"class":68},"Shape",[58,295,245],{"class":72},[58,297,299,301,304],{"class":60,"line":298},11,[58,300,108],{"class":64},[58,302,303],{"class":117}," __init__",[58,305,306],{"class":72},"(self, r):\n",[58,308,310,313,316,319],{"class":60,"line":309},12,[58,311,312],{"class":117},"        self",[58,314,315],{"class":72},".r ",[58,317,318],{"class":64},"=",[58,320,321],{"class":72}," r\n",[58,323,325,327,329,332],{"class":60,"line":324},13,[58,326,108],{"class":64},[58,328,257],{"class":68},[58,330,331],{"class":72},"(self):      ",[58,333,334],{"class":82},"# must implement, or Circle is abstract too\n",[58,336,338,341,344,347,350,352,355],{"class":60,"line":337},14,[58,339,340],{"class":64},"        return",[58,342,343],{"class":117}," 3.14159",[58,345,346],{"class":64}," *",[58,348,349],{"class":117}," self",[58,351,315],{"class":72},[58,353,354],{"class":64},"**",[58,356,357],{"class":117}," 2\n",[58,359,361],{"class":60,"line":360},15,[58,362,90],{"emptyLinePlaceholder":89},[58,364,366,369,372,375],{"class":60,"line":365},16,[58,367,368],{"class":72},"Circle(",[58,370,371],{"class":117},"2",[58,373,374],{"class":72},").area()         ",[58,376,377],{"class":82},"# 12.566...\n",[15,379,380,381,384],{},"This turns \"you forgot to implement ",[24,382,383],{},"area","\" from a runtime surprise into an error at object\ncreation.",[10,386,388],{"id":387},"why-use-abcs-over-plain-classes","Why use ABCs over plain classes",[15,390,391,392,395,396,399,400,406,407,410],{},"ABCs give you three things plain classes don't: ",[19,393,394],{},"enforcement"," (can't instantiate without\nthe methods), ",[19,397,398],{},"documentation"," (the interface is explicit), and ",[19,401,402,405],{},[24,403,404],{},"isinstance"," grouping","\n(register virtual subclasses, and ",[24,408,409],{},"isinstance(obj, Shape)"," reflects the contract). Use them\nwhen you have a real family of types that must share an interface.",[10,412,414],{"id":413},"collectionsabc","collections.abc",[15,416,417,418,420,421,424,425,424,428,424,431,424,434,437,438,440],{},"The standard library ships ready-made ABCs for the container protocols in\n",[24,419,414],{}," — ",[24,422,423],{},"Iterable",", ",[24,426,427],{},"Iterator",[24,429,430],{},"Sequence",[24,432,433],{},"Mapping",[24,435,436],{},"Hashable",", and more. You\ncan inherit from them to get mixin methods for free, or use them in ",[24,439,404],{}," checks:",[49,442,444],{"className":51,"code":443,"language":53,"meta":54,"style":54},"from collections.abc import Iterable, Mapping\n\nisinstance([1, 2], Iterable)     # True\nisinstance({}, Mapping)          # True\n\nclass MyList(Sequence):          # implement __getitem__ + __len__...\n    ...                          # ...and get __contains__, __iter__, etc. free\n",[24,445,446,458,462,482,491,495,512],{"__ignoreMap":54},[58,447,448,450,453,455],{"class":60,"line":61},[58,449,215],{"class":64},[58,451,452],{"class":72}," collections.abc ",[58,454,221],{"class":64},[58,456,457],{"class":72}," Iterable, Mapping\n",[58,459,460],{"class":60,"line":76},[58,461,90],{"emptyLinePlaceholder":89},[58,463,464,466,469,472,474,476,479],{"class":60,"line":86},[58,465,404],{"class":117},[58,467,468],{"class":72},"([",[58,470,471],{"class":117},"1",[58,473,424],{"class":72},[58,475,371],{"class":117},[58,477,478],{"class":72},"], Iterable)     ",[58,480,481],{"class":82},"# True\n",[58,483,484,486,489],{"class":60,"line":93},[58,485,404],{"class":117},[58,487,488],{"class":72},"({}, Mapping)          ",[58,490,481],{"class":82},[58,492,493],{"class":60,"line":105},[58,494,90],{"emptyLinePlaceholder":89},[58,496,497,499,502,504,506,509],{"class":60,"line":131},[58,498,96],{"class":64},[58,500,501],{"class":68}," MyList",[58,503,121],{"class":72},[58,505,430],{"class":68},[58,507,508],{"class":72},"):          ",[58,510,511],{"class":82},"# implement __getitem__ + __len__...\n",[58,513,514,517],{"class":60,"line":141},[58,515,516],{"class":117},"    ...",[58,518,519],{"class":82},"                          # ...and get __contains__, __iter__, etc. free\n",[10,521,523],{"id":522},"typingprotocol-structural-typing","typing.Protocol — structural typing",[15,525,526,527,530,531,534,535,538,539,542],{},"ABCs require ",[19,528,529],{},"explicit inheritance"," (nominal typing). ",[24,532,533],{},"typing.Protocol"," instead checks\n",[19,536,537],{},"structure",": any class with the right methods satisfies the protocol, with ",[19,540,541],{},"no\ninheritance needed"," — duck typing that a static type checker can verify:",[49,544,546],{"className":51,"code":545,"language":53,"meta":54,"style":54},"from typing import Protocol\n\nclass Drawable(Protocol):\n    def draw(self) -> str: ...\n\ndef render(item: Drawable) -> str:\n    return item.draw()\n\nclass Button:                    # does NOT inherit Drawable\n    def draw(self) -> str:\n        return \"[Button]\"\n\nrender(Button())                 # type checker accepts it — it has draw()\n",[24,547,548,560,564,577,596,600,614,622,626,639,651,658,662],{"__ignoreMap":54},[58,549,550,552,555,557],{"class":60,"line":61},[58,551,215],{"class":64},[58,553,554],{"class":72}," typing ",[58,556,221],{"class":64},[58,558,559],{"class":72}," Protocol\n",[58,561,562],{"class":60,"line":76},[58,563,90],{"emptyLinePlaceholder":89},[58,565,566,568,571,573,575],{"class":60,"line":86},[58,567,96],{"class":64},[58,569,570],{"class":68}," Drawable",[58,572,121],{"class":72},[58,574,26],{"class":68},[58,576,245],{"class":72},[58,578,579,581,584,587,590,593],{"class":60,"line":93},[58,580,108],{"class":64},[58,582,583],{"class":68}," draw",[58,585,586],{"class":72},"(self) -> ",[58,588,589],{"class":117},"str",[58,591,592],{"class":72},": ",[58,594,595],{"class":117},"...\n",[58,597,598],{"class":60,"line":105},[58,599,90],{"emptyLinePlaceholder":89},[58,601,602,604,607,610,612],{"class":60,"line":131},[58,603,65],{"class":64},[58,605,606],{"class":68}," render",[58,608,609],{"class":72},"(item: Drawable) -> ",[58,611,589],{"class":117},[58,613,102],{"class":72},[58,615,616,619],{"class":60,"line":141},[58,617,618],{"class":64},"    return",[58,620,621],{"class":72}," item.draw()\n",[58,623,624],{"class":60,"line":159},[58,625,90],{"emptyLinePlaceholder":89},[58,627,628,630,633,636],{"class":60,"line":164},[58,629,96],{"class":64},[58,631,632],{"class":68}," Button",[58,634,635],{"class":72},":                    ",[58,637,638],{"class":82},"# does NOT inherit Drawable\n",[58,640,641,643,645,647,649],{"class":60,"line":173},[58,642,108],{"class":64},[58,644,583],{"class":68},[58,646,586],{"class":72},[58,648,589],{"class":117},[58,650,102],{"class":72},[58,652,653,655],{"class":60,"line":298},[58,654,340],{"class":64},[58,656,657],{"class":124}," \"[Button]\"\n",[58,659,660],{"class":60,"line":309},[58,661,90],{"emptyLinePlaceholder":89},[58,663,664,667],{"class":60,"line":324},[58,665,666],{"class":72},"render(Button())                 ",[58,668,669],{"class":82},"# type checker accepts it — it has draw()\n",[15,671,672,673,676],{},"This gives you the flexibility of duck typing ",[19,674,675],{},"plus"," static safety from mypy\u002Fpyright.",[10,678,680],{"id":679},"abcs-vs-protocols-which-to-choose","ABCs vs Protocols — which to choose",[682,683,684,695],"ul",{},[685,686,687,688,690,691,694],"li",{},"Use an ",[19,689,196],{}," when you own the hierarchy and want ",[19,692,693],{},"runtime enforcement"," and shared mixin\nbehaviour through inheritance.",[685,696,697,698,700,701,704],{},"Use a ",[19,699,26],{}," when you want to type-check ",[19,702,703],{},"third-party or unrelated classes"," by\nshape, without forcing them to inherit anything.",[15,706,707,708,711,712,714,715,718],{},"Add ",[24,709,710],{},"@runtime_checkable"," to a Protocol if you also need ",[24,713,404],{}," checks against it (it\nthen verifies method ",[29,716,717],{},"presence",", not signatures).",[10,720,722],{"id":721},"recap","Recap",[15,724,725,728,729,731,732,734,735,737,738,741,742,744,745,749,750,753],{},[19,726,727],{},"Duck typing"," is Python's default — capabilities are checked at call time, flexible but\nlate-failing. ",[19,730,190],{}," (",[24,733,196],{}," + ",[24,736,204],{},") ",[29,739,740],{},"declare and enforce"," an\ninterface, blocking instantiation until it's implemented, and ",[24,743,414],{}," provides\nready-made container ABCs. ",[19,746,747],{},[24,748,533],{}," brings ",[19,751,752],{},"structural"," typing: classes match\nby having the right methods, no inheritance required, giving duck typing that static checkers\ncan verify. Choose ABCs for owned hierarchies with runtime enforcement, Protocols for typing\nunrelated classes by shape.",[755,756,757],"style",{},"html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":54,"searchDepth":76,"depth":76,"links":759},[760,761,762,763,764,765,766,767],{"id":12,"depth":76,"text":13},{"id":39,"depth":76,"text":40},{"id":189,"depth":76,"text":190},{"id":387,"depth":76,"text":388},{"id":413,"depth":76,"text":414},{"id":522,"depth":76,"text":523},{"id":679,"depth":76,"text":680},{"id":721,"depth":76,"text":722},"How Python defines interfaces — abstract base classes with @abstractmethod, collections.abc, duck typing, and typing.Protocol for structural (static) typing.","hard","md","Python",{},"\u002Fblog\u002Fpython-abc-protocols-explained","\u002Fpython\u002Foop\u002Fabc-protocols",{"title":5,"description":768},"blog\u002Fpython-abc-protocols-explained","Abstract Base Classes & Protocols","Object-Oriented Programming","oop","2026-06-19","uJmJ4noe9XEqpC3loEH9rwEdmZZuMiVsEoJuu65ZEEs",1782244094218]