<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Carlos Saldaña</title>
    <description>The latest articles on Forem by Carlos Saldaña (@csalda3a).</description>
    <link>https://forem.com/csalda3a</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F492159%2F100251ec-0f45-4f66-af7a-1d84c679344b.jpg</url>
      <title>Forem: Carlos Saldaña</title>
      <link>https://forem.com/csalda3a</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/csalda3a"/>
    <language>en</language>
    <item>
      <title>A prompt is not a conversation. It's a component contract.</title>
      <dc:creator>Carlos Saldaña</dc:creator>
      <pubDate>Mon, 25 May 2026 21:48:25 +0000</pubDate>
      <link>https://forem.com/csalda3a/a-prompt-is-not-a-conversation-its-a-component-contract-4jk8</link>
      <guid>https://forem.com/csalda3a/a-prompt-is-not-a-conversation-its-a-component-contract-4jk8</guid>
      <description>&lt;p&gt;Most of us use LLMs by trial and error. This post gives you a structure: the building blocks of an LLM, and a reusable template for writing production prompts.&lt;/p&gt;

&lt;h3&gt;
  
  
  What is an LLM?
&lt;/h3&gt;

&lt;p&gt;Foundation models are very large models pretrained on internet-data; that's what builds Generative AI. With a foundation model, you can adapt one pretrained model to many tasks.&lt;/p&gt;

&lt;p&gt;A Large Language Model (LLM) is a foundation model for text, and at its core, the same FM can be used for many tasks: summarisation, classification, translation, code generation.&lt;/p&gt;

&lt;p&gt;So what an LLM does is predict the next word (next token) in a sequence. At each step it checks the surrounding context of what it has seen so far and then produces a probability distribution over the possible next tokens. Running this in a loop produces fluent, coherent new content. So basically, an LLM gets text and returns text. The input is called the prompt and the output is called completion.&lt;/p&gt;

&lt;h3&gt;
  
  
  What goes into a prompt
&lt;/h3&gt;

&lt;p&gt;A prompt is any input given to a generative model to produce a desired output. Prompt engineering is the practice of designing and refining those prompts to get the best possible results from the model. Refining a prompt means experimenting with the factors that influence the model's output. A vague prompt can lead to many reasonable responses; every constraint you add reduces the number of possible responses.&lt;/p&gt;

&lt;p&gt;A well-structured prompt is usually assembled from four parts.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Building block&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Role&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Instruction&lt;/td&gt;
&lt;td&gt;The task you want performed.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;Relevant background that frames the situation for the model.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Input data&lt;/td&gt;
&lt;td&gt;The specific content the task should operate on.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Output indicator&lt;/td&gt;
&lt;td&gt;A description (or example) of the form the response should take.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The instruction and context here are two of the template slots, &lt;strong&gt;Task&lt;/strong&gt; and &lt;strong&gt;Context&lt;/strong&gt;, we'll pull together at the end.&lt;/p&gt;

&lt;p&gt;And best practice for writing prompts can be organised into four dimensions. Strong prompts attend to all four.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;strong&gt;Dimension&lt;/strong&gt;&lt;/th&gt;
&lt;th&gt;&lt;strong&gt;Practice&lt;/strong&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Clarity&lt;/td&gt;
&lt;td&gt;Use simple, direct language. Avoid ambiguous or overly complex terminology so the prompt is easily understood.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Context&lt;/td&gt;
&lt;td&gt;Provide relevant background and specific details to guide the model's understanding of the situation.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Precision&lt;/td&gt;
&lt;td&gt;Clearly state the type of response you want, and use examples to illustrate the expected output.&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Role-play / Persona&lt;/td&gt;
&lt;td&gt;Write the prompt from the perspective of a specific character or expert, with enough detail for the model to assume that role effectively.&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Think of a prompt like a search beam. Vague = wide beam, the model lands somewhere in a large valid region. Each constraint narrows the beam. Specificity isn't politeness toward the model; it's aiming.&lt;/p&gt;

