The only difference between your two solutions is the first one returns a list and the second, a tuple. Neither one is a generator.
Edit: That was wrong. TIL if you write what looks like a tuple comprehension, it's actually a generator.
To answer the original question though, if you write it as a generator with the yield keyword
def square(nums: list):
for num in nums:
yield num * num
will consume less memory because it won't have the return value in memory (the list). You can verify this by putting a print statement after the yield - it won't be executed until you call next on the generator.