Python numbers every programmer should know

2026/01/01 23:39

Python numbers every programmer should know

RSS: https://news.ycombinator.com/rss

要約

本文

There are numbers every Python programmer should know. For example, how fast or slow is it to add an item to a list in Python? What about opening a file? Is that less than a millisecond? Is there something that makes that slower than you might have guessed? If you have a performance sensitive algorithm, which data structure should you use? How much memory does a floating point number use? What about a single character or the empty string? How fast is FastAPI compared to Django? I wanted to take a moment and write down performance numbers specifically focused on Python developers. Below you will find an extensive table of such values. They are grouped by category. And I provided a couple of graphs for the more significant analysis below the table. Source code for the benchmarks This article is posted without any code. I encourage you to dig into the benchmarks. The code is available on GitHub at: https://github.com/mikeckennedy/python-numbers-everyone-should-know 📊 System Information The benchmarks were run on the sytem described in this table. While yours may be faster or slower, the most important thing to consider is relative comparisons.

      Property
      Value
  


  
      Python Version
      CPython 3.14.2
  
  
      Hardware
      Mac Mini M4 Pro
  
  
      Platform
      macOS Tahoe (26.2)
  
  
      Processor
      ARM
  
  
      CPU Cores
      14 physical / 14 logical
  
  
      RAM
      24 GB
  
  
      Timestamp
      2025-12-30
  

Acknowledgments Inspired by Latency Numbers Every Programmer Should Know and similar resources.