&lt;h3&gt;
  
  
  Who reads the output: code or a person
&lt;/h3&gt;

&lt;p&gt;The output is the more interesting part for us. An LLM's output has two possible audiences, a parser and a person, and you write a contract for each. Even when a person reads the output, "looks fine" isn't the same as "matches what I needed." When an LLM's output is read by &lt;em&gt;code&lt;/em&gt; instead of &lt;em&gt;eyes&lt;/em&gt;, the output is an API response and the prompt is its schema. A human forgives a messy answer; &lt;code&gt;json.loads()&lt;/code&gt; doesn't. It either succeeds or throws. Without an explicit spec, the &lt;em&gt;model&lt;/em&gt; decides format, length, tone, and depth, and it picks something plausible but generic. Controlling output here means moving that decision from the model to you.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two kinds of control:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Format control:&lt;/strong&gt; the &lt;em&gt;shape&lt;/em&gt; of the output (JSON keys and types; or prose vs. bullets vs. table, headings, sections).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Behavior control:&lt;/strong&gt; what the model may and may not &lt;em&gt;do&lt;/em&gt; (e.g. "valid Terraform only, no comments"; or "concise executive tone, no technical jargon").&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Each audience fails differently, and one of them fails silently:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Parser&lt;/strong&gt; (output read by code)&lt;/th&gt;
&lt;th&gt;
&lt;strong&gt;Person&lt;/strong&gt; (output read by a human)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;What breaks&lt;/td&gt;
&lt;td&gt;Markdown fences, chatty preamble, invented/renamed keys, numbers as strings&lt;/td&gt;
&lt;td&gt;Too long, wrong tone, missing a section, pitched at the wrong level&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;How it breaks&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Loud:&lt;/strong&gt; &lt;code&gt;json.loads()&lt;/code&gt; throws, the pipeline stops; you notice immediately&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Quiet:&lt;/strong&gt; output looks fluent and complete; the gap only shows on a second read, and nobody flags it&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;A loud failure is annoying; a quiet failure is more dangerous because a subtly off-target answer can go unnoticed.&lt;/p&gt;

&lt;p&gt;There are some mitigations you can apply for better results:&lt;/p&gt;

&lt;p&gt;&lt;em&gt;For the parser:&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Specify the exact schema.&lt;/li&gt;
&lt;li&gt;Forbid the noise.&lt;/li&gt;
&lt;li&gt;Give one example of the exact shape.&lt;/li&gt;
&lt;li&gt;Set &lt;code&gt;temperature: 0&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Use native &lt;strong&gt;structured outputs / tool-calling&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;em&gt;For the person:&lt;/em&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;State the structure explicitly.&lt;/li&gt;
&lt;li&gt;Give a length budget.&lt;/li&gt;
&lt;li&gt;Name the audience.&lt;/li&gt;
&lt;li&gt;List the required elements.&lt;/li&gt;
&lt;li&gt;For a recurring format, show one example of the desired output.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;So the point here is that prompt-only format control is a request; decode-time constraints are a guarantee. Treat the model's output as an API response, and the prompt as its schema.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Constraints&lt;/strong&gt; and &lt;strong&gt;Output&lt;/strong&gt; are template slots too: the rules that prune behavior, and the exact shape you contract for.&lt;/p&gt;

&lt;h3&gt;
  
  
  The system and user split
&lt;/h3&gt;

&lt;p&gt;We already saw the role as one of the four dimensions of an effective prompt. It's the part lots of people forget about.&lt;/p&gt;

&lt;p&gt;A prompt can include three types of messages: system, user, and assistant.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;System message:&lt;/strong&gt; defines the model's behavior, rules, and overall role.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;User message:&lt;/strong&gt; contains the request or input for the current task.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Assistant message:&lt;/strong&gt; previous responses from the model, used as conversational context or examples.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The "type" is usually set through the &lt;code&gt;role&lt;/code&gt; field of each message.&lt;/p&gt;

