Declaração de variável Python

86

Aprendendo Python , e tem algumas dúvidas básicas.

1. Eu vi a declaração de variável (caminho aqui) como

class writer:
    path = ""

às vezes, nenhuma declaração explícita, mas inicializa por meio __init__.

def __init__(self, name):
    self.name = name

Eu entendo o propósito de __init__, mas é aconselhável declarar variável em qualquer outra função.

2.Como posso criar uma variável para conter um tipo personalizado?

class writer:
    path = "" # string value
    customObj = ??
bsr
fonte
12
E, em relação à 2ª questão: em Python, variáveis ​​não têm tipo: têm valores. Portanto, qualquer variável pode conter seus objetos personalizados.
jpaugh
4
Ajudará se você parar de pensar em nomes / identificadores como sendo variáveis . Eles são referências a objetos
John La Rooy
2
@gnibbler Ou nomes para eles ...
detly
1
@detly, acho que nomes é uma escolha ruim. As coisas geralmente têm apenas um nome, enquanto um objeto pode ter várias referências
John La Rooy
2
@JohnClements, mas python não funciona como matemática. Se você quiser entender o python, você precisa saber que os objetos com um __name__atributo têm um "nome". Nem todos os objetos têm um __name__atributo, mas você ainda pode ter referências a eles. Se começarmos a chamar uma "referência" de "nome", estaremos prestando um péssimo serviço aos iniciantes no futuro.
John La Rooy

Respostas:

202

Ok, as primeiras coisas primeiro.

Não existe "declaração de variável" ou "inicialização de variável" em Python.

Existe simplesmente o que chamamos de "atribuição", mas provavelmente deveríamos chamar apenas de "nomeação".

Atribuição significa "este nome do lado esquerdo agora se refere ao resultado da avaliação do lado direito, independentemente do que foi referido antes (se houver)".

foo = 'bar' # the name 'foo' is now a name for the string 'bar'
foo = 2 * 3 # the name 'foo' stops being a name for the string 'bar',
# and starts being a name for the integer 6, resulting from the multiplication

Como tal, os nomes do Python (um termo melhor do que "variáveis", sem dúvida) não têm tipos associados; os valores sim. Você pode reaplicar o mesmo nome a qualquer coisa, independentemente de seu tipo, mas a coisa ainda tem um comportamento que depende de seu tipo. O nome é simplesmente uma forma de se referir ao valor (objeto). Isso responde à sua segunda pergunta: você não cria variáveis ​​para conter um tipo personalizado. Você não cria variáveis ​​para conter nenhum tipo específico. Você não "cria" variáveis. Você dá nomes aos objetos.

Segundo ponto: Python segue uma regra muito simples quando se trata de classes, que na verdade é muito mais consistente do que linguagens como Java, C ++ e C #: tudo que é declarado dentro do classbloco faz parte da classe . Portanto, functions ( def) escritas aqui são métodos, ou seja, parte do objeto de classe (não armazenado por instância), assim como em Java, C ++ e C #; mas outros nomes aqui também fazem parte da classe. Novamente, os nomes são apenas nomes, não têm tipos associados e as funções também são objetos em Python. Portanto:

class Example:
    data = 42
    def method(self): pass

As classes também são objetos , em Python.

Portanto, agora criamos um objeto chamado Example, que representa a classe de todas as coisas que são Examples. Este objeto tem dois atributos fornecidos pelo usuário (em C ++, "membros"; em C #, "campos ou propriedades ou métodos"; em Java, "campos ou métodos"). Um deles é nomeado datae armazena o valor inteiro 42. O outro é nomeado methode armazena um objeto de função. (Existem vários outros atributos que o Python adiciona automaticamente.)

Porém, esses atributos ainda não fazem parte do objeto. Fundamentalmente, um objeto é apenas um pacote de mais nomes (os nomes dos atributos), até que você descubra coisas que não podem mais ser divididas. Assim, os valores podem ser compartilhados entre diferentes instâncias de uma classe, ou mesmo entre objetos de diferentes classes, se você definir isso deliberadamente.

Vamos criar uma instância:

x = Example()

Agora temos um objeto separado chamado x, que é uma instância de Example. Os datae methodnão são realmente parte do objeto, mas ainda podemos procurá-los por meio xde alguma mágica que o Python faz nos bastidores. Quando olhamos para cima method, em particular, obteremos um "método vinculado" (quando o chamamos, xé passado automaticamente como o selfparâmetro, o que não pode acontecer se olharmos Example.methoddiretamente).

O que acontece quando tentamos usar x.data?

Quando o examinamos, ele é pesquisado primeiro no objeto. Se não for encontrado no objeto, o Python procura na classe.

No entanto, quando atribuímos a x.data , Python criará um atributo no objeto. Ele vai não substituir o atributo de classe.

Isso nos permite fazer a inicialização do objeto . O Python chamará automaticamente o __init__método da classe em novas instâncias quando forem criadas, se houver. Neste método, podemos simplesmente atribuir a atributos para definir valores iniciais para esse atributo em cada objeto:

class Example:
    name = "Ignored"
    def __init__(self, name):
        self.name = name
    # rest as before

Agora devemos especificar a namequando criamos um Example, e cada instância tem a sua própria name. Python irá ignorar o atributo class Example.namesempre que procurarmos o .namede uma instância, porque o atributo da instância será encontrado primeiro.

Uma última advertência: modificação (mutação) e atribuição são coisas diferentes!

Em Python, as strings são imutáveis. Eles não podem ser modificados. Quando você faz:

a = 'hi '
b = a
a += 'mom'

Você não altera a string 'hi' original. Isso é impossível em Python. Em vez disso, você cria uma nova string 'hi mom'e adeixa de ser um nome para 'hi ', e passa a ser um nome para 'hi mom'. Fizemos bum nome para 'hi 'também, e depois de reaplicar o anome, bainda é um nome para 'hi ', porque 'hi 'ainda existe e não foi alterado.

Mas as listas podem ser alteradas:

a = [1, 2, 3]
b = a
a += [4]

Agora bé [1, 2, 3, 4] também, porque criamos bum nome para a mesma coisa que o anomeou e então mudamos essa coisa. Não criamos uma nova lista para anomear, porque Python simplesmente trata +=de listas de forma diferente.

Isso é importante para os objetos porque se você tivesse uma lista como um atributo de classe e usasse uma instância para modificar a lista, então a mudança seria "vista" em todas as outras instâncias. Isso ocorre porque (a) os dados são, na verdade, parte do objeto de classe e não qualquer objeto de instância; (b) porque você estava modificando a lista e não fazendo uma atribuição simples, você não criou um novo atributo de instância ocultando o atributo de classe.

Karl Knechtel
fonte
12
É por isso que os chamamos de 'identificadores' . :-)
Martijn Pieters
@Karl_Knechtel em seu exemplo de string no final, o que acontece com 'hi' se nunca o nomearmos como 'b'? Isso é um vazamento de memória?
infinito
2
@heretoinfinity: não; Python usa coleta de lixo para liberar objetos inacessíveis
John Clements
A declaração da variável é bem explicada neste vídeo: youtube.com/watch?v=ao2N37E-D9s .
Ishaq Khan
confira este vídeo para a resposta youtube.com/…
Thao N
23

