First-class objects in Python
There are a few general improvements I would suggest for readability and clarity and apply throughout.


Use docstring to document your functions, modules and classes.
Since Python 3, you can now use type hints for your function and method signatures, and use static code analysis tools. This also makes the code easier to read for humans.




Applying these to your cube function, you would get something like this:

def cube(number: int) -&gt; int:
    """Return the cube of a number,
    i.e. the number to the power of 3."""
    return number ** 3


Note that I renamed the x argument to a more descriptive number argument. What the above means is that the function expects number to be an integer, and that the function should return an integer. 

Another problem I see with this function is that it does not check for correct input. If you type cube("hello") then the program just crashes with an error TypeError: unsupported operand type(s) for ** or pow(): 'str' and 'int'. It would be better to handle the error. There are at least two ways to do this.

Using isinstance:

def cube(number: int) -&gt; int:
    if not isinstance(number, int):
        return TypeError("{0} is not an integer".format(number))
    return number ** 3


Using try and except:

def cube(number: int) -&gt; int:
    try:
        return number ** 3
    except TypeError as err:
        return TypeErrorTypeError("{0} is not an integer".format(number))


Using the latter, you could also just except TypeError as err and return err which will return the normal error listed above. 

You can also use assert which will instead raise an AssertionError if the type is incorrect, for instance assert(isinstance(number, int)). This is particularly useful when setting up tests for your code. 

I personally find that the first method is simpler to use, and you can use it to check multiple types at once, but the latter is the standard way to handle something that may cause an error. Choose whichever is more appropriate for the situation. 



Applying the same ideas to map_it, this is slightly more complicated since the type hints have to be imported from typing library. Again, I renamed x to a more descriptive name elem.

from typing import Callable, Sequence, Iterable

def map_it(func: Callable, seq: Sequence) -&gt; Iterable:
    """Applies a given function to a sequence and yields the result."""
    for elem in seq:
        yield func(elem)


You could also test using instanceof here if you liked, which may or may not be necessary depending on your use cases. 



Your greet function can also have type errors, if you call it with a non-string type, e.g., greet(42). The simple fix for this (and I recommend to use it any time you need to put variables/arguments into strings) is to use .format() instead of +'ing string and values. 

from typing import Callable

def greet(name: str) -&gt; Callable:
    """Returns a callable greeting with given name."""
    def print_message() -&gt; None:
        """Callable to print a greeting to stdout."""
        print("Hello, {0}!".format(name))
    return print_message




One more thing, I noticed your code is peppered with # line comments, in general comments like that should only be used to clarify or explain why something is done, in most cases they are not needed as the code speaks for itself.