Use type() to see what you are getting:
>>> type(df.describe().mean())
<class 'pandas.core.series.Series'>
>>> type(df.describe().mean)
<class 'method'>
Something without parenthesis cannot be a method call, at least not from a normal viewpoint. Instead you are trying to access the attribute "mean" of the result of describe() if you run df.groupby(df.var1).describe().mean. This attribute might very well be a method itself (a method object).Last in that chain was describe(), so let's look at that: https://pandas.pydata.org/pandas-docs/stable/generated/panda...
It returns a "summary: Series/DataFrame of summary statistics", so calling .mean() on that, will call https://pandas.pydata.org/pandas-docs/stable/generated/panda...
Accessing .mean will instead return that method object itself.
By default pandas will return a string describing the data itself when printing a method:
>>> df.describe()
numeric n2
count 3.0 3.0
mean 2.0 3.0
std 1.0 1.0
min 1.0 2.0
25% 1.5 2.5
50% 2.0 3.0
75% 2.5 3.5
max 3.0 4.0
>>> df.describe().mean
<bound method DataFrame.mean of numeric n2
count 3.0 3.0
mean 2.0 3.0
std 1.0 1.0
min 1.0 2.0
25% 1.5 2.5
50% 2.0 3.0
75% 2.5 3.5
max 3.0 4.0>
>>> df.describe().mean()
numeric 2.00
n2 2.75
dtype: float64
I assume this is what threw you off? You are not using the "results" of .mean in further code but were just looking at them, right?