&lt;p&gt;The system message is the component's configuration, while the user message is the input for a specific call.&lt;/p&gt;

&lt;p&gt;The system prompt defines persistent behavior and rules. The user message provides the variable data for that particular request. The system sets how the component behaves in general; the user message tells it what to do right now.&lt;/p&gt;

&lt;p&gt;Why do roles work? It's not magic.&lt;/p&gt;

&lt;p&gt;When you say, "Act as a senior security engineer," the model shifts its output toward patterns statistically associated with that kind of writing in its training data.&lt;/p&gt;

&lt;p&gt;Likewise, "Explain this to a junior developer" pushes the model toward simpler, more educational, and more heavily explained responses.&lt;/p&gt;

&lt;p&gt;A role doesn't give the model a real personality. It changes the probability distribution of the kind of text the model is likely to generate. This is the &lt;strong&gt;Role&lt;/strong&gt; slot, the first line of the template.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why the system/user split matters in production:&lt;/strong&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Caching.&lt;/strong&gt; The system message is usually stable and reused across requests, which makes it ideal for prompt caching. Keep the system prompt consistent, and place most of the changing data in the user message.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Testability.&lt;/strong&gt; The system prompt is one of the highest-leverage parts of an AI application. Treat it like code: version it, compare changes, and test it carefully.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Security.&lt;/strong&gt; Trusted instructions should live in the system message. Untrusted content (user input, retrieved documents, tool outputs) should stay in the user message. When untrusted text lands somewhere the model treats as instructions, you get &lt;strong&gt;prompt injection&lt;/strong&gt;. Clean separation is the first line of defense.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Showing the model examples
&lt;/h3&gt;

&lt;p&gt;One good prompt gets you far. A few techniques get you further. The most useful one: showing the model examples.&lt;/p&gt;

&lt;p&gt;In few-shot prompting, the prompt includes a few worked demonstrations. The model uses in-context learning to infer the pattern and apply it to the new input.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;few-shot examples are unit tests that double as a spec.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why it works&lt;/strong&gt;: the model is a pattern continuator. A few input→output pairs establish a strong, low-ambiguity pattern, and the model continues it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;When to use which:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Zero-shot:&lt;/strong&gt; simple, common tasks the model has clearly seen many times.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "role": "user",
    "content": "Summarize this article in 3 bullet points."
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;One-shot:&lt;/strong&gt; when you mainly need to pin the &lt;em&gt;format&lt;/em&gt;.&lt;br&gt;
&lt;/p&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
  {
    "role": "user",
    "content": "Convert countries to JSON.\n\nExample:\nFrance -&amp;gt; {\"country\": \"France\"}\n\nNow convert:\nBrazil"
  }
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Few-shot:&lt;/strong&gt; classification, structured output, code style, anything with a specific schema or edge cases the model wouldn't guess.&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Those worked demonstrations are the &lt;strong&gt;Examples&lt;/strong&gt; slot.&lt;/p&gt;

&lt;p&gt;In the example below, the last message is the new input; the model produces the &lt;code&gt;assistant&lt;/code&gt; turn.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[
   {"role": "user",      "content": "Today the weather is fantastic"},
   {"role": "assistant", "content": "positive"},
   {"role": "user",      "content": "I don't like your attitude"},
   {"role": "assistant", "content": "negative"},
   {"role": "user",      "content": "That shot selection was awful"},
]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Tokens are what you pay for
&lt;/h3&gt;

&lt;p&gt;LLMs are not free, and the token is the meter. For an LLM, it's the unit of both cost and latency. For most of us the mental model is familiar: tokens are network payload, and each LLM call is a billable, latency-bearing API request.&lt;/p&gt;

&lt;p&gt;Output tokens dominate latency. The model generates text one token at a time, and each new token depends on all the previous ones, so generation has to happen sequentially.&lt;/p&gt;

