Q: How to print the Name and Gender by using class and methods? I am writing a program that will render the name and gender of people entered. The program should ask for a name and then for the gender until the user only presses Enter as the name. Only after Enter has been pressed, the text should be output for all persons. I have tried to implement some code before, but unfortunately I am not getting anywhere.
class Person():
    def __init__(self):
        self.name = input('Name: ')
        self.gender= input('Gender: ')

    def give_name(self):
        return self.name

    def give_gender(self):
        return self.gender

    def show_name(self):
        return self.give_name() + ' is ' + self.give_gender()

my_person = Person()
print(my_person.show_name())

At the end should output the following:
Name: Maya Mitermacht
Gender: female 
Name: Max Mustermann
Gender: male
Name:
Maya Mitermacht is female
Max Mustermann is male


A: Inspired by @Pac0, I'm submitting another answer, though his is already enough and works well!
The one thing that kept tickling me at the back of my head was only a matter of concept, not efficiency or effectiveness. The idea is: if we want to stop creating Persons, we should do so before creating the next one, not after its creation.
For that, I present an alternative using __new__. Perhaps not the best use of it, but it works as intended and has that 'feature' to stop the creation of objects between instances, not after the last one.
class Person:
    name: str
    gender: str

    def __new__(cls):
        cls.name = input('Name: ')
        if cls.name:
            cls.gender = input('Gender: ')
            return super(Person, cls).__new__(cls)
        else:
            raise ValueError("A name is required to create a Person instance")

    def __init__(self):
        self.name = Person.name
        self.gender = Person.gender

    def give_name(self):
        return self.name

    def give_gender(self):
        return self.gender

    def show_name(self):
        return self.give_name() + ' is ' + self.give_gender()


def main():
    people = []
    while True:
        try:
            new_person = Person()
        except ValueError:
            break
        else:
            people.append(new_person)

    for person in people:
        print(person.show_name())


if __name__ == "__main__":
    main()

The idea is to use __new__ to capture input, then check if a name was provided. If it was, move on to get gender and create the new Person instance. Else, raise an exception, as a person requires a name.
The usage is simple, we can create as many people as we want (and here I'm storing them all in people list. When we are done, provide no name to the next Person, which will raise ValuError, that we catch and break from the loop. Then, print all names.

Since I'm already here, I'll also make some general suggestions to the code. First, getters and setters are not common in Python, and usually are discouraged. Therefore, a person.give_name() could simply be swapped by person.name, and even more when that's only used inside the class. Using that idea, we could simplify the methods give_name, give_gender and show_name to simply:
class Person:
    name: str
    gender: str

    .
    .
    .

    def show_name(self):
        return self.name + ' is ' + self.gender

Next, I believe we can make good use of one more "magical" method from Python, and that is __str__. Since we are using name and gender to identify a Person instance, and we want to show that info in a particular way, we can simply define how a Person is represented as a string. We do that using __str__ (inside the class), such as:
def __str__(self):
    return f"{self.name} is {self.gender}"

Note: f-strings are very neat and easy to use, but if you're not a fan, replace it with self.name + ' is ' + self.gender, originally.
That way, when we call str() on a Person instance, it will return that same string. If we directly try to print() a Person, we'll have the same result. So the loop to print the names doesn't need print(person.show_name()) anymore, it can be simply print(person) -- which seems more readable to me, and a bit shorter!

So, to wrap it all up, everything would look like this:
class Person:
    name: str
    gender: str

    def __new__(cls):
        cls.name = input('Name: ')
        if cls.name:
            cls.gender = input('Gender: ')
            return super(Person, cls).__new__(cls)
        else:
            raise ValueError("A name is required to create a Person instance")

    def __init__(self):
        self.name = Person.name
        self.gender = Person.gender

    def __str__(self):
        return f"{self.name} is {self.gender}"


def main():
    people = []
    while True:
        try:
            new_person = Person()
        except ValueError:
            break
        else:
            people.append(new_person)

    for person in people:
        print(person)


if __name__ == "__main__":
    main()


A: Since the input will decide to create object or not, it can't be the role of the object/class itself to automatically initialize it. You must have a way to know if the input was empty or not.
Hence, you should delegate this to another class method, call it from your main program and check return value. For instance, in the code below, I chose to return True or False in my function.
class Person():
    def initialize(self):
        name_entered = input('Name: ')
        if name_entered == '':
            return False
            
        gender_entered = input('Gender: ')
        if gender_entered == '':
            return False

        self.name = name_entered
        self.gender = gender_entered
        return True

    def give_name(self):
        return self.name

    def give_gender(self):
        return self.gender

    def show_name(self):
        return self.give_name() + ' is ' + self.give_gender()

persons = []
while True:
    my_person = Person()
    isCreated = my_person.initialize()
    # if it returned False, then user has entered nothing for gender or name, stop the loop
    if (not isCreated):
        break
    #other wise, person is initialized, let's add it to the list and continue
    persons.append(my_person)

for person in persons:
    print(person.show_name())

