Метод класса для создания нового экземпляра

Я серьезно занимаюсь ООП с Python и сталкиваюсь с некоторыми проблемами. У меня есть класс с инициализатором, тремя закрытыми переменными и методами установки и получения для каждой переменной.

Кроме того, у него есть метод ввода, который позволяет пользователю вводить значение для переменных при их вызове, вместо того, чтобы требовать input вызова в основной функции, которая передает значение методу набора классов. Таким образом, я могу немедленно проверить ввод пользователя в методе, соответствующем требованиям к данным:

def input_first_name(self):
    done = 0

    while done == 0:
        print("Input First Name:",end='')
        first_name = input()
        if first_name.isalpha():
            self.__first_name = first_name
            done = 1
        else:
            print("Invalid Entry. Please use alphabetic characters only.")

Используя эти методы ввода, у меня есть другой метод, который использует их для создания нового экземпляра класса (просто подождите, я уже знаю, о чем вы думаете):

@classmethod
def new(cls):
    cls.input_first_name(cls)
    cls.input_last_name(cls)
    cls.input_birthday(cls)

    return cls 

Итак, я знаю, что, используя это, я фактически не буду создавать новый экземпляр. Однако использование методов не приводит к немедленному возникновению ошибок. Но когда я обращаюсь к своему вновь созданному экземпляру с помощью метода get, у меня возникают проблемы, которые, как я знаю, связаны с отсутствием создания экземпляра.

new_relative = Relative.new()

print(new_relative.get_first_name())

приводит к:

TypeError: get_first_name() missing 1 required positional argument: 'self'       

Есть ли способ использовать метод класса в моей основной функции для создания экземпляра нового экземпляра, который использует мою идею пользовательского ввода и проверки простым и элегантным образом?

Для справки, минимальный образец класса:

class Relative:
    __first_name = ""
    __last_name = ""
    __birthday = 0

    def __init__(self, first_name,last_name,birthday):
        self.__first_name = first_name
        self.__last_name = last_name
        self.__birthday = birthday

    def get_first_name(self):
        return self.__first_name

person Andres Mino    schedule 21.04.2015    source источник


Ответы (1)


Я бы сделал это с помощью property (замена Pythonic для бесконечных gets и sets, см., например, Python @property против геттеров и сеттеры), и оставьте только input для метода класса. Вот минимальный пример:

class Relative:

    def __init__(self, first_name=None):
        self.first_name = first_name

    @staticmethod
    def _valid_name(name):
        return name is None or name.isalpha()
        # this will choke if name isn't None or a string - maybe add more rules?

    @property
    def first_name(self):
        return self._first_name

    @first_name.setter
    def first_name(self, new_name):
        if not self._valid_name(new_name):
            raise ValueError('Invalid first name: {!r}'.format(new_name))
        self._first_name = new_name

    @classmethod
    def from_input(cls):
        relative = cls()
        while True:
            # you could alternatively use cls._valid_name directly here
            try:
                relative.first_name = input('Enter first name: ')
            except ValueError:
                print('Please try again.')
            else:
                break
        return relative

В использовании:

>>> uncle_bob = Relative.from_input()
Enter first name: 123
Please try again.
Enter first name: *
Please try again.
Enter first name: Robert
>>> uncle_bob.first_name
'Robert'
>>> uncle_bob.first_name = '123'
Traceback (most recent call last):
  File "<pyshell#14>", line 1, in <module>
    uncle_bob.first_name = '123'
  File "<pyshell#11>", line 17, in first_name
    raise ValueError('Invalid first name: {!r}'.format(new_name))
ValueError: Invalid first name: '123'

Обратите внимание, что:

  1. Абстрагирование проверки имени на Relative._valid_name упрощает проверку имени и фамилии по одним и тем же правилам (вы также можете добавить отчество и легко сделать его таким же).
  2. Использование @property означает, что пользователь не может задать неправильное имя в любой момент, не заставляя его вызывать определенный метод для получения/установки значений.
  3. Я использовал частное по соглашению имя с одним подчеркиванием для внутренней переменной, а не искаженное имя с двойным подчеркиванием — см., например, руководство по стилю.
person jonrsharpe    schedule 21.04.2015