&lt;p&gt;Input works differently: the prompt is processed in a single parallel "prefill" pass, which is relatively fast (even though you still pay for those tokens).&lt;/p&gt;

&lt;p&gt;Four levers do most of the work:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Compress the input&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Instead of sending entire documents, summarize them first or extract only the relevant parts. Most prompts ship context the model never reads.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Limit and structure the output&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Set &lt;code&gt;max_tokens&lt;/code&gt;, prefer structured formats like JSON or arrays instead of long prose, and ask for concise summaries such as "keep under 120 tokens."&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use the right model for the task&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Small models are faster and cheaper for simple work like classification, routing, or extraction. Save the stronger models for tasks that actually require reasoning or synthesis.&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;strong&gt;Use caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;There are two different kinds:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Prompt/prefix caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Reuses stable prompt sections like system prompts, examples, or reference documents. Since the provider caches this server-side, you avoid recomputing the expensive input processing step.&lt;/p&gt;

&lt;p&gt;Practical implication: put stable content first and variable content last to maximize cache hits.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Response/semantic caching&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Your own infrastructure stores previous answers and reuses them when the same or a very similar request appears again. This caches outputs, not prompts.&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  A Reusable Prompt Template
&lt;/h3&gt;

&lt;p&gt;All that we've seen here gives us the structure to build a template to use when working on a production prompt.&lt;/p&gt;

&lt;p&gt;The template &lt;strong&gt;R-T-C-C-E-O&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;[ROLE]        Who the model acts as. Sets the output distribution.
[TASK]        The one thing to do, stated unambiguously.
[CONTEXT]     Inputs, background, data; clearly delimited from instructions.
[CONSTRAINTS] Rules that prune the space. Each maps to a real failure mode.
[EXAMPLES]    1–3 representative input→output pairs (for structured/edge-case tasks).
[OUTPUT]      The exact shape of the response. Schema + example if machine-consumed.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Not every prompt needs all six, but every prompt should be a &lt;em&gt;deliberate&lt;/em&gt; subset, not an accident.&lt;/p&gt;

&lt;p&gt;Finally, let's make the production checklist, a pre-flight pass before a prompt ships:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Role&lt;/strong&gt; set and matched to the task?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Task:&lt;/strong&gt; could a competent stranger misread it?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraints:&lt;/strong&gt; does each map to a failure mode you've actually seen? Drop the decorative ones.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Output contract:&lt;/strong&gt; explicit; schema + example if consumed by code?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Format guarantee:&lt;/strong&gt; decode-time constraint (structured output / tool call), not just a worded request?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Examples:&lt;/strong&gt; present for classification/structured work; diverse, consistent, minimal?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Sampling:&lt;/strong&gt; &lt;code&gt;temperature&lt;/code&gt; matched to the task?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Token budget:&lt;/strong&gt; &lt;code&gt;max_tokens&lt;/code&gt; capped; output format compact?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; right-sized, not just "the strong one"?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cache-friendliness:&lt;/strong&gt; stable content first, variable content last?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Injection safety:&lt;/strong&gt; instructions in the system message, data in the user message?&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Versioned &amp;amp; tested:&lt;/strong&gt; in source control with a few regression cases?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So we covered six slots: Role, Task, Context, Constraints, Examples, Output, plus a pre-flight checklist. Run every production prompt through both, and you've turned a hopeful request into a tested component. &lt;strong&gt;A prompt is not a conversation. It's a component contract.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>llm</category>
      <category>promptengineering</category>
      <category>programming</category>
    </item>
    <item>
      <title>The Protocol Behind Python's Containers</title>
      <dc:creator>Carlos Saldaña</dc:creator>
      <pubDate>Tue, 19 May 2026 14:35:05 +0000</pubDate>
      <link>https://forem.com/csalda3a/the-protocol-behind-pythons-containers-10km</link>
      <guid>https://forem.com/csalda3a/the-protocol-behind-pythons-containers-10km</guid>
      <description>&lt;p&gt;Lists, tuples, dicts, and sets look like four different things. Underneath, they share the same idea: they all implement a small set of protocols, and once you see that, the rest stops feeling arbitrary. &lt;/p&gt;