Python numbers you should know More analysis and graphs by category below the table.

      Category
      Operation
      Time
      Memory
  


  
      💾 Memory
      Empty Python process
      —
      15.73 MB
  
  
      
      Empty string
      —
      41 bytes
  
  
      
      100-char string
      —
      141 bytes
  
  
      
      Small int (0-256)
      —
      28 bytes
  
  
      
      Large int
      —
      28 bytes
  
  
      
      Float
      —
      24 bytes
  
  
      
      Empty list
      —
      56 bytes
  
  
      
      List with 1,000 ints
      —
      35.2 KB
  
  
      
      List with 1,000 floats
      —
      32.1 KB
  
  
      
      Empty dict
      —
      64 bytes
  
  
      
      Dict with 1,000 items
      —
      63.4 KB
  
  
      
      Empty set
      —
      216 bytes
  
  
      
      Set with 1,000 items
      —
      59.6 KB
  
  
      
      Regular class instance (5 attrs)
      —
      694 bytes
  
  
      
      __slots__ class instance (5 attrs)
      —
      212 bytes
  
  
      
      List of 1,000 regular class instances
      —
      165.2 KB
  
  
      
      List of 1,000 __slots__ class instances
      —
      79.1 KB
  
  
      
      dataclass instance
      —
      694 bytes
  
  
      
      namedtuple instance
      —
      228 bytes
  
  
      ⚙️ Basic Ops
      Add two integers
      19.0 ns (52.7M ops/sec)
      —
  
  
      
      Add two floats
      18.4 ns (54.4M ops/sec)
      —
  
  
      
      String concatenation (small)
      39.1 ns (25.6M ops/sec)
      —
  
  
      
      f-string formatting
      64.9 ns (15.4M ops/sec)
      —
  
  
      
      .format()
      103 ns (9.7M ops/sec)
      —
  
  
      
      % formatting
      89.8 ns (11.1M ops/sec)
      —
  
  
      
      List append
      28.7 ns (34.8M ops/sec)
      —
  
  
      
      List comprehension (1,000 items)
      9.45 μs (105.8k ops/sec)
      —
  
  
      
      Equivalent for-loop (1,000 items)
      11.9 μs (83.9k ops/sec)
      —
  
  
      📦 Collections
      Dict lookup by key
      21.9 ns (45.7M ops/sec)
      —
  
  
      
      Set membership check
      19.0 ns (52.7M ops/sec)
      —
  
  
      
      List index access
      17.6 ns (56.8M ops/sec)
      —
  
  
      
      List membership check (1,000 items)
      3.85 μs (259.6k ops/sec)
      —
  
  
      
      len() on list
      18.8 ns (53.3M ops/sec)
      —
  
  
      
      Iterate 1,000-item list
      7.87 μs (127.0k ops/sec)
      —
  
  
      
      Iterate 1,000-item dict
      8.74 μs (114.5k ops/sec)
      —
  
  
      
      sum() of 1,000 ints
      1.87 μs (534.8k ops/sec)
      —
  
  
      🏷️ Attributes
      Read from regular class
      14.1 ns (70.9M ops/sec)
      —
  
  
      
      Write to regular class
      15.7 ns (63.6M ops/sec)
      —
  
  
      
      Read from __slots__ class
      14.1 ns (70.7M ops/sec)
      —
  
  
      
      Write to __slots__ class
      16.4 ns (60.8M ops/sec)
      —
  
  
      
      Read from @property
      19.0 ns (52.8M ops/sec)
      —
  
  
      
      getattr()
      13.8 ns (72.7M ops/sec)
      —
  
  
      
      hasattr()
      23.8 ns (41.9M ops/sec)
      —
  
  
      📄 JSON
      json.dumps() (simple)
      708 ns (1.4M ops/sec)
      —
  
  
      
      json.loads() (simple)
      714 ns (1.4M ops/sec)
      —
  
  
      
      json.dumps() (complex)
      2.65 μs (376.8k ops/sec)
      —
  
  
      
      json.loads() (complex)
      2.22 μs (449.9k ops/sec)
      —
  
  
      
      orjson.dumps() (complex)
      310 ns (3.2M ops/sec)
      —
  
  
      
      orjson.loads() (complex)
      839 ns (1.2M ops/sec)
      —
  
  
      
      ujson.dumps() (complex)
      1.64 μs (611.2k ops/sec)
      —
  
  
      
      msgspec encode (complex)
      445 ns (2.2M ops/sec)
      —
  
  
      
      Pydantic model_dump_json()
      1.54 μs (647.8k ops/sec)
      —
  
  
      
      Pydantic model_validate_json()
      2.99 μs (334.7k ops/sec)
      —
  
  
      🌐 Web Frameworks
      Flask (return JSON)
      16.5 μs (60.7k req/sec)
      —
  
  
      
      Django (return JSON)
      18.1 μs (55.4k req/sec)
      —
  
  
      
      FastAPI (return JSON)
      8.63 μs (115.9k req/sec)
      —
  
  
      
      Starlette (return JSON)
      8.01 μs (124.8k req/sec)
      —
  
  
      
      Litestar (return JSON)
      8.19 μs (122.1k req/sec)
      —
  
  
      📁 File I/O
      Open and close file
      9.05 μs (110.5k ops/sec)
      —
  
  
      
      Read 1KB file
      10.0 μs (99.5k ops/sec)
      —
  
  
      
      Write 1KB file
      35.1 μs (28.5k ops/sec)
      —
  
  
      
      Write 1MB file
      207 μs (4.8k ops/sec)
      —
  
  
      
      pickle.dumps()
      1.30 μs (769.6k ops/sec)
      —
  
  
      
      pickle.loads()
      1.44 μs (695.2k ops/sec)
      —
  
  
      🗄️ Database
      SQLite insert (JSON blob)
      192 μs (5.2k ops/sec)
      —
  
  
      
      SQLite select by PK
      3.57 μs (280.3k ops/sec)
      —
  
  
      
      SQLite update one field
      5.22 μs (191.7k ops/sec)
      —
  
  
      
      diskcache set
      23.9 μs (41.8k ops/sec)
      —
  
  
      
      diskcache get
      4.25 μs (235.5k ops/sec)
      —
  
  
      
      MongoDB insert_one
      119 μs (8.4k ops/sec)
      —
  
  
      
      MongoDB find_one by _id
      121 μs (8.2k ops/sec)
      —
  
  
      
      MongoDB find_one by nested field
      124 μs (8.1k ops/sec)
      —
  
  
      📞 Functions
      Empty function call
      22.4 ns (44.6M ops/sec)
      —
  
  
      
      Function with 5 args
      24.0 ns (41.7M ops/sec)
      —
  
  
      
      Method call
      23.3 ns (42.9M ops/sec)
      —
  
  
      
      Lambda call
      19.7 ns (50.9M ops/sec)
      —
  
  
      
      try/except (no exception)
      21.5 ns (46.5M ops/sec)
      —
  
  
      
      try/except (exception raised)
      139 ns (7.2M ops/sec)
      —
  
  
      
      isinstance() check
      18.3 ns (54.7M ops/sec)
      —
  
  
      ⏱️ Async
      Create coroutine object
      47.0 ns (21.3M ops/sec)
      —
  
  
      
      run_until_complete(empty)
      27.6 μs (36.2k ops/sec)
      —
  
  
      
      asyncio.sleep(0)
      39.4 μs (25.4k ops/sec)
      —
  
  
      
      gather() 10 coroutines
      55.0 μs (18.2k ops/sec)
      —
  
  
      
      create_task() + await
      52.8 μs (18.9k ops/sec)
      —
  
  
      
      async with (context manager)
      29.5 μs (33.9k ops/sec)
      —
  

