Here, I make space a catcode 12 before I absorb the argument. Then I count them using a \numexpr. But, as matexmatics noted, the result is not expandable, unless one changes the catcode of space in advance of the expansion, as in
{\catcode`\ =12 \edef\z{\countspaces{ A B C }}\z}
which yields the correct answer of 4. Here is the full MWE:
\documentclass{article}
\let\endcsp\empty
\makeatletter
\catcode`\_=10 %
\catcode`\ =12_%
\def\countspaces{\catcode`\ =12_\csp}%
\def\csp#1{\the\numexpr0\cspaux#1\endcsp\relax\catcode`\ =10_}%
\def\cspaux#1{\ifx#1\endcsp\else\ifx#1 +1\fi\expandafter\cspaux\fi}%
\catcode`\ =10_%
\catcode`\_=12 %
\makeatother
\begin{document}
\countspaces{ A B } Should return 3 (1 is ok too if leading and trailing spaces are removed)
\countspaces{A \mycommand B} Should return 2 (mycommand is not expanded)
\countspaces{A {a b c} B} Should return 2 (spaces inside groups are not counted)
\end{document}
