Skip to main content
The ** operator in Python serves two distinct syntactic roles depending on its lexical context: as a binary arithmetic exponentiation operator for numeric types, and as a mapping unpacking or packing token for dictionary-like objects.

Arithmetic Exponentiation

When used as a binary operator between two expressions, ** performs exponentiation, raising the left operand to the power of the right operand. Underlying Mechanics: The operator delegates to the __pow__(self, other) magic method of the left operand. If the left operand does not support the operation or returns NotImplemented, Python falls back to the right operand’s __rpow__(self, other) method. Associativity and Precedence: Unlike most Python operators, ** is right-associative. The expression x ** y ** z is evaluated as x ** (y ** z). It also binds more tightly than unary arithmetic operators on its left, but less tightly than unary operators on its right.

Mapping Unpacking and Packing

When prefixed to an expression within a dictionary literal or a function call, ** acts as an unpacking operator. It is not a standalone operator that evaluates to a value; rather, it is a syntactic construct resolved by the Python parser to expand a mapping object into discrete key-value pairs. Underlying Mechanics: Because Python utilizes duck typing, the operand following ** does not need to formally inherit from or register with the collections.abc.Mapping abstract base class. It only needs to implement the mapping protocol—specifically the keys() and __getitem__() methods. Python internally iterates over the mapping’s keys and retrieves the corresponding values to populate the target structure. Contextual Syntax:
  • Dictionary Displays: Expands the key-value pairs into a new dictionary literal. If duplicate keys exist, the values from the rightmost unpacked mapping overwrite previous values.
  • Function Invocations: Expands the mapping into discrete keyword arguments. The keys must be strings. These string keys must match the function’s explicitly defined parameter names, unless the function signature includes a **kwargs catch-all parameter, in which case any valid string key is accepted. This unpacking is evaluated dynamically at runtime; the compiler emits a bytecode instruction (such as CALL_FUNCTION_EX) to handle the expansion during execution.
  • Function Signatures: When used in a function definition (def func(**kwargs):), it acts as a packing parameter, capturing all unbound keyword arguments into a single dictionary bound to the specified identifier.
Tired of Poor Python Skills? Fix That With Deep Grasping!Learn More