Breaking a 3GB gz file into chunks
Since the split files do not need to be readable text files, I would read &amp; write in chunks of bytes, not in lines. This should be faster than reading and writing line by line. We can go with a chunk size of 4,096 bytes because 4,096 or 8,192 is a typical block size on most file systems.
Although, I should note that when I tested your script on my system with a 9 GB text file which has 81 characters per line, it took about 73 seconds to split it into files of 10,000,000 lines each, or about 772 MB per file. The fact that it takes 12 minutes on your laptop likely confirms @Graipher's suspicion that your disk's write speed is the bottleneck here.
Using a refactored version of your script that reads &amp; writes in byte chunks (provided at the end of this review), when I ran it on the same file with --size 772 (split into files of size 772 MB) it took about 35 seconds. Still a decent improvement, but I'd be curious to know how it runs on your machine.
To implement this, I personally like having a helper method like the following which tells us the chunk sizes to read/write:
from typing import Iterator

def chunk_sizes(total_bytes: int, chunk_size: int) -&gt; Iterator[int]:
    for _ in range(total_bytes // chunk_size):
        yield chunk_size

    if remaining_bytes := total_bytes % chunk_size:
        yield remaining_bytes

For example, list(chunk_sizes(10000, 4096)) which returns [4096, 4096, 1808] would tell us that in order to read 10,000 bytes in 4,096 byte chunks, we should read 4,096 bytes, followed by another 4,096 bytes, followed by 1,808 bytes.
General review

There's no reason to have run() be a staticmethod, and it's weird how FileSplitter creates an instance of itself inside this method.
If you change the top-level invocation to FileSplitter().run() then run() could be simplified to
def run(self):
    self.split()

But if you do that you might as well remove run() and make FileSplitter().split() the top-level call. My recommendation is to move the contents of run() under the __name__ == &quot;__main__&quot; guard and then remove run() entirely as it's no longer needed.

Always open files with a with block to ensure the file is closed properly.

I recommend using argparse because you get a lot of stuff for free (input type validations, help/usage message with -h, etc.).

I would move the command-line argument parsing logic out of FileSplitter, because it should ideally only have one responsibility: splitting files.

Specify the exact exception you want to handle with a try-except. Handling a bare except is considered a bad practice.

I wouldn't use sys.exit inside methods you might want to test, because it exits the Python interpreter.

In Python 3, f-strings are a much more legible way of doing string interpolation.
Before
new_file_name = &quot;%s.%s&quot; % (self.file_base_name, str(file_number))

After
new_file_name = f&quot;{self.file_base_name}.{file_number}&quot;



Refactored version
#!/usr/bin/env python3

import argparse
import gzip
import os
import itertools

from typing import BinaryIO, Iterator

CHUNK_SIZE = 4096  # bytes


def megabytes_to_bytes(megabytes: int) -&gt; int:
    return megabytes * 1024 * 1024


def chunk_sizes(total_bytes: int, chunk_size: int) -&gt; Iterator[int]:
    for _ in range(total_bytes // chunk_size):
        yield chunk_size

    if remaining_bytes := total_bytes % chunk_size:
        yield remaining_bytes


class FileSplitter:
    input_file_path: str
    output_directory: str
    output_file_base_name: str
    split_size: int  # size of each partition, in MB

    def __init__(
        self, input_file_path: str, output_directory: str, split_size: int
    ) -&gt; None:
        self.input_file_path = input_file_path
        self.output_directory = output_directory
        name = os.path.basename(input_file_path)
        name_without_file_extension = os.path.splitext(name)[0]
        self.output_file_base_name = name_without_file_extension
        self.split_size = split_size

    def get_new_file(self, file_number: int) -&gt; BinaryIO:
        new_file_name = f&quot;{self.output_file_base_name}.{file_number}&quot;
        new_file_path = os.path.join(self.output_directory, new_file_name)
        return open(new_file_path, &quot;wb&quot;)

    def split(self) -&gt; None:
        bytes_per_file = megabytes_to_bytes(self.split_size)
        os.makedirs(self.output_directory, exist_ok=True)

        with gzip.open(self.input_file_path, mode=&quot;rb&quot;) as in_file:
            for file_number in itertools.count(start=1, step=1):
                with self.get_new_file(file_number) as out_file:
                    for chunk_size in chunk_sizes(bytes_per_file, CHUNK_SIZE):
                        chunk = in_file.read(chunk_size)
                        # chunk == &quot;&quot; means we finished reading the input file
                        if not chunk:
                            return
                        out_file.write(chunk)


if __name__ == &quot;__main__&quot;:
    parser = argparse.ArgumentParser(
        description=&quot;Read a gzip-compressed file &quot;
        &quot;and split its uncompressed contents into separate files.&quot;
    )
    parser.add_argument(
        &quot;file_name&quot;, help=&quot;a gzip-compressed file, e.g. foo.gz&quot;
    )
    parser.add_argument(
        &quot;--size&quot;,
        type=int,
        default=500,
        help=&quot;desired size of each partition, in MB&quot;,
    )
    args = parser.parse_args()

    output_directory = os.path.join(os.getcwd(), &quot;pychunks&quot;)
    splitter = FileSplitter(args.file_name, output_directory, args.size)
    splitter.split()