В Python 3.4+, почему я должен использовать namedtuple поверх SimpleNamespace, когда не используется dict, они кажутся очень похожими

11

В тот или иной момент вы можете встретить функции с множеством аргументов. Иногда имеет смысл объединить некоторые аргументы в супер-аргументы. Я часто делал это с помощью диктовок, но сейчас я смотрю на лучшие способы сделать это.

Я хотел бы включить ...

def do_something(ax, ay, az, bu, bv, c):
    # Do something

... в ...

def do_something(a, b, c):
    # Do something

... где aи bсодержат свои подвариации.

Один из способов сделать это состоит в следующем:

A = namedtuple('A', 'x, y, z')
a = A(ax, ay, az)
B = namedtuple('B', 'u, v')
b = B(bu, bv)

Тем не менее, это кажется проще:

a = SimpleNamespace(x=ax, y=ay, z=az)
b = SimpleNamespace(u=bu, v=bv)

В чем недостаток? То, что так aи bне набрано? Они не объекты A и B?

(Кстати, не беспокойтесь об именах переменных. Обычно я не использую их как короткие имена.)

Андре Кристоффер Андерсен
источник
1
Недостатков как таковых нет, это просто разные вещи. Для начала именованные корни являются неизменяемыми, а пространства имен изменяемыми. Изменчивый лучше или хуже неизменного? Это зависит от того, что вам нужно или чего вы хотите, во многих случаях это просто не имеет значения. Ваша функция, вероятно, будет работать с любым объектом с необходимыми атрибутами, как его создать, зависит от вызывающего.
Прекратить причинять вред Монике
@ Goyo Спасибо. «Недостаток» - это неуклюжий способ сказать это. Я не хотел сказать, что одно по своей сути лучше другого. Просто хотел плюсы и минусы. Еще раз спасибо.
Андре Кристоффер Андерсен
1
не должна ли 4-я строка выглядеть как "b = B (bu, bv)"?
Ален Сильяк
@AlenSiljak Да, это должно быть. Я исправлю это сейчас.
Андре Кристоффер Андерсен

Ответы:

21

SimpleNamespaceэто просто красивый фасад поверх словаря. Это позволяет использовать свойства вместо индексных ключей. Это приятно, так как он очень гибкий и простой в управлении.

Недостатком этой гибкости является то, что она не обеспечивает никакой структуры. Ничто не мешает кому-то звонить SimpleNamespace(x=ax, y=ay)del a.zв какой-то момент позже). Если этот экземпляр передается вашей функции, исключение возникает при попытке доступа к полю.

Напротив, namedtupleпозволяет создавать структурированный тип. Тип будет иметь имя, и он будет знать, какие поля он должен иметь. Вы не сможете создать экземпляр без каждого из этих полей, и они не могут быть удалены позже. Кроме того, экземпляр является неизменным, поэтому вы будете знать, что значение a.xвсегда будет одинаковым.

Вы сами решаете, нужна ли вам гибкость SimpleNamespace, или вы предпочитаете иметь структуру и гарантии, предоставляемые вами namedtuple.

unholysampler
источник
2

Мне очень нравится ответ о структурированных, а не нет, поэтому я просто приведу конкретный пример ниже.

SimpleNamespaceпримет ключи, которые начинаются с _. Если вы ищете быстрый и простой способ преобразования, скажем, JSON, который вы не управляете объектами с именами полей, это очень удобно:

d = {"_id": 2342122, "text": "hi there!"} # Elasticsearch gives this id!
e = SimpleNamespace(**d) # works
Name = namedtuple("Name", sorted(d)) # ValueError so we can't do Name(**d)

Обратите внимание, что вы видите, что это namedtupleдает нам целый дополнительный объект, который SimpleNamespaceникогда не будет. Каждая из SimpleNamespaceних на самом деле является «уникальной снежинкой», хотя namedtupleсуществует без каких-либо конкретных значений. Везде, где вам нужны абстракции, которые обобщают конкретные значения, вам, вероятно, следует отдавать предпочтение.

Алекс Мур-Ниеми
источник
1

Резюме SimpleNamespace

Это позволяет инициализировать атрибуты при построении объекта:

sn = SimpleNamespace(a=1, b=2)

Обеспечивает читабельный

repr(): eval(repr(sn)) == sn

Он отменяет сравнение по умолчанию. Вместо сравнения id(), он сравнивает значения атрибутов.

Влад Безден
источник