Não faço ideia do que realmente são chamados, mas os vejo o tempo todo. A implementação do Python é algo como:
x += 5
como uma notação abreviada para x = x + 5
.
Mas por que isso é considerado uma boa prática? Eu o encontrei em quase todos os livros ou tutoriais de programação que li para Python, C, R e assim por diante. Entendo que é conveniente, economizando três pressionamentos de tecla, incluindo espaços. Mas eles sempre parecem me atrapalhar quando leio código e, pelo menos na minha opinião, tornam menos legível, não mais.
Estou perdendo algum motivo claro e óbvio que eles são usados em todo o lugar?
programming-practices
Fomite
fonte
fonte
x += 5
que o CLRx = x + 5
? Ou é realmente apenas açúcar sintático, como você sugere?Respostas:
Não é taquigrafia.
O
+=
símbolo apareceu na linguagem C na década de 1970 e - com a idéia C de "assembler inteligente" corresponde a um modo de instrução e endereço da máquina claramente diferente:Coisas como "
i=i+1
","i+=1
"e"++i
", embora em um nível abstrato produzam o mesmo efeito, correspondem em baixo nível a uma maneira diferente de trabalhar do processador.Em particular, essas três expressões, assumindo que a
i
variável reside no endereço de memória armazenado em um registro da CPU (vamos chamá-laD
- pense nela como um "ponteiro para int") e a ALU do processador pega um parâmetro e retorna um resultado em um "acumulador" (vamos chamá-lo de A - pense nisso como um int).Com essas restrições (muito comuns em todos os microprocessadores daquele período), a tradução provavelmente será
A primeira maneira de fazer isso é desagradável, mas é mais geral ao operar com variáveis em vez de constante (
ADD A, B
ouADD A, (D+x)
) ou ao traduzir expressões mais complexas (todas elas se resumem na operação push de baixa prioridade em uma pilha, chame de alta prioridade, pop). e repita até que todos os argumentos tenham sido eliminados).A segunda é mais típica da "máquina de estados": não estamos mais "avaliando uma expressão", mas "operando um valor": ainda usamos a ALU, mas evitamos mover valores, pois o resultado pode substituir o parâmetro. Esse tipo de instrução não pode ser usado onde expressões mais complicadas são necessárias:
i = 3*i + i-2
não pode ser operado no local, poisi
é necessário mais vezes.O terceiro - mesmo mais simples - nem considera a idéia de "adição", mas usa um circuito mais "primitivo" (no sentido computacional) para um contador. A instrução é reduzida, carrega mais rápido e é executada imediatamente, pois a rede combinatória necessária para atualizar um registro para torná-lo um contador é menor e, portanto, mais rápida que a de um somador completo.
Com os compiladores contemporâneos (consulte C, a essa altura), habilitando a otimização do compilador, a correspondência pode ser trocada com base na conveniência, mas ainda há uma diferença conceitual na semântica.
x += 5
significaMas
x = x + 5
significa:Obviamente, a otimização pode
&x
no acumuladorfazendo com que o código otimizado coincida com o código
x += 5
.Mas isso pode ser feito apenas se "encontrar x" não tiver efeitos colaterais, caso contrário
e
são semanticamente diferentes, pois os
x()
efeitos colaterais (admitirx()
é uma função que faz coisas estranhas e retorna umint*
) serão produzidos duas ou uma vez.A equivalência entre
x = x + y
ex += y
é, portanto, devida ao caso particular em que+=
e=
é aplicada a um valor l direto.Para migrar para o Python, ele herdou a sintaxe de C, mas como não há tradução / otimização ANTES da execução em linguagens interpretadas, as coisas não são necessariamente tão intimamente relacionadas (já que há uma etapa a menos de análise). No entanto, um intérprete pode se referir a diferentes rotinas de execução para os três tipos de expressão, aproveitando diferentes códigos de máquina, dependendo de como a expressão é formada e do contexto da avaliação.
Para quem gosta de mais detalhes ...
Toda CPU possui uma ALU (unidade aritmética-lógica) que é, em sua essência, uma rede combinatória cujas entradas e saídas são "conectadas" aos registradores e / ou memória, dependendo do código de operação da instrução.
As operações binárias são tipicamente implementadas como "modificador de um registrador acumulador com uma entrada obtida" em algum lugar ", em que algum lugar pode estar - dentro do próprio fluxo de instruções (típico para o manifesto de manifestação: ADD A 5) - dentro de outro registro (típico para computação de expressão com temporários: por exemplo, ADD AB) - dentro da memória, em um endereço fornecido por um registro (típico de busca de dados, por exemplo: ADD A (H)) - H, nesse caso, funciona como um ponteiro de cancelamento de referência.
Com esse pseudocódigo,
x += 5
éenquanto
x = x+5
éOu seja, x + 5 fornece um temporário que é atribuído posteriormente.
x += 5
opera diretamente em x.A implementação real depende do conjunto real de instruções do processador: se não houver
ADD (.) c
código de operação, o primeiro código se tornará o segundo: de jeito nenhum.Se houver tal código de operação e a otimização estiver ativada, a segunda expressão, após eliminar os movimentos reversos e ajustar o código de registro, será a primeira.
fonte
Dependendo de como você pensa sobre isso, é realmente mais fácil de entender, porque é mais direto. Considere por exemplo:
x = x + 5
chama o processamento mental de "pegue x, adicione cinco a ele e atribua esse novo valor de volta a x"x += 5
pode ser pensado como "aumentar x em 5"Portanto, não é apenas uma abreviação, na verdade descreve a funcionalidade de maneira muito mais direta. Ao ler um monte de código, é muito mais fácil entender.
fonte
x = x + 5
ainda me incomodava. Quando entrei em matemática mais tarde, isso me incomodou ainda mais. O usox += 5
é significativamente mais descritivo e faz muito mais sentido como expressão.reallyreallyreallylongvariablename = reallyreallyreallylongvariablename + 1
... oh não !!! Um erro de digitaçãoPelo menos em Python ,
x += y
ex = x + y
pode fazer coisas completamente diferentes.Por exemplo, se fizermos
então
a += [3]
resultará ema == b == [3]
, enquantoa = a + [3]
resultará ema == [3]
eb == []
. Ou seja,+=
modifica o objeto no local (bem, pode ser, você pode definir o__iadd__
método para fazer praticamente o que quiser), enquanto=
cria um novo objeto e vincula a variável a ele.Isso é muito importante ao realizar trabalhos numéricos com o NumPy , pois você frequentemente termina com várias referências a diferentes partes de uma matriz e é importante garantir que você não modifique inadvertidamente parte de uma matriz à qual existem outras referências, ou copiar desnecessariamente matrizes (que podem ser muito caras).
fonte
__iadd__
: existem línguas onde você pode criar uma referência imutável para uma estrutura de dados mutáveis, onde aoperator +=
é definido, por exemplo, scala:val sb = StringBuffer(); lb += "mutable structure"
vsvar s = ""; s += "mutable variable"
. o modifica ainda mais o conteúdo da estrutura de dados, enquanto o último faz com que a variável aponte para uma nova.É chamado de idioma . Os idiomas de programação são úteis porque são uma maneira consistente de escrever uma construção de programação específica.
Sempre que alguém escreve,
x += y
você sabe quex
está sendo incrementadoy
e não por alguma operação mais complexa (como prática recomendada, normalmente eu não misturaria operações mais complicadas e essas abreviações de sintaxe). Isso faz mais sentido ao incrementar em 1.fonte
++x
ex+=1
são equivalentes em C e Java (talvez também em C #), embora não necessariamente em C ++, devido à semântica complexa do operador. A chave é que ambos avaliamx
uma vez, incrementam a variável em um e têm um resultado que é o conteúdo da variável após a avaliação.++x + 3
não é a mesma coisa quex += 1 + 3
. Entre parênteses oex += 1
é idêntico. Como declarações por si mesmas, são idênticas.++x + 3
,x += 1 + 3
ou(x += 1) + 3
ter indefinido comportamento (assumindo que o valor "encaixa" resultantes).Para esclarecer um pouco o argumento de @ Pubby, considere
someObj.foo.bar.func(x, y, z).baz += 5
Sem o
+=
operador, existem dois caminhos a seguir:someObj.foo.bar.func(x, y, z).baz = someObj.foo.bar.func(x, y, z).baz + 5
. Isso não é apenas extremamente redundante e longo, mas também mais lento. Portanto, seria precisotmp := someObj.foo.bar.func(x, y, z); tmp.baz = tmp.bar + 5
. Tudo bem, mas é muito barulho para uma coisa simples. Isso é realmente muito parecido com o que acontece no tempo de execução, mas é entediante escrever e usar apenas+=
mudará o trabalho para o compilador / intérprete.A vantagem de
+=
outros operadores é inegável, enquanto se acostumar com eles é apenas uma questão de tempo.fonte
É verdade que é mais curto e fácil, e é provável que tenha sido inspirado pela linguagem assembly subjacente, mas a razão pela qual é uma prática recomendada é que ela evita toda uma classe de erros e facilita a revisão do código e a certeza o que faz.
Com
Como há apenas um nome de variável envolvido, você tem certeza do que a declaração faz.
Com
RidiculouslyComplexName = RidiculosulyComplexName + 1;
Sempre há dúvidas de que os dois lados são exatamente iguais. Você viu o bug? Fica ainda pior quando há inscrições e qualificadores.
fonte
Embora a notação + = seja idiomática e mais curta, essas não são as razões pelas quais é mais fácil ler. A parte mais importante da leitura do código é o mapeamento da sintaxe para o significado e, quanto mais a sintaxe corresponder aos processos de pensamento do programador, mais legível será (esta também é a razão pela qual o código clichê é ruim: não faz parte do pensamento processo, mas ainda necessário para fazer o código funcionar). Nesse caso, o pensamento é "incrementar variável x por 5", não "seja x o valor de x mais 5".
Há outros casos em que uma notação menor é ruim para facilitar a leitura, por exemplo, quando você usa um operador ternário em que uma
if
instrução seria mais apropriada.fonte
Para ter uma ideia de por que esses operadores estão nos idiomas 'C-style', para começar, há um trecho da K&R 1st Edition (1978), 34 anos atrás:
Acho que, nesta passagem, ficou claro que Brian Kernighan e Dennis Ritchie (K&R) acreditavam que os operadores de atribuição composta ajudavam na legibilidade do código.
Já faz muito tempo que a K&R escreveu isso, e muitas das "melhores práticas" sobre como as pessoas deveriam escrever código mudaram ou evoluíram desde então. Mas essa pergunta programmers.stackexchange é a primeira vez que lembro que alguém fez uma reclamação sobre a legibilidade de atribuições compostas, então me pergunto se muitos programadores as consideram um problema. Então, novamente, enquanto digito isso, a pergunta tem 95 votos positivos, então talvez as pessoas os achem distraídos ao ler código.
fonte
Além da legibilidade, eles realmente fazem coisas diferentes:
+=
não precisam avaliar o operando esquerdo duas vezes.Por exemplo,
expr = expr + 5
seria avaliadoexpr
duas vezes (supondo queexpr
seja impuro).fonte
expr = expr + 5
eexpr += 5
expr
tem efeitos colaterais.expr
tiver efeitos colaterais,expr = expr + 5
deve invocar esses efeitos colaterais duas vezes.volatile
efeitos colaterais, ex=x+5
ex+=5
têm os mesmos efeitos colaterais quandox
évolatile
Além dos méritos óbvios que outras pessoas descreveram muito bem, quando você tem nomes muito longos, é mais compacto.
ou
fonte
É conciso.
É muito mais curto para digitar. Envolve menos operadores. Tem menos área de superfície e menos oportunidade de confusão.
Ele usa um operador mais específico.
Este é um exemplo artificial e não tenho certeza se os compiladores reais implementam isso. x + = y realmente usa um argumento e um operador e modifica x no lugar. x = x + y poderia ter uma representação intermediária de x = z onde z é x + y. Este último usa dois operadores, adição e atribuição, e uma variável temporária. O operador único deixa super claro que o lado do valor não pode ser outra coisa senão y e não precisa ser interpretado. E, teoricamente, poderia haver alguma CPU sofisticada que possua um operador mais-igual que seja executado mais rapidamente do que um operador mais e um operador de atribuição em série.
fonte
ADD
instruções em muitas CPUs têm variantes que operam diretamente em registradores ou memória usando outros registradores, memória ou constantes como o segundo adendo. Nem todas as combinações estão disponíveis (por exemplo, adicionar memória à memória), mas há o suficiente para ser útil. De qualquer forma, qualquer compilador com um otimizador decente saberá gerar o mesmo código parax = x + y
o que seriax += y
.É um bom idioma. Se é mais rápido ou não, depende do idioma. Em C, é mais rápido porque se traduz em uma instrução para aumentar a variável no lado direito. Linguagens modernas, incluindo Python, Ruby, C, C ++ e Java, suportam a sintaxe op =. É compacto e você se acostuma rapidamente. Como você o verá muito no código de outras pessoas (OPC), você também pode se acostumar e usá-lo. Aqui está o que acontece em alguns outros idiomas.
No Python, a digitação
x += 5
ainda causa a criação do objeto inteiro 1 (embora possa ser extraído de um pool) e o órfão do objeto inteiro contendo 5.Em Java, causa uma conversão tácita. Tente digitar
fonte
x+=5
tem tão pouco significado quantox=x+5
; por exemplo, em Haskell, o último não causax
aumento de 5 - em vez disso, gera um loop infinito de recursão. Quem iria querer uma abreviação para isso?+=
não depende tanto do idioma quanto do processador . Por exemplo, o X86 é uma arquitetura de dois endereços, que suporta apenas+=
nativamente. Uma declaração comoa = b + c;
deve ser compilada,a = b; a += c;
porque simplesmente não há instruções que possam colocar o resultado da adição em qualquer outro lugar do que no lugar de um dos summands. A arquitetura Power, por outro lado, é uma arquitetura de três endereços que não possui comandos especiais+=
. Nesta arquitetura, as instruçõesa += b;
ea = a + b;
sempre compilam no mesmo código.Operadores como esses
+=
são muito úteis quando você está usando uma variável como acumulador , ou seja, um total em execução:É muito mais fácil ler do que:
No primeiro caso, conceitualmente, você está modificando o valor
x
. No segundo caso, você está computando um novo valor e atribuindo-o ax
cada vez. E embora você provavelmente nunca escreva um código tão simples assim, a idéia permanece a mesma ... o foco está no que você está fazendo com um valor existente, em vez de criar um novo valor.fonte
Considere isto
D0 você realmente quer escrever
fonte
As outras respostas têm como alvo os casos mais comuns, mas há outro motivo: em algumas linguagens de programação, ele pode ser sobrecarregado; por exemplo, Scala .
Pequena lição sobre Scala:
Se uma classe define apenas o
+
operador,x+=y
é realmente um atalho dex=x+y
.Se uma classe sobrecarregar
+=
, no entanto, elas não são:Além disso, operadores
+
e+=
são dois operadores separados (e não apenas estes: + a, ++ a, a ++, a + ba + = b também são operadores diferentes); nos idiomas em que a sobrecarga do operador está disponível, isso pode criar situações interessantes. Assim como descrito acima - se você sobrecarregar o+
operador para realizar a adição, lembre-se de que também+=
será necessário sobrecarregar.fonte
+=
e depois definira+b
como "fazer uma cópiaa
, colocarb
nela usando+=
, retornar a cópia dea
".Diga uma vez e apenas uma vez: em
x = x + 1
, eu digo 'x' duas vezes.Mas nunca escreva 'a = b + = 1' ou teremos que matar 10 gatinhos, 27 ratos, um cachorro e um hamster.
Você nunca deve alterar o valor de uma variável, pois isso torna mais fácil provar que o código está correto - consulte programação funcional. No entanto, se o fizer, é melhor não dizer as coisas apenas uma vez.
fonte