&lt;h3&gt;
  
  
  What is a protocol?
&lt;/h3&gt;

&lt;p&gt;A protocol in Python is an informal contract defined by behavior, not by declaration. Anything that behaves like a sequence is a sequence, as far as Python cares. This style of typing has a name, it’s called structural typing, or as most of us know it, duck typing.("if it walks like a duck...").&lt;/p&gt;

&lt;p&gt;Think of this like the USB port. A USB port doesn't ask "are you a keyboard?" or "are you a hard drive?" It exposes a physical shape and a data protocol. Anything that fits the shape and speaks the protocol works.&lt;/p&gt;

&lt;h3&gt;
  
  
  &lt;strong&gt;Iterators and Iterables&lt;/strong&gt;
&lt;/h3&gt;

&lt;p&gt;If you only learn one thing about Python containers, learn the difference between an iterable and an iterator.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An &lt;strong&gt;iterable&lt;/strong&gt; is an object that we can iterate over, it's an object that can produce an iterator. To make this happen, it implements &lt;code&gt;__iter__()&lt;/code&gt;, which returns a fresh iterator each time it's called.&lt;/li&gt;
&lt;li&gt;An &lt;strong&gt;iterator&lt;/strong&gt; is an object that &lt;em&gt;produces values one at a time&lt;/em&gt;. It implements &lt;code&gt;__next__()&lt;/code&gt; (and, by convention, &lt;code&gt;__iter__()&lt;/code&gt; returning &lt;code&gt;self&lt;/code&gt;). It raises &lt;code&gt;StopIteration&lt;/code&gt; when there are no more values.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are a few &lt;strong&gt;built-in iterables&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;List&lt;/li&gt;
&lt;li&gt;Tuples&lt;/li&gt;
&lt;li&gt;Strings&lt;/li&gt;
&lt;li&gt;Dictionaries&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let’s try an example to understand why a list is iterable but not an iterator:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;nums&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="mi"&gt;3&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nb"&gt;TypeError&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;list&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="nb"&gt;object&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="ow"&gt;not&lt;/span&gt; &lt;span class="n"&gt;an&lt;/span&gt; &lt;span class="n"&gt;iterator&lt;/span&gt;
&lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;1&lt;/span&gt;
&lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="mi"&gt;2&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;for&lt;/code&gt; loop is doing this exact dance. &lt;code&gt;for i in nums:&lt;/code&gt; it’s roughly doing:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;_it&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;iter&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;nums&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;x&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;next&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;_it&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="nb"&gt;StopIteration&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;break&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That’s the whole &lt;strong&gt;protocol&lt;/strong&gt;. Once you understand this, things like &lt;code&gt;for&lt;/code&gt; loops, generators, &lt;code&gt;zip&lt;/code&gt;, &lt;code&gt;map&lt;/code&gt;, comprehensions, unpacking, and &lt;code&gt;*args&lt;/code&gt; all start to work the same way in your mind.&lt;/p&gt;

&lt;p&gt;I'm more visual, so here's a sequence diagram showing the interaction over time between two objects.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5jbmzqnfmjaqja488fm.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fy5jbmzqnfmjaqja488fm.png" alt="A for loop" width="800" height="743"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Built-in Containers
&lt;/h3&gt;

&lt;p&gt;This is the good part. Most of us learn Python's containers at the surface: lists are mutable and ordered, tuples are immutable and ordered, dicts are key-value pairs, sets are unique and unordered. That's correct but also useless most of the time. It tells you nothing about performance. Nothing about why your set of dicts blew up with a TypeError. Nothing about why your queue is suddenly O(n²). &lt;/p&gt;

