What Is New in Python 3.3
Python 3.3 was released on September 29, 2012. The headline additions are the yield from syntax for delegating sub-generators (the foundation of what later became await), the Virtual Environments module venv, and a redesigned import system via importlib. Python 3.3 also introduced a more flexible string representation for Unicode strings, cutting memory usage significantly for ASCII-heavy workloads.
| Category | Change | PEP / Reference |
|---|---|---|
| New Syntax | yield from -- sub-generator delegation |
PEP 380 |
| New Syntax | raise X from Y -- explicit exception chaining |
PEP 3134 |
| New Modules | venv -- lightweight virtual environments |
PEP 405 |
| New Modules | ipaddress -- IPv4/IPv6 address manipulation |
PEP 3144 |
| New Modules | lzma -- LZMA/XZ compression |
-- |
| New Modules | faulthandler -- debug-mode crash dumps from Python |
PEP 423 |
| Interpreter | Flexible string representation (PEP 393) -- Latin-1, UCS-2, or UCS-4 per string | PEP 393 |
| Interpreter | Redesigned import system -- fully implemented in Python via importlib |
PEP 302, PEP 328 |
| Interpreter | Namespace packages -- packages without __init__.py |
PEP 420 |
| Security | Hash randomization enabled by default (PYTHONHASHSEED) |
-- |
| Performance | Unicode string memory footprint reduced by up to 3-4x for ASCII text | PEP 393 |
yield from -- Sub-Generator Delegation (PEP 380)
The yield from expression lets a generator delegate to a sub-generator transparently. Values from the inner generator flow out through the outer one, send() calls flow in, and the return value of the inner generator becomes the value of the yield from expression. This was the direct predecessor of await in Python 3.5.
def inner():
yield 1
yield 2
return "inner done"
def outer():
result = yield from inner() # delegates to inner()
print(f"inner returned: {result}")
yield 3
list(outer()) # [1, 2, 3]; prints "inner returned: inner done"
Before yield from, delegating to a sub-generator required a manual for item in sub_gen: yield item loop -- which lost the ability to pass send() values and return values back to the caller.
venv -- Built-in Virtual Environments (PEP 405)
Python 3.3 ships venv as a standard module, replacing the third-party virtualenv for most use cases. A virtual environment is a lightweight, isolated Python installation with its own site-packages directory.
# Create a virtual environment
python3 -m venv .venv
# Activate (Linux/macOS)
source .venv/bin/activate
# Activate (Windows)
.venv\Scripts\activate.bat
# Install packages into the environment
pip install requests
The key difference from virtualenv: venv does not copy the full Python binary -- it uses symlinks (or shims on Windows), making environments smaller and faster to create.
Flexible String Representation (PEP 393)
Before Python 3.3, every Unicode string used UCS-2 or UCS-4 format regardless of content, meaning a string of 100 ASCII characters occupied 200 or 400 bytes. PEP 393 changed this so CPython uses the narrowest representation that fits the actual characters: Latin-1 for strings with codepoints up to U+00FF, UCS-2 up to U+FFFF, and UCS-4 only when needed. For typical English text, this cuts string memory by 3-4x and speeds up ASCII string operations.
Other Notable Additions in Python 3.3
- Namespace packages (PEP 420): A directory without an
__init__.pyfile is now recognized as a "namespace package," allowing packages to be split across multiple directories or distribution packages. - ipaddress module (PEP 3144):
ipaddress.IPv4Address,IPv6Address,IPv4Network,IPv6Network-- clean objects for network programming without string parsing. - faulthandler: Dumps a Python traceback on segfault or SIGFPE, which was previously impossible from within Python. Enable with
python -X faulthandlerorfaulthandler.enable(). - Explicit exception chaining (PEP 3134):
raise NewError from original_errorsets the__cause__attribute, and Python prints both exceptions and their chain when a traceback is displayed. - lzma module: Python gains native LZMA/XZ compression -- the same format used for
.tar.xzarchives. Higher compression ratio than gzip or bzip2 at the cost of slower compression speed.
FAQ
What is the connection between yield from in 3.3 and await in 3.5?
await is semantically built on the same mechanism as yield from. Internally, the asyncio event loop uses generators and yield from delegation. The async def/await keywords in 3.5 are a cleaner syntax over the same machinery, with added restrictions (can only be used in async-designated functions) that make the intent unambiguous.
Is venv the same as virtualenv?
Similar, but not identical. venv is the stdlib version and creates lighter environments using symlinks. virtualenv (third-party) supports older Python versions, offers more configuration options, and creates environments faster via wheel caching. For modern Python (3.3+), venv is sufficient for most workflows. Use virtualenv when you need features like --copies or compatibility with older Pythons.
Do namespace packages require any changes to existing code?
No, existing packages with __init__.py continue to work unchanged. Namespace packages are an additive feature -- directories without __init__.py that would previously have been ignored are now treated as namespace packages. This only matters when you intentionally want to split a logical package across multiple filesystem locations or distributions.
Is hash randomization in Python 3.3 enabled by default?
Yes, starting with 3.3. The hash values of str, bytes, and datetime objects are randomized per-process by default (seeded from os.urandom()). This prevents hash collision denial-of-service attacks. Set PYTHONHASHSEED=0 to disable and get deterministic hashes, or a specific integer seed for reproducible results in testing.
Can raise X from None suppress the original exception context?
Yes. raise NewError("msg") from None sets __cause__ to None and __suppress_context__ to True, which suppresses the "During handling of the above exception, another exception occurred:" message. This is the right pattern when you're intentionally converting one exception type to another and don't want to expose the original.