Memory Costs Understanding how much memory different Python objects consume. An empty Python process uses 15.73 MB

Strings The rule of thumb for strings is the core string object takes 41 bytes. Each additional character is 1 byte.

      String
      Size
  


  
      Empty string ""
      41 bytes
  
  
      1-char string "a"
      42 bytes
  
  
      100-char string
      141 bytes
  

Numbers Numbers are surprisingly large in Python. They have to derive from CPython’s PyObject and are subject to reference counting for garabage collection, they exceed our typical mental model many of:

2 bytes = short int 4 bytes = long int etc.

      Type
      Size
  


  
      Small int (0-256, cached)
      28 bytes
  
  
      Large int (1000)
      28 bytes
  
  
      Very large int (10**100)
      72 bytes
  
  
      Float
      24 bytes
  

Collections Collections are amazing in Python. Dynamically growing lists. Ultra high-perf dictionaries and sets. Here is the empty and “full” overhead of each.

      Collection
      Empty
      1,000 items
  


  
      List (ints)
      56 bytes
      35.2 KB
  
  
      List (floats)
      56 bytes
      32.1 KB
  
  
      Dict
      64 bytes
      63.4 KB
  
  
      Set
      216 bytes
      59.6 KB
  

Classes and Instances Slots are an interesting addition to Python classes. They remove the entire concept of a dict for tracking fields and other values. Even for a single instance, slots classes are significantly smaller (212 bytes vs 694 bytes for 5 attributes). If you are holding a large number of them in memory for a list or cache, the memory savings of a slots class becomes very dramatic - over 2x less memory usage. Luckily for most use-cases, just adding a slots entry saves a ton of memory with minimal effort.

      Type
      Empty
      5 attributes
  


  
      Regular class
      344 bytes
      694 bytes
  
  
      __slots__ class
      32 bytes
      212 bytes
  
  
      dataclass
      —
      694 bytes
  
  
      @dataclass(slots=True)
      —
      212 bytes
  
  
      namedtuple
      —
      228 bytes
  

Aggregate Memory Usage (1,000 instances):

      Type
      Total Memory
  


  
      List of 1,000 regular class instances
      165.2 KB
  
  
      List of 1,000 __slots__ class instances
      79.1 KB
  

Basic Operations The cost of fundamental Python operations: Way slower than C/C++/C# but still quite fast. I added a brief comparison to C# to the source repo. Arithmetic

      Operation
      Time
  


  
      Add two integers
      19.0 ns (52.7M ops/sec)
  
  
      Add two floats
      18.4 ns (54.4M ops/sec)
  
  
      Multiply two integers
      19.4 ns (51.6M ops/sec)
  

String Operations String operations in Python are fast as well. f-strings are the fastest formatting style, while even the slowest style is still measured in just nano-seconds.

      Operation
      Time
  


  
      Concatenation (+)
      39.1 ns (25.6M ops/sec)
  
  
      f-string
      64.9 ns (15.4M ops/sec)
  
  
      .format()
      103 ns (9.7M ops/sec)
  
  
      % formatting
      89.8 ns (11.1M ops/sec)
  

List Operations List operations are very fast in Python. Adding a single item usually requires 28ns. Said another way, you can do 35M appends per second. This is unless the list has to expand using something like a doubling algorithm. You can see this in the ops/sec for 1,000 items. Surprisingly, list comprehensions are 26% faster than the equivalent for loops with append statements.

      Operation
      Time
  


  
      list.append()
      28.7 ns (34.8M ops/sec)
  
  
      List comprehension (1,000 items)
      9.45 μs (105.8k ops/sec)
  
  
      Equivalent for-loop (1,000 items)
      11.9 μs (83.9k ops/sec)
  

