The proper way to deal with what you are attempting to do is to use capturing groups. This will let you refer back to your match. First, let me begin by explaining why your attempt was giving you the output you saw.
Why you saw what you saw
In the re.sub function, when you give it ' '+str(pattern)+' ' as the third parameter, this gets evaluated to the string " <_sre.SRE_Pattern object at some_memory_location> ", because str(pattern) returns the string representation of the pattern object, not of the pattern.
As an aside, on Python 3.4 and 3.5, str(pattern) returns re.compile('[%&\\(\\)\\+,-./:;=–‘’“”″]') for me, what version of Python are you using? Is it perhaps a version of Python 2?
Solution
As I alluded to before, your solution requires utilizing capturing groups. To denote a group, you simply use parentheses. In your case, the solution is simple enough because you only need one group:
>>> import re
>>> pattern = re.compile(r"([%&\(\)\+,-./:;=–‘’“”″]+)")
Notice for my string literal, I used an r before the start of the string. This denotes a raw string, which causes the string to ignore any escape sequence as defined by Python. An escape sequence is something like '\t', for example, which denotes a tab. However, if you use r'\t' then it is the actual string \t.
>>> text = "hi. hi.. hi; hi;; 55% good& good&&"
>>> pattern.sub(r' \1 ', text)
'hi . hi .. hi ; hi ;; 55 % good & good && '
Notice I simply used the sub method of the pattern object rather than the module-level function re.sub. It's not a big deal, but it just seems cleaner to me. Also, for the replacement argument, I used r' \1 '. This \1 refers to the first group captured by your pattern. If you had more than one group you could use something like \2 \1 if you wanted to reverse some pattern, for example. This again, is an escape sequence!
A potential improvement
It was unclear in your specification how you wanted to deal with more than 2 character e.g. three characters. So your pattern would deal with that situation thusly:
>>> text2 = "hi. hi.. hi; hi;; 55% good& good&& hi &&& hello,"
>>> pattern.sub(r' \1 ', text2)
'hi . hi .. hi ; hi ;; 55 % good & good && hi &&& hello , '
Perhaps that is what you what, but maybe you want to consider '&&&' as two distinct matches: '&&' and '&'. You can deal with that situation using quantifiers:
>>> pattern2 = re.compile(r'([%&\(\)\+,-./:;=–‘’“”″]{1,2})')
>>> pattern2.sub(r' \1 ', text2)
'hi . hi .. hi ; hi ;; 55 % good & good && hi && & hello , '
Instead of using the + sign which denotes one-or-more, you can use the bracket notation to have more fine-grained control. For example, {1,3} will match 1 to 3. {3} will match exactly 3. {3,} will match 3 or more.