What it seems you are really trying to do here is find a way to override the builtin print function in a "pythonic" way.
While there is a way to do this, I do have a word of caution. One of the rules of "pythonic code" is
Explicit is better than implicit.
Overwriting print is inherently an implicit solution, and it would be more "pythonic" to allow for a custom print function to solve your needs.
However, let's assume we are talking about a use case where the best option available is to override print. For example, lets say you want to indent the output from the help() function.
You could override print directly, but you run the risk of causing unexpected changes you can't see.
For example:
def function_that_prints():
    log_file = open("log_file.txt", "a")
    print("This should be indented")
    print("internally logging something", file = log_file)
    log_file.close()
    
indent = Iprint()
indent.level = 3
function_that_prints() # now this internal log_file.txt has been corrupted
print = indent.old_print
This is bad, since presumably you just meant to change the output that is printed on screen, and not internal places where print may or may not be used.
Instead, you should just override the stdout, not print.
Python now includes a utility to do this called contextlib.redirect_stdout() documented here.
An implementation may look like this:
import io
import sys
import contextlib
class StreamIndenter(io.TextIOBase):
    # io.TextIOBase provides some base functions, such as writelines()
    def __init__(self, tab = 4, level = 1, newline = "\n", stream = sys.stdout):
        """Printer that adds an indent at the start of each line"""
        self.tab = tab
        self.level = level
        self.stream = stream
        self.newline = newline
        self.linestart = True
    def write(self, buf, *args, **kwargs):
        if self.closed:
            raise ValueError("write to closed file")
        if not buf:
            # Quietly ignore printing nothing
            # prevents an issue with print(end='')
            return 
        indent = " " * (self.tab * self.level)
        if self.linestart:
            # The previous line has ended. Indent this one
            self.stream.write(indent)
        # Memorize if this ends with a newline
        if buf.endswith(self.newline):
            self.linestart = True
            # Don't replace the last newline, as the indent would double
            buf = buf[:-len(self.newline)]
            self.stream.write(buf.replace(self.newline, self.newline + indent))
            self.stream.write(self.newline)
        else:
            # Does not end on a newline
            self.linestart = False
            self.stream.write(buf.replace(self.newline, self.newline + indent))
    # Pass some calls to internal stream
    @property
    def writable(self):
        return self.stream.writable
    @property
    def encoding(self):
        return self.stream.encoding
    @property
    def name(self):
        return self.stream.name
with contextlib.redirect_stdout(StreamIndenter()) as indent:
    indent.level = 2
    print("this should be indented")
print("this shouldn't be indented")
Overriding print this way both doesn't corrupt other uses of print and allows for proper handling of more complicated usages.
For example:
with contextlib.redirect_stdout(StreamIndenter()) as indent:
    indent.level = 2
    print("this should be indented")
    indent.level = 3
    print("more indented")
    indent.level = 2
    for c in "hello world\n": print(c, end='')
    print()
    print("\n", end='')
    print(end = '')
    
print("this shouldn't be indented")
Formats correctly as:
        this should be indented
            more indented
        hello world
        
        
this shouldn't be indented