Collection Access and Iteration How fast can you get data out of Python’s built-in collections? Here is a dramatic example of how much faster the correct data structure is. item in set or item in dict is 200x faster than item in list for just 1,000 items! The graph below is non-linear in the x-axis. Access by Key/Index

      Operation
      Time
  


  
      Dict lookup by key
      21.9 ns (45.7M ops/sec)
  
  
      Set membership (in)
      19.0 ns (52.7M ops/sec)
  
  
      List index access
      17.6 ns (56.8M ops/sec)
  
  
      List membership (in, 1,000 items)
      3.85 μs (259.6k ops/sec)
  

Length len() is very fast. Maybe we don’t have to optimize it out of the test condition on a while loop looping 100 times after all.

      Collection
      len() time
  


  
      List (1,000 items)
      18.8 ns (53.3M ops/sec)
  
  
      Dict (1,000 items)
      17.6 ns (56.9M ops/sec)
  
  
      Set (1,000 items)
      18.0 ns (55.5M ops/sec)
  

Iteration

      Operation
      Time
  


  
      Iterate 1,000-item list
      7.87 μs (127.0k ops/sec)
  
  
      Iterate 1,000-item dict (keys)
      8.74 μs (114.5k ops/sec)
  
  
      sum() of 1,000 integers
      1.87 μs (534.8k ops/sec)
  

Class and Object Attributes The cost of reading and writing attributes, and how slots changes things. Slots saves over 2x the memory usage on large collections, with virtually identical attribute access speed. Attribute Access

      Operation
      Regular Class
      __slots__ Class
  


  
      Read attribute
      14.1 ns (70.9M ops/sec)
      14.1 ns (70.7M ops/sec)
  
  
      Write attribute
      15.7 ns (63.6M ops/sec)
      16.4 ns (60.8M ops/sec)
  

Other Attribute Operations

      Operation
      Time
  


  
      Read @property
      19.0 ns (52.8M ops/sec)
  
  
      getattr(obj, 'attr')
      13.8 ns (72.7M ops/sec)
  
  
      hasattr(obj, 'attr')
      23.8 ns (41.9M ops/sec)
  

JSON and Serialization Comparing standard library JSON with optimized alternatives. orjson handles more data types and is over 8x faster than standard lib json for complex objects. Impressive! Serialization (dumps)

      Library
      Simple Object
      Complex Object
  


  
      json (stdlib)
      708 ns (1.4M ops/sec)
      2.65 μs (376.8k ops/sec)
  
  
      orjson
      60.9 ns (16.4M ops/sec)
      310 ns (3.2M ops/sec)
  
  
      ujson
      264 ns (3.8M ops/sec)
      1.64 μs (611.2k ops/sec)
  
  
      msgspec
      92.3 ns (10.8M ops/sec)
      445 ns (2.2M ops/sec)
  

Deserialization (loads)

      Library
      Simple Object
      Complex Object
  


  
      json (stdlib)
      714 ns (1.4M ops/sec)
      2.22 μs (449.9k ops/sec)
  
  
      orjson
      106 ns (9.4M ops/sec)
      839 ns (1.2M ops/sec)
  
  
      ujson
      268 ns (3.7M ops/sec)
      1.46 μs (682.8k ops/sec)
  
  
      msgspec
      101 ns (9.9M ops/sec)
      850 ns (1.2M ops/sec)
  

Pydantic

      Operation
      Time
  


  
      model_dump_json()
      1.54 μs (647.8k ops/sec)
  
  
      model_validate_json()
      2.99 μs (334.7k ops/sec)
  
  
      model_dump() (to dict)
      1.71 μs (585.2k ops/sec)
  
  
      model_validate() (from dict)
      2.30 μs (435.5k ops/sec)
  

