Страницы

Поиск по вопросам

суббота, 30 ноября 2019 г.

Можно ли как-нибудь упростить инициализацию классов на Python?

#python #python_3x #классы #конструктор #инициализация


У меня есть конструктор класса примерно вот такого вида:

class A:
    def __init__(self, a1, a2, ..., an):
        self.a1 = a1
        self.a2 = a2
        ...
        self.an = an
    ...


Можно ли как-то упростить процесс инициализации, а то писать n повторяющихся строчек
совсем не интересно.

UPD. Дополню вопрос: 1) Как быть в случае, когда присутствуют значения по умолчанию?

class B:
    def __init__(self, b1=b1_0, b2=b2_0, ..., bn=bn_0):
        self.b1 = b1
        self.b2 = b2
        ...
        self.bn = bn
    ...


2) Возможно ответ последует из п.1, но всё равно задам ещё один вопрос: Что делать,
если для каких-то аргументов есть значение по умолчанию, а для каких-то нет?
    


Ответы

Ответ 1



Если неизменяемый объект хотите создать, представляющий запись с полями, можно использовать collections.namedtuple: from collections import namedtuple A = namedtuple("A", "a1 a2 an") A.__new__.__defaults__ = (0,) * 3 # set default values print(A(1, 2)) # -> A(a1=1, a2=2, an=0) Для изменяемых объектов, можно использовать types.SimpleNamespace: from types import SimpleNamespace print(SimpleNamespace(a1=1, a2=2, an=3)) # -> namespace(a1=1, a2=2, an=3) SimpleNamespace это простая альтернатива конструкции: class NS: pass, которая за вас __init__, __repr__, __eq__ методы определяет. Чтобы указать типы, в неизменяемом случае, можно использовать typing.NamedTuple: from typing import NamedTuple class B(NamedTuple): b1: str = '0' b2: int = 1 print(B()) # -> B(b1='0', b2=1) print(B(b2=3)) # -> B(b1='0', b2=3) PEP 557 -- Data Classes (Python 3.7+) хочет ввести классы данных для случая «изменяемые namedtuple со значениями по умолчанию». Для предыдущих версий Питона, можно использовать неинтегрированную в стандартную библиотеку реализацию из dataclasses пакета: from dataclasses import dataclass # $ pip install dataclasses @dataclass class B: b1: str = '0' b2: int = 1 print(B()) # -> B(b1='0',b2=1) b = B(b2=2) print(b) # -> B(b1='0',b2=2) b.b1 = '1' print(b) # -> B(b1='1',b2=2) В более общем случае, можно воспользоваться attrs пакетом, который множество дополнительной функциональности предоставляет. См. ещё примеры в ответе на похожий вопрос: Как создать коллекцию объектов пользовательского класса?

Ответ 2



Тогда уж так: class A: def __init__(self, **kwargs): self.__dict__.update(kwargs) kwargs - это словарь аргументов (пары "имя": значение). Вариант со значениями по-умолчанию: class A: __defaults = dict(b1=1, b2='2', foo='bar') def __init__(self, **kwargs): self.__dict__.update(self.__defaults) self.__dict__.update(kwargs) Первоначально класс инициализируется значениями по-умолчанию (__defaults), а потом значениями из kwargs. Если в kwargs есть аргументы, присутствующие в __defaults, то новые значения (из kwargs) заменят старые (из __defaults).

Ответ 3



Мне больше нравится следующий метод: class A: def __init__(self, a1, a2, ..., an): self.__dict__.update({k: v for k, v in locals().items() if k != 'self'}) Немного теории: __dict__ хранит определенные пользователем атрибуты объекта, в нашем случае мы ручками добавляем в него новые значения, он представляет из себя словарь, где ключ это имя атрибута, а значение - значение соответствующего атрибута locals() возвращает локальные переменные, в нашем случае - аргументы __init__ self фильтруем, его нам задавать не нужно

Ответ 4



class A: def __init__(self, a1, a2, ..., an): for i in self.__init__.__code__.co_varnames[1:-1]: exec('self.{0} = {0}'.format(i)) Идея в чем: получаем список всех аргументов __init__, потом в цикле, исключая self, присваиваем каждый аргумент атрибуту объекта с таким же названием.

Комментариев нет:

Отправить комментарий