Есть лучший способ сделать это?
class MyClass:
def __init__(self, my_var_1, my_var_2, my_var_3, my_var_4, my_var_5, my_var_6, my_var_7, my_var_8, my_var_9):
self.my_var_1 = my_var_1
self.my_var_2 = my_var_2
self.my_var_3 = my_var_3
self.my_var_4 = my_var_4
self.my_var_5 = my_var_5
self.my_var_6 = my_var_6
self.my_var_7 = my_var_7
self.my_var_8 = my_var_8
self.my_var_9 = my_var_9
Предположим, что имена переменных не следуют шаблону.
Другой вариант — получить аргументы ключевого слова с помощью **kwargs и прокрутить dict для установки атрибутов экземпляра. Вы должны указать аргументы ключевого слова, так как вам нужны ключевые слова для установки имени атрибута.
«Другой» на самом деле следует читать как «Другой». В Python почти всегда есть какие-то другие продвинутые способы что-то сделать. Вы можете получить dict и обновить с его помощью self.__dict__ вместо того, чтобы итерировать его для установки атрибутов, как очень простой пример.
Я думаю, что это одна из немногих небольших неприятностей Python, и, вероятно, классы данных пытались решить эту проблему. Обратите внимание, что классы данных по-прежнему являются обычными классами, они не ограничиваются хранением только полей данных.
Если целью класса является просто хранение набора связанных данных («Обычный старый класс данных»), я бы использовал либо NamedTuple (неизменяемый), либо класс данных (изменяемый):
from typing import NamedTuple
class MyClass(NamedTuple):
my_var_1: the_type
my_var_2: the_type
my_var_3: the_type
my_var_4: the_type
my_var_5: the_type
my_var_6: the_type
my_var_7: the_type
my_var_8: the_type
my_var_9: the_type
Или
from dataclasses import dataclass
@dataclass
class MyClass:
my_var_1: the_type
my_var_2: the_type
# Ignoring other fields for brevity, but they'd be written the same as above
Где the_type
— тип каждого поля. К сожалению, в этом случае синтаксис Python требует указания типа. Типовые подсказки в любом случае являются хорошей практикой, поскольку они помогают уменьшить количество ошибок, которые могут быть обнаружены до запуска кода. Если вам действительно не нужны типы, вы можете использовать старый синтаксис namedtuple
:
from collections import namedtuple
MyClass = namedtuple("MyClass", ["my_var_1", "my_var_2", "my_var_3"])
obj = MyClass(1, 2, 3)
print(obj)
# Prints MyClass(my_var_1=1, my_var_2=2, my_var_3=3)
Все эти параметры сгенерируют для вас этот инициализатор вместе с другими функциями, такими как приятные методы __str__
/__repr__
. Например:
class MyClass(NamedTuple):
my_var_1: int
my_var_2: int
my_var_3: int
my_var_4: int
my_var_5: int
my_var_6: int
my_var_7: int
my_var_8: int
my_var_9: int
obj = MyClass(1, 2, 3, 4, 5, 6, 7, 8, 9)
print(obj)
# Prints MyClass(my_var_1=1, my_var_2=2, my_var_3=3, my_var_4=4, my_var_5=5, my_var_6=6, my_var_7=7, my_var_8=8, my_var_9=9)
Не требуется ручное определение инициализатора.
Основное различие между этими двумя типами заключается в том, что dataclass
можно изменять, а NamedTuple
нельзя (как обычные кортежи):
# obj from above
print(obj.my_var_6) # Prints 6
obj.my_var_6 = 5
Traceback (most recent call last):
File "<input>", line 1, in <module>
AttributeError: can't set attribute
Я бы посоветовал вам показать пример инициализации каждого из этих классов, поскольку это основное внимание в вопросе. Как выглядят сгенерированные инициализаторы? В вашем ответе сейчас я не демонстрирую, что какая-либо альтернатива является лучшим способом инициализации объектов, о чем и был задан вопрос. Типы не были частью исходного вопроса, поэтому ваш ответ кажется немного «яблоки против апельсинов» мне.
@ Стив Да. К сожалению, я написал этот ответ на клавиатуре Swype на моем телефоне в приложении, когда я выходил из автобуса. Я постараюсь улучшить его, когда у меня будет доступ к надлежащему компьютеру.
@Carcigenicate, пожалуйста, не рискуйте своей безопасностью, отвечая на SO-вопросы (думаю, я никогда не говорил ничего подобного на SO). Ответ на самом деле точен и по делу, но он не объясняет почему, и для тех, кто не знает классы данных, может выглядеть так, как говорит Стив, из-за аннотаций типов.
Если вы хотите иметь возможность хранить произвольные пары ключ/значение, как если бы они были атрибутами хранящего их объекта, без необходимости определять их явно, вы можете определить класс, который действует как dict
, но разрешает доступ к своим ключам/значениям. через ссылки на атрибуты, например:
class AttrDict(dict):
def __init__(self, *args, **kwargs):
super(AttrDict, self).__init__(*args, **kwargs)
self.__dict__ = self
class MyClass(AttrDict):
def __init__(self, *args, **kwargs):
super(MyClass, self).__init__(*args, **kwargs)
# Attributes built into your class
self.predef = 'predefind'
pass
# Define attributes at class creation time
mc = MyClass((('abc', '123'), ('foo', 'bar')))
print(mc.abc)
print(mc.foo)
# See that the class defined attribute is there
print(mc.predef)
# Change the value of an existing attribute
mc.abc = 'blah'
print(mc.abc)
# Create a new attribute
mc.xxx = 1111
print(mc.xxx)
from typing import NamedTuple
class MyClass(NamedTuple):
my_var_1: int
my_var_2: str
Результат:
123
bar
predefined
blah
1111
Если вы хотите применить определенный набор аргументов/атрибутов, вам понадобится что-то более сложное, чем это, или вы захотите использовать одну из других альтернатив. Но если вы хотите иметь возможность использовать произвольные атрибуты объекта с минимальным количеством установочного кода (никакого), это ваш ответ.
«Предположим, что имена переменных не следуют шаблону». - тогда имело бы смысл не вкладывать в имена большой очевидный шаблон.