Web Frameworks Returning a simple JSON response. Benchmarked with wrk against localhost running 4 works in Granian. Each framework returns the same JSON payload from a minimal endpoint. No database access or that sort of thing. This is just how much overhead/perf do we get from each framework itself. The code we write that runs within those view methods is largely the same. Results

      Framework
      Requests/sec
      Latency (p99)
  


  
      Flask
      16.5 μs (60.7k req/sec)
      20.85 ms (48.0 ops/sec)
  
  
      Django
      18.1 μs (55.4k req/sec)
      170.3 ms (5.9 ops/sec)
  
  
      FastAPI
      8.63 μs (115.9k req/sec)
      1.530 ms (653.6 ops/sec)
  
  
      Starlette
      8.01 μs (124.8k req/sec)
      930 μs (1.1k ops/sec)
  
  
      Litestar
      8.19 μs (122.1k req/sec)
      1.010 ms (990.1 ops/sec)
  

File I/O Reading and writing files of various sizes. Note that the graph is non-linear in y-axis. Basic Operations

      Operation
      Time
  


  
      Open and close (no read)
      9.05 μs (110.5k ops/sec)
  
  
      Read 1KB file
      10.0 μs (99.5k ops/sec)
  
  
      Read 1MB file
      33.6 μs (29.8k ops/sec)
  
  
      Write 1KB file
      35.1 μs (28.5k ops/sec)
  
  
      Write 1MB file
      207 μs (4.8k ops/sec)
  

Pickle vs JSON to Disk

      Operation
      Time
  


  
      pickle.dumps() (complex obj)
      1.30 μs (769.6k ops/sec)
  
  
      pickle.loads() (complex obj)
      1.44 μs (695.2k ops/sec)
  
  
      json.dumps() (complex obj)
      2.72 μs (367.1k ops/sec)
  
  
      json.loads() (complex obj)
      2.35 μs (425.9k ops/sec)
  

Database and Persistence Comparing SQLite, diskcache, and MongoDB using the same complex object. Test Object user_data = { "id": 12345, "username": "alice_dev", "email": "alice@example.com", "profile": { "bio": "Software engineer who loves Python", "location": "Portland, OR", "website": "https://alice.dev", "joined": "2020-03-15T08:30:00Z" }, "posts": [ {"id": 1, "title": "First Post", "tags": ["python", "tutorial"], "views": 1520}, {"id": 2, "title": "Second Post", "tags": ["rust", "wasm"], "views": 843}, {"id": 3, "title": "Third Post", "tags": ["python", "async"], "views": 2341}, ], "settings": { "theme": "dark", "notifications": True, "email_frequency": "weekly" } } SQLite (JSON blob approach)

      Operation
      Time
  


  
      Insert one object
      192 μs (5.2k ops/sec)
  
  
      Select by primary key
      3.57 μs (280.3k ops/sec)
  
  
      Update one field
      5.22 μs (191.7k ops/sec)
  
  
      Delete
      191 μs (5.2k ops/sec)
  
  
      Select with json_extract()
      4.27 μs (234.2k ops/sec)
  

diskcache

      Operation
      Time
  


  
      cache.set(key, obj)
      23.9 μs (41.8k ops/sec)
  
  
      cache.get(key)
      4.25 μs (235.5k ops/sec)
  
  
      cache.delete(key)
      51.9 μs (19.3k ops/sec)
  
  
      Check key exists
      1.91 μs (523.2k ops/sec)
  

MongoDB

      Operation
      Time
  


  
      insert_one()
      119 μs (8.4k ops/sec)
  
  
      find_one() by _id
      121 μs (8.2k ops/sec)
  
  
      find_one() by nested field
      124 μs (8.1k ops/sec)
  
  
      update_one()
      115 μs (8.7k ops/sec)
  
  
      delete_one()
      30.4 ns (32.9M ops/sec)
  

Comparison Table

      Operation
      SQLite
      diskcache
      MongoDB
  


  
      Write one object
      192 μs (5.2k ops/sec)
      23.9 μs (41.8k ops/sec)
      119 μs (8.4k ops/sec)
  
  
      Read by key/id
      3.57 μs (280.3k ops/sec)
      4.25 μs (235.5k ops/sec)
      121 μs (8.2k ops/sec)
  
  
      Read by nested field
      4.27 μs (234.2k ops/sec)
      N/A
      124 μs (8.1k ops/sec)
  
  
      Update one field
      5.22 μs (191.7k ops/sec)
      23.9 μs (41.8k ops/sec)
      115 μs (8.7k ops/sec)
  
  
      Delete
      191 μs (5.2k ops/sec)
      51.9 μs (19.3k ops/sec)
      30.4 ns (32.9M ops/sec)
  

