The reason for this difference is that while python creates row vectors when calling np.random.rand, julia creates a column vector. Which explains your dimension mismatch.
I think it’s easier to remember that in Python, you have to start aligning dimensions from the right, and in Julia from the left. That’s because Python is row-major and therefore the right-most dimension is the one contiguous in memory, while in Julia it’s the left-most.
In this example, broadcasting only works if two numbers on top of each other are the same, or at least one of them is 1. The [1] are implicit singleton dimensions that will always match, if one of the arguments is shorter than another.
# python: align from the right
arr1: 12 10
arr2: [1] 10
# julia: align from the left, 12 and 10 are incompatible
arr1: 12 10
arr2: 10 [1]
# julia: with rand(10)', we have added a singleton dimension
# to arr2, so now it works
arr1: 12 10
arr2: 1 10
That works in higher dimensions, too, where it’s not just about rows and columns anymore.
I actually think it would be cool if the error messages for broadcasting gave such a visualization, but maybe it would be too crazy with many components.