Collections
C# gives you a deliberate menu of collection types — Array, List<T>,
Dictionary<K,V> — each with a generic type parameter enforced at compile time.
Python gives you four built-in literals — list, tuple, dict,
set — that hold anything, with no type parameter at all. Fewer
types, less ceremony, less compile-time safety.
💡 The Bottom Line
List<T> maps to Python's list. Dictionary<K,V> maps to
dict. There's no separate fixed-size array type you'll reach for day-to-day, and no
generic type parameter to declare — a Python list can hold mixed types in the same list,
whether you meant to or not.
Lists
var names = new List<string> { "Ada", "Grace" };
names.Add("Linus");
Console.WriteLine(names[0]);
Console.WriteLine(names.Count);
names = ["Ada", "Grace"]
names.append("Linus")
print(names[0])
print(len(names))
📋 Key differences
.Add()→.append()..Count(a property) →len(names)(a built-in function, not a method).- No
<string>type parameter — a Python list can hold["Ada", 42, True]without complaint. - Negative indexing works out of the box:
names[-1]is the last element — no.Last()LINQ call needed.
⚠️ Gotcha
Python's slicing looks like C#'s array[i] indexing but goes much further:
names[1:3] returns a new list from index 1 up to (not including) index 3. It's
easy to assume slicing behaves like List<T>.GetRange(), but the "exclusive
end" convention and the ability to omit either side (names[:2], names[2:])
have no direct one-line C# equivalent — get comfortable with slicing early, it's everywhere
in idiomatic Python.
Dictionaries
var ages = new Dictionary<string, int>
{
{ "Ada", 36 },
{ "Grace", 85 }
};
Console.WriteLine(ages["Ada"]);
ages["Linus"] = 54;
ages = {
"Ada": 36,
"Grace": 85,
}
print(ages["Ada"])
ages["Linus"] = 54
The shape is nearly identical — curly braces, key-value pairs, square-bracket access. The
real difference is what happens on a missing key: C#'s indexer throws
KeyNotFoundException, same as Python's ages["missing"] throwing
KeyError. But Python also gives you ages.get("missing", 0) — a built-in
"give me a default instead of throwing" — which in C# means reaching for
TryGetValue instead.
Tuples and sets: no direct C# equivalent for one of them
Python's tuple — point = (3, 4) — is an immutable, ordered group, similar
in spirit to a C# ValueTuple ((3, 4) in modern C#) but used far more
casually in everyday Python code, especially for returning multiple values from a function
without declaring a class. Python's set — colors = {"red", "green"} — maps
directly onto C#'s HashSet<T>: unordered, unique elements, fast membership
checks with in.
🙋 There Are No Dumb Questions
Q: How do I get a fixed-size array like C#'s int[] arr = new int[5];?
A: Python's built-in list is always resizable — there's no separate fixed-size array
type in everyday use. If you specifically need a fixed-size, single-type numeric array for
performance (matching C#'s actual memory layout), that's what the third-party
array module or NumPy's ndarray are for — but for typical business logic,
a plain list is the idiomatic default, even where C# would reach for an array.
🔧 Try It Yourself
Take a C# method that builds a Dictionary<string, List<int>> (grouping numbers
by a string key) and rewrite it in Python using a plain dict whose values are
lists. Notice you never declare the value type anywhere — you just start
appending to whichever list is at that key.
✅ Quick Check
Which Python built-in most directly corresponds to C#'s Dictionary<K,V>?
Next up: Python has classes too — but no interfaces, no access modifiers enforced by the compiler, and a very different idea of what "private" means. That's Classes & OOP.