Note: MongoDB is a victim of network access version in-process access.

Function and Call Overhead The hidden cost of function calls, exceptions, and async. Function Calls

      Operation
      Time
  


  
      Empty function call
      22.4 ns (44.6M ops/sec)
  
  
      Function with 5 arguments
      24.0 ns (41.7M ops/sec)
  
  
      Method call on object
      23.3 ns (42.9M ops/sec)
  
  
      Lambda call
      19.7 ns (50.9M ops/sec)
  
  
      Built-in function (len())
      17.1 ns (58.4M ops/sec)
  

Exceptions

      Operation
      Time
  


  
      try/except (no exception raised)
      21.5 ns (46.5M ops/sec)
  
  
      try/except (exception raised)
      139 ns (7.2M ops/sec)
  

Type Checking

      Operation
      Time
  


  
      isinstance()
      18.3 ns (54.7M ops/sec)
  
  
      type() == type
      21.8 ns (46.0M ops/sec)
  

Async Overhead The cost of async machinery. Coroutine Creation

      Operation
      Time
  


  
      Create coroutine object (no await)
      47.0 ns (21.3M ops/sec)
  
  
      Create coroutine (with return value)
      45.3 ns (22.1M ops/sec)
  

Running Coroutines

      Operation
      Time
  


  
      run_until_complete(empty)
      27.6 μs (36.2k ops/sec)
  
  
      run_until_complete(return value)
      26.6 μs (37.5k ops/sec)
  
  
      Run nested await
      28.9 μs (34.6k ops/sec)
  
  
      Run 3 sequential awaits
      27.9 μs (35.8k ops/sec)
  

asyncio.sleep()

      Operation
      Time
  


  
      asyncio.sleep(0)
      39.4 μs (25.4k ops/sec)
  
  
      Coroutine with sleep(0)
      41.8 μs (23.9k ops/sec)
  

asyncio.gather()

      Operation
      Time
  


  
      gather() 5 coroutines
      49.7 μs (20.1k ops/sec)
  
  
      gather() 10 coroutines
      55.0 μs (18.2k ops/sec)
  
  
      gather() 100 coroutines
      155 μs (6.5k ops/sec)
  

Task Creation

      Operation
      Time
  


  
      create_task() + await
      52.8 μs (18.9k ops/sec)
  
  
      Create 10 tasks + gather
      85.5 μs (11.7k ops/sec)
  

Async Context Managers & Iteration

      Operation
      Time
  


  
      async with (context manager)
      29.5 μs (33.9k ops/sec)
  
  
      async for (5 items)
      30.0 μs (33.3k ops/sec)
  
  
      async for (100 items)
      36.4 μs (27.5k ops/sec)
  

Sync vs Async Comparison

      Operation
      Time
  


  
      Sync function call
      20.3 ns (49.2M ops/sec)
  
  
      Async equivalent (run_until_complete)
      28.2 μs (35.5k ops/sec)
  

Methodology Benchmarking Approach

All benchmarks run multiple times and with warmup not timed Timing uses timeit or perf_counter_ns as appropriate Memory measured with sys.getsizeof() and tracemalloc Results are median of N runs

Environment

OS: macOS 26.2 Python: 3.14.2 (CPython) CPU: ARM - 14 cores (14 logical) RAM: 24.0 GB

Code Repository All benchmark code available at: https://github.com/mkennedy/python-numbers-everyone-should-know

Key Takeaways

Memory overhead: Python objects have significant memory overhead - even an empty list is 56 bytes Dict/set speed: Dictionary and set lookups are extremely fast (O(1) average case) compared to list membership checks (O(n)) JSON performance: Alternative JSON libraries like orjson and msgspec are 3-8x faster than stdlib json Async overhead: Creating and awaiting coroutines has measurable overhead - only use async when you need concurrency slots tradeoff: slots saves significant memory (over 2x for collections) with virtually no performance impact

Last updated: 2026-01-01

同じ日のほかのニュース

一覧に戻る →