@SquareOne's answer shows the natural, idiomatic way to express the query.  It exploits the fact that an association can be applied to a key to extract that key's value.  A similar work-around would use part notation:

    d[Select[#[[colname]] > 3 &]]

This response will show how to achieve the desired result using only `Slot` notation.  The motivation is purely academic -- in normal code I would use @SquareOne's answer.

The argument to `Slot` is not evaluated when it appears within a `Function`.  This is true even when using simple numeric slot references in earlier versions of _Mathematica_ before the introduction of named slots:

    n = 1;
    Slot[n] &
    (* Function::slot: Slot[n] (in Slot[n]&) should contain a non-negative integer. >> *)

When such evaluation is desired it must be done externally and then injected into the held expression.  For example:

    With[{c = colname}, Slot[c] > 3 &]
    (* #a > 3 & *)

or

    colname /. c_ :> (Slot[c] > 3 &)
    (* #a > 3 & *)

In the original context, we could get the desired result by writing:

    d[Select[With[{c = colname}, Slot[c] > 3 &]]]

This is notationally inferior to @SquareOne's solution, but might be useful in more complex scenarios that involve, for example, code generation.

Beware that this injection technique will only work if the `Slot` expression is written in full form.  If we try to perform this replacement using the short form (`#c`), the replacement will fail:

    With[{c = colname}, #c > 3 &]
    (* #c > 3 & *)

Note carefully that we ended up with `#c` in the result, not the desired `#a`.  The reason for this is subtle and is revealed by inspecting the full form of the slot reference:

    #c // FullForm
    (* Slot["c"] *)

Pay close attention to the fact that the full form involves the string `"c"` and not the symbol `c`.  Therefore, the `With` statement does nothing to the function expression `#c > 3 &` because all it sees is `Slot["c"] > 3 &` with no mention of the symbol `c`.