&lt;p&gt;Here's what to focus on instead. Every container in Python is three things at once:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A data structure: how it's stored in memory and how fast operations are.&lt;/li&gt;
&lt;li&gt;A set of protocols: the special methods it supports.&lt;/li&gt;
&lt;li&gt;A semantic choice: the meaning you communicate to other developers by using it.&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  List: the dynamic array
&lt;/h4&gt;

&lt;p&gt;A python list is a dynamic array (called a vector or &lt;code&gt;ArrayList&lt;/code&gt; in other languages).&lt;/p&gt;

&lt;p&gt;Under the hood, it’s a block of pointers to python objects, with extra space preallocated at the end for appending new items.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌────┬────┬────┬────┬────┬────┬────┬────┐
│ p0 │ p1 │ p2 │ p3 │ ▒▒ │ ▒▒ │ ▒▒ │ ▒▒ │   length=4, capacity=8
└────┴────┴────┴────┴────┴────┴────┴────┘
  ↑    ↑    ↑    ↑
  obj  obj  obj  obj

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This one detail is what determines the entire complexity table.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Cost&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lst[i]&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lst.append(x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1) amortized&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lst.pop()&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lst.pop(0)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;lst.insert(0, x)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;x in lst&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;len(lst)&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Protocol:&lt;/strong&gt; &lt;code&gt;MutableSequence&lt;/code&gt; (which gives you &lt;code&gt;Sequence&lt;/code&gt; + &lt;code&gt;Iterable&lt;/code&gt; + &lt;code&gt;Container&lt;/code&gt; + &lt;code&gt;Sized&lt;/code&gt; + &lt;code&gt;Reversible&lt;/code&gt;).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Semantic intent:&lt;/strong&gt; An ordered, mutable collection of items that I expect to iterate over, append to, and access by index.&lt;/p&gt;

&lt;p&gt;A common mistake is to use a &lt;code&gt;list&lt;/code&gt; as a FIFO queue:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;pending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;task&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;task&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pop&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This is pretty expensive, the &lt;code&gt;pop(0)&lt;/code&gt; shifts every remaining element one position to the left, and remember this operation is &lt;code&gt;O(n)&lt;/code&gt;. If this runs inside a loop it can accidentally become &lt;code&gt;O(n²)&lt;/code&gt; .&lt;/p&gt;

&lt;h4&gt;
  
  
  Tuple: the record
&lt;/h4&gt;

&lt;p&gt;We think of tuples as immutable lists, but that idea can mislead. &lt;/p&gt;

&lt;p&gt;A tuple is better understood as a record: a fixed-size structure where each position can hold different types of data.&lt;/p&gt;

&lt;p&gt;In practice:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Tuples represent structured data.&lt;/li&gt;
&lt;li&gt;Lists represent collections of items.
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="c1"&gt;# List
&lt;/span&gt;&lt;span class="n"&gt;orders&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;order1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order2&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;order3&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...]&lt;/span&gt;

&lt;span class="c1"&gt;# Tuple
&lt;/span&gt;&lt;span class="n"&gt;db_row&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;alice&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;active&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;42&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Immutability is a result of this distinction, not the point. &lt;/p&gt;

&lt;p&gt;And because tuples are immutable, they gain a few important properties that lists don’t have:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Hashable&lt;/li&gt;
&lt;li&gt;Memory efficiency&lt;/li&gt;
&lt;li&gt;Read-only contract&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;From a protocol perspective, tuples behave like read-only sequences and can also be hashable.&lt;/p&gt;

&lt;p&gt;Semantically, a tuple means: “This is a single thing identified by its contents.”&lt;/p&gt;

&lt;p&gt;This matters a lot in a production application, tuples are commonly used for:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Compound dictionary keys&lt;/li&gt;
&lt;li&gt;Deduplication&lt;/li&gt;
&lt;li&gt;Cache and memoization keys&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Dict: the hash table
&lt;/h4&gt;

