The einsum rule
The rule, in one sentence: an index that appears in the inputs but not after the arrow is summed over. An index that appears after the arrow is kept.
np.einsum('hwc,c->hw', photo, w)
c appears in the inputs and not after the arrow, so it is summed — that is the contraction. h and w appear after the arrow, so they are kept.
There is nothing else to memorise. Every einsum string in the workshop is this one rule applied to different letters.