Isso pode estar 6 anos atrasado, mas no Python 3.5 e superior, você declara um tipo de variável como este:

variable_name: type_name

ou isto:

variable_name # type: shinyType

Portanto, no seu caso (se você tiver uma CustomObjectclasse definida), você pode fazer:

customObj: CustomObject

Veja isso ou aquilo para mais informações.

O. Aroesti
fonte
22

Não há necessidade de declarar novas variáveis ​​em Python. Se estamos falando sobre variáveis ​​em funções ou módulos, nenhuma declaração é necessária. Apenas atribuir um valor a um nome de onde você precisa: mymagic = "Magic". Variáveis ​​em Python podem conter valores de qualquer tipo, e você não pode restringir isso.

Porém, sua pergunta pergunta especificamente sobre classes, objetos e variáveis ​​de instância. A maneira idiomática de criar variáveis ​​de instância está no __init__método e em nenhum outro lugar - embora você possa criar novas variáveis ​​de instância em outros métodos, ou mesmo em código não relacionado, é apenas uma má ideia. Isso tornará seu código difícil de raciocinar ou de manter.

Então, por exemplo:

class Thing(object):

    def __init__(self, magic):
        self.magic = magic

Fácil. Agora, as instâncias desta classe têm um magicatributo:

thingo = Thing("More magic")
# thingo.magic is now "More magic"

A criação de variáveis ​​no namespace da própria classe leva a um comportamento totalmente diferente. É funcionalmente diferente e você só deve fazer isso se tiver um motivo específico para isso. Por exemplo:

class Thing(object):

    magic = "Magic"

    def __init__(self):
        pass

Agora tente:

thingo = Thing()
Thing.magic = 1
# thingo.magic is now 1

Ou:

class Thing(object):

    magic = ["More", "magic"]

    def __init__(self):
        pass

thing1 = Thing()
thing2 = Thing()
thing1.magic.append("here")
# thing1.magic AND thing2.magic is now ["More", "magic", "here"]

Isso ocorre porque o namespace da própria classe é diferente do namespace dos objetos criados a partir dela. Vou deixar para você pesquisar um pouco mais.

A mensagem para casa é que o Python idiomático deve (a) inicializar atributos de objeto em seu __init__método e (b) documentar o comportamento de sua classe conforme necessário. Você não precisa se dar ao trabalho de obter uma documentação completa no nível da Esfinge para tudo o que você escreve, mas pelo menos alguns comentários sobre quaisquer detalhes que você ou outra pessoa possa precisar para pegar.

detly
fonte
12

Para fins de escopo, eu uso:

custom_object = None
vermelho quente
fonte
1

Variáveis ​​têm escopo, então sim, é apropriado ter variáveis ​​que são específicas para sua função. Você nem sempre precisa ser explícito sobre sua definição; normalmente você pode apenas usá-los. Apenas se você quiser fazer algo específico para o tipo da variável, como acrescentar para uma lista, você precisa defini-los antes de começar a usá-los. Exemplo típico disso.

list = []
for i in stuff:
  list.append(i)

A propósito, essa não é uma boa maneira de configurar a lista. Seria melhor dizer:

list = [i for i in stuff] # list comprehension

... mas estou divagando.

Sua outra pergunta. O objeto personalizado deve ser uma classe em si.

class CustomObject(): # always capitalize the class name...this is not syntax, just style.
  pass
customObj = CustomObject()
ChipJust
fonte