&lt;p&gt;The king. If lists are the most common container in Python, dictionaries are probably the most important. A big part of Python's internals is built on top of dictionaries. &lt;/p&gt;

&lt;p&gt;A dict is a hash table with open addressing. The keys you insert get hashed, and the hash points to a slot.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Operation&lt;/th&gt;
&lt;th&gt;Average&lt;/th&gt;
&lt;th&gt;Worst&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;d[k]&lt;/code&gt;, &lt;code&gt;d[k] = v&lt;/code&gt;, &lt;code&gt;del d[k]&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(n) (hash collisions)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;k in d&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;O(1)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Iteration&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;td&gt;O(n)&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;From a protocol perspective, a &lt;code&gt;dict&lt;/code&gt; implements &lt;code&gt;MutableMapping&lt;/code&gt;, not &lt;code&gt;Sequence&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;There are two things you must remember about dictionaries:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Keys must be hashable&lt;/li&gt;
&lt;li&gt;Insertion order is preserved&lt;/li&gt;
&lt;/ul&gt;

&lt;h4&gt;
  
  
  Set: the dict without values
&lt;/h4&gt;

&lt;p&gt;A &lt;code&gt;set&lt;/code&gt; is almost the same thing as a dictionary, except it only cares about keys.&lt;/p&gt;

&lt;p&gt;It uses the same hash-table implementation underneath, which means it has:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;O(1) average-time lookups, inserts, and removals&lt;/li&gt;
&lt;li&gt;elements must be hashable&lt;/li&gt;
&lt;li&gt;same "no duplicates" guarantee, duplicates are naturally eliminated
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;

&lt;span class="c1"&gt;# the antipattern
&lt;/span&gt;&lt;span class="n"&gt;seen&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="bp"&gt;...&lt;/span&gt;
&lt;span class="n"&gt;seen&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;So in practice it's just a collection of unique keys. From a protocol perspective, a &lt;code&gt;set&lt;/code&gt; implements &lt;code&gt;MutableSet&lt;/code&gt;, which gives you built-in set operations like: &lt;code&gt;|&lt;/code&gt;, &lt;code&gt;&amp;amp;&lt;/code&gt;, &lt;code&gt;-&lt;/code&gt;, &lt;code&gt;^&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;Semantically, a set means: “I only care about uniqueness and fast membership checks.”&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;These are some of the most commonly used containers in Python in our applications, and it's important to understand the trade-offs when deciding which one to use. When you reach for a container, try to ask these:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;What's the access pattern?&lt;/li&gt;
&lt;li&gt;Will it grow?&lt;/li&gt;
&lt;li&gt;Does identity-by-contents matter?&lt;/li&gt;
&lt;li&gt;What does the choice say to the reader?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These are the common bad choices we covered and what you should reach for instead:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;You wrote&lt;/th&gt;
&lt;th&gt;You probably wanted&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;list.pop(0)&lt;/code&gt; in a loop&lt;/td&gt;
&lt;td&gt;&lt;code&gt;collections.deque&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;if x in big_list&lt;/code&gt; repeatedly&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;set&lt;/code&gt; (convert once)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;f"{a}:{b}"&lt;/code&gt; as a dict key&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;(a, b)&lt;/code&gt; tuple key&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;List of &lt;code&gt;(key, value)&lt;/code&gt; pairs&lt;/td&gt;
&lt;td&gt;&lt;code&gt;dict&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;dict[str, None]&lt;/code&gt; for membership&lt;/td&gt;
&lt;td&gt;&lt;code&gt;set&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tuple with named indexes everywhere&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;namedtuple&lt;/code&gt; or &lt;code&gt;dataclass&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;If you remember one thing: the container you pick is a contract, not a convenience.&lt;/p&gt;

</description>
      <category>python</category>
      <category>datastructures</category>
      <category>programming</category>
    </item>
  </channel>
</rss>
