sexta-feira, 15 de janeiro de 2010

Diferenças entre ruby e python: Que perigo

Em ruby:

def a(z=[])
z.push 'a'
end
a # retorna ["a"]
a # retorna ["a"]
a # retorna ["a"]
a # retorna ["a"]

Em python:

def a(z=[]):
z.append('a')
return z
a() # retorna ['a']
a() # retorna ['a','a']
a() # retorna ['a','a','a']
a() # retorna ['a','a','a','a']

10 comentários:

  1. olá

    em ruby eu já sabia o comportamento, agora em python.. que loucura!! mas pode ser util em algumas situações

    grande abraço


    Bruno

    ResponderExcluir
  2. Você poderia explicar pq acontece isso nas duas?

    ResponderExcluir
  3. No ruby eu consigo entender o porque. É um comportamento esperado já que o escopo do parametro "z" refere-se somente ao ciclo de vida dentro do método. A variável é criada à entrada do método e é destruída após o término da execução dele. Já o comportamento no python eu ainda não sei a explicação.

    ResponderExcluir
  4. Esperava mesmo a explicação para o python
    em ruby eu tinha entendido

    ResponderExcluir
  5. É o seguinte.
    Em python, tanto listas como dicionários são mutáveis, e por isso, funcionam como os ponteiros de C.

    Por isso, é uma má prática usar listas ou dicionários vazios como valor padrão de métodos em python.

    Outro exemplo:

    dic1 = {}
    def alterar(nome, valor, w):
    w[nome] = valor

    alterar("campo1", "valor1", dic1)
    alterar("campo2", "valor2", dic1)
    alterar("campo3", "valor3", dic1)

    print dic1
    {'campo1': 'valor1', 'campo2': 'valor2', 'campo3': 'valor3'}

    Por fim, se quiserem entender melhor, aconselho a leitura do python pitfalls, que ensina tudo o que não se deve fazer em python. Ou se for fazer, que use com consciencia.

    http://zephyrfalcon.org/labs/python_pitfalls.html

    Abração!

    ResponderExcluir
  6. Esquisito, pq em ruby listas e hashes também são mutáveis. No caso do exemplo que você deu eu até entendo, mas no caso da declaração do parâmetro só funcionar uma vez e depois ele assumir que é a declaração da primeira chamada é que funcionou é que eu não entendo. Lerei o artigo.

    ResponderExcluir
  7. Isso acontece porque parâmetros default de funções só são atribuídos uma única vez, no momento que a instrução def é interpretada (quando o objeto function é criado)

    Então sempre que você chamar essa função, ela vai usar o mesmo valor atribuído a x (caso você não passe outro na chamada).

    Veja esse outro exemplo:

    >>> def nada(x=[], y=0):
    ... x.append(2)
    ... return x, y
    ...

    Você pode acessar os atributos default:

    >>> nada.__defaults__
    ([], 0)
    >>> id(nada.__defaults__[0])
    911760
    >>> id(nada.__defaults__[1])
    8427924

    >>> nada()
    ([2], 0)
    >>> id(nada.__defaults__[0])
    911760
    >>> id(nada.__defaults__[1])
    8427924

    Veja que ao chamar nada(), a identificação dos objetos não muda, são os mesmos. Você mudou o conteúdo da lista, porque ela é mutável, mas a lista em si é o mesmo objeto, assim como o inteiro também é o mesmo objeto, mas o inteiro você não pode mudar, qualquer operação que fizer com um inteiro vai te retornar outro (somar com outro, subtrair, etc).

    ResponderExcluir
  8. Parece que em Python as variaveis locais tem escopo de classe '-',que loucura.

    ResponderExcluir
  9. Em Python os objetos são realmente como já disseram acima, ponteiros (como os de C).

    O negócio é que Python REAPROVEITA alguns ponteiros. Se você atribuir duas strings iguais a duas variáveis diferentes e verificar com a função id, elas apontam para o mesmo endereço.

    Agora se você fizer isso com Ruby, chamando a função __id__ de dois objetos com a mesma string, terá valores de retorno diferentes.

    Ao executar pela primeira vez ele cria a referência para W. Na segunda vez que o método é chamado, W já está criado (por algum motivo o garbage collector talvez não tenha feito seu trabalho) e ele aproveita a referência.

    Acho que é isso.

    (:

    ResponderExcluir
  10. Complicado... Só entendi porque acontece isso em Python... porque listas são mutáveis... Sei nada de Ruby. Mas boa dica ;)

    ResponderExcluir