There are four cases:
- input datetime.timehastzinfoset (eg OP mentions UTC)
- output as non-naive time
- output as naive time (tzinfonot set)
 
- input datetime.timehastzinfonot set
- output as non-naive time
- output as naive time (tzinfonot set)
 
The correct answer needs to make use of datetime.datetime.timetz() function because datetime.time cannot be built as a non-naive timestamp by calling localize() or astimezone() directly.
from datetime import datetime, time
import pytz
def timetz_to_tz(t, tz_out):
    return datetime.combine(datetime.today(), t).astimezone(tz_out).timetz()
def timetz_to_tz_naive(t, tz_out):
    return datetime.combine(datetime.today(), t).astimezone(tz_out).time()
def time_to_tz(t, tz_out):
    return tz_out.localize(datetime.combine(datetime.today(), t)).timetz()
def time_to_tz_naive(t, tz_in, tz_out):
    return tz_in.localize(datetime.combine(datetime.today(), t)).astimezone(tz_out).time()
Example based on OP requirement:
t = time(12, 56, 44, 398402)
time_to_tz(t, pytz.utc) # assigning tzinfo= directly would not work correctly with other timezones
datetime.time(12, 56, 44, 398402, tzinfo=<UTC>)
In case naive timestamp is wanted:
time_to_tz_naive(t, pytz.utc, pytz.timezone('Europe/Berlin'))
datetime.time(14, 56, 44, 398402)
The cases where the time() instance has already tzinfo set are easier because datetime.combine picks up the tzinfo from the passed parameter, so we just need to convert to tz_out.