This is a very good question and the accepted answer indicates the key (i.e. loc denotes alignment and bbox_to_anchor denotes position). I have also tried some codes and would like to stress the importance of bbox_transform property that may sometimes needs to be explicitly specified to achieve desired effects. Below I will show you my findings on fig.legend. ax.legend should be very similar as loc and bbox_to_anchor works the same way.
When using the default setting, we will have the following.
%matplotlib inline
import numpy as np
import matplotlib.pyplot as plt
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,4), sharex=True)
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)

This is basically satisfactory. But it could be easily found that the legend overlays with the x-axis ticklabels of ax2. This is the problem that will become even severe when figsize and/or dpi of the figure changes, see the following.
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2)

So you see there are big gaps between ax2 and the legend. That's not what we want. Like the questioner, we would like to manually control the location of the legend. First, I will use the 2-number style of bbox_to_anchor like the answer did.
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
axbox = ax2.get_position()
# to place center point of the legend specified by loc at the position specified by bbox_to_anchor.
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05])

Almost there! But it is totally wrong as the center of the legend is not at the center of what we really mean! The key to solving this is that we need to explicitly inform the bbox_transform as fig.transFigure. By default None, the Axes' transAxes transform will be used. This is understandable as most of the time we will use ax.legend().
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
axbox = ax2.get_position()
# to place center point of the legend specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='center', ncol=2,
bbox_to_anchor=[axbox.x0+0.5*axbox.width, axbox.y0-0.05], bbox_transform=fig.transFigure)

As an alternative, we can also use a 4-number style bbox_to_anchor for loc. This is essentially specify a real box for the legend and loc really denotes alignment! The default bbox_to_anchor should just be [0,0,1,1], meaning the entire figure box! The four numbers represent x0,y0,width,height, respectively. It is very similar to specifying a cax for a shared colorbar! Hence you can easily change the y0 just a little bit lower than axbox.y0 and adjust loc accordingly.
fig, (ax1, ax2) = plt.subplots(nrows=2, figsize=(6,12), sharex=True, facecolor='w', gridspec_kw={'hspace':0.01})
x = np.linspace(0, np.pi, 100)
line1, = ax1.plot(x, np.cos(3*x), color='red')
line2, = ax2.plot(x, np.sin(4*x), color='green')
axbox = ax2.get_position()
# to place center point specified by loc at the position specified by bbox_to_anchor!
fig.legend([line1, line2], ['yep', 'nope'], loc='lower center', ncol=2,
bbox_to_anchor=[0, axbox.y0-0.05,1,1], bbox_transform=fig.transFigure)
