###Solution using `Block` The easiest option is probably to use `Block`, and wrap your result in `Hold` or `HoldForm`. So, a = 1; b = 2; c = 3; result = Block[{a, b, c}, Hold[Evaluate[Expand[(a + b + c)^3]]] ] (* Hold[a^3+3 a^2 b+3 a b^2+b^3+3 a^2 c+6 a b c+3 b^2 c+3 a c^2+3 b c^2+c^3] *) The values will be automatically substituted once you call `ReleaseHold`: ReleaseHold[result] (* 216 *) ###Use metaprogramming to automate things Here I will show how you can automate this step with some meta-programming. The following function returns all symbols used in building a given expression, and belonging to some pre-defined set of contexts (which I default to ``{"Global`"}``: ClearAll[getHeldSymbols]; SetAttributes[getHeldSymbols, HoldFirst]; getHeldSymbols[expr_, contexts_: {"Global`"}] := Thread[ Cases[ Unevaluated[expr], s_Symbol /; MemberQ[contexts, Context[s]] :> HoldComplete[s], {0, Infinity}, Heads -> True ], HoldComplete] For example: getHeldSymbols[Expand[(a + b + c)^3]] (* HoldComplete[{a, b, c}] *) Now, the following short code defines a dynamic environment where the values of defined symbols participating in building your expression have been "frozen": ClearAll[evalWithFrozenSyms] SetAttributes[evalWithFrozenSyms, HoldFirst]; evalWithFrozenSyms[expr_, wrapper_: Hold] := getHeldSymbols[expr] /. HoldComplete[{syms___}] :> Block[{syms}, wrapper[##] &@expr] Now we can use it: evalWithFrozenSyms[Expand[(a+b+c)^3]] (* Hold[a^3+3 a^2 b+3 a b^2+b^3+3 a^2 c+6 a b c+3 b^2 c+3 a c^2+3 b c^2+c^3] *)