No Python 3.4 ou superior, por que devo usar o nomeduplo sobre o SimpleNamespace quando não uso o dict, eles parecem muito semelhantes

11

Em um ponto ou outro, você pode encontrar funções com muitos argumentos. Às vezes, faz sentido combinar alguns argumentos em super argumentos. Eu sempre fiz isso com ditados, mas agora estou procurando maneiras melhores de fazê-lo.

Eu gostaria de virar ...

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

... para dentro ...

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

... onde ae bconter suas subvariações.

Uma maneira de fazer isso é:

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

No entanto, isso parece mais simples:

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

Qual é a desvantagem? O fato de que ae bnão são bem digitados? Eles não são objetos A e B?

(Aliás, não se preocupe com os nomes das variáveis. Normalmente não uso como nomes curtos de variáveis.)

André Christoffer Andersen
fonte
1
Não há desvantagens em si, são apenas coisas diferentes. Para iniciantes, os nomuplos são imutáveis, enquanto os namespaces são mutáveis. Mutável é melhor ou pior que imutável? Depende do que você precisa ou deseja, em muitos casos isso simplesmente não importa. Sua função provavelmente funcionaria com qualquer objeto com os atributos necessários; como construí-lo depende do chamador.
Pare de prejudicar Monica
@Goyo Obrigado. A coisa "desvantagem" era uma maneira desajeitada de dizer isso. Não quis dizer que um é inerentemente melhor que o outro. Só queria os prós e contras. Obrigado novamente.
André Christoffer Andersen
1
a quarta linha não deve se parecer com "b = B (bu, bv)"?
Alen Siljak
@AlenSiljak Sim, deveria. Eu vou consertar agora.
André Christoffer Andersen

Respostas:

21

SimpleNamespaceé basicamente apenas uma bela fachada em cima de um dicionário. Ele permite que você use propriedades em vez de chaves de índice. Isso é legal, pois é super flexível e fácil de manipular.

A desvantagem dessa flexibilidade é que ela não fornece nenhuma estrutura. Não há nada para impedir alguém de ligar SimpleNamespace(x=ax, y=ay)(e del a.zem algum momento depois). Se essa instância for passada para sua função, a exceção ocorrerá quando você tentar acessar o campo.

Por outro lado, namedtuplepermite criar um tipo estruturado. O tipo terá um nome e saberá quais campos ele deve ter. Você não poderá criar uma instância sem cada um desses campos e eles não poderão ser removidos posteriormente. Além disso, a instância é imutável, portanto você saberá que o valor em a.xsempre será o mesmo.

Cabe a você decidir se precisa da flexibilidade que SimpleNamespacelhe oferece, ou se prefere ter a estrutura e as garantias fornecidas por namedtuple.

unholysampler
fonte
2

Eu realmente gosto da resposta sobre estruturado versus não, por isso estou apenas fornecendo um exemplo concreto abaixo.

SimpleNamespaceaceitará chaves que começam com _. Se você está procurando uma maneira rápida e fácil de transformar, digamos, JSON que você não controla em objetos com nomes de campos, isso é muito útil:

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)

Observe acima que você pode ver que namedtuplenos dá um objeto extra inteiro que SimpleNamespacenunca será. Cada um SimpleNamespaceé realmente um "floco de neve único", enquanto namedtupleexiste sem nunca ser instanciado com valores concretos. Onde quer que você precise de abstrações que generalizem valores concretos, você provavelmente deve preferir.

Alex Moore-Niemi
fonte
1

Sumário de SimpleNamespace

Permite inicializar atributos enquanto constrói o objeto:

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

Ele fornece uma legível

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

Ele substitui a comparação padrão. Em vez de comparar id(), ele compara valores de atributo.

Vlad Bezden
fonte