No Python ao declarar e inicializar uma variável, por inferência de tipos, ele sabe qual o tipo dessa variável.
x = 10
y = 10No exemplo acima, o Python vai reservar um espaço na mémoria e armazenar o valor inteiro 10 e vai atualizar a sua tabela de símbilos, que diz para qual objeto um símbilo aponta.
Nesse exemplo temos as variáveis x e y, onde as duas tem o mesmo valor. No caso do tipo inteiro, em Python ele é um tipo imutável, sendo assim, a implementação do Python vai fazer com que na tabela de símbolos, tanto x quanto y apontem para o mesmo endereço de memória.
Podemos ver qual o endereço de memória de uma variáviel com a função id(x):
print(id(x))
print(id(y))| Símbolo | Objeto (Endereço de Memória) |
| x | 0 |
| y | 0 |
Quando fazemos, por exemplo um incremento em uma variável, x += 1, o que vai acontecer é que o Python vai alocar um novo espaço de memória para armazenar o novo valor que agora é 11, sem alterar o anterior.
Dessa forma a tabela de símbolos é atualizada para:
| Símbolo | Objeto (Endereço de Memória) |
| x | 1 |
| y | 0 |
E se tentarmos ver os endereços de memória das variáveis, apenas x teve alteração;
print(id(x))
print(id(y))Dessa forma o Python consegue economizar memória, pois uma vez que variáveis recebem valores imutavéis que outras já estão utilizando, elas irão apontar para um mesmo endereço de memória.
Agora digamos que y = 11 e passou a apontar para esse novo endereço de memória, que também é o mesmo de x, o espaço alocado em mémoria para o valor 10 será retirado da mémoria pelo Garbage Collector do Python, pois não tem mais nenhuma variável apontando para ele.
Alguns objetos imutáveis no Python:
intstrcomplexboolbytesfrozensettuple
Alguns objetos mutáveis no Python:
listdictsetbytearray
Para definir uma classe em Python usamos a palavra reservada class:
class Evento:
passPoderiamos ...(Ellipsis) no lugar de pass, mas há diferença entre eles que você pode conferir aqui.
Dessa forma podemos criar instâncias dessa classe:
ev1 = Evento()
ev1.nome = "Classe 1"
ev2 = Evento()
ev2.nome = "Classe 2"Em Python, mesmo não tendo o atribuito nome na classe, podemos criá-los dinamicamente, de forma similar a um objeto Javascript.
E objetos que instanciamos a partir de classes que definimos, por padrão eles são objetos mutáveis. Dessa forma cada uma da instância que criamos, aponta para um endereço de memória diferente:
print(id(ev1))
print(id(ev2))Se criarmos uma função para alterar o nome, podemos fazer:
def alterar_nome(evento, novo_nome):
evento.nome = novo_nome
alterar_nome(ev1, 'Novo nome da classe 1')Mas quando trabalhos com classes, a ideia é que seus atributos e métodos estajam agrupados. Então podemos refatorar a nossa classe, onde a função de alterar_nome passará a ser um método da classe Evento:
Porém, temos algumas convenções a serem seguidas:
- O primeiro argumento de um método da classe sempre será uma referência da instância dessa classe, chamado
self. - Seguindo dos outros possíveis argumentos.
- Para utilizar esse método o Python já passa por padrão o parâmetro da instância da classe que chamou o metódo, dessa forma a chamada ficaria algo como
ev1.alterar_nome('Meu novo nome').
class Evento:
def alterar_nome(self, novo_nome):
self.nome = novo_nome
ev1 = Evento()
ev1.nome = "Classe 1"
ev1.alterar_nome('Meu novo nome')Dada classe, se tentar acessar o atributo nome:
class Evento:
def alterar_nome(self, novo_nome):
self.nome = novo_nome
ev1 = Evento()
print(ev1.nome)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'Evento' object has no attribute 'nome'Teremos um erro, pois o atributo não definido. No casos anteriores fizemos isso explicitamente. Para garantirmos que toda instância dessa classe tenha o atributo nome, devemos usar contrutores.
Um construtor é definido pelo método __int__, onde o primeiro argumento é o self, seguidos dos demais argumentos que deseja que o seu objeto tenha for instânciado:
class Evento:
def __init__(self, nome):
self.nome = nome
def alterar_nome(self, novo_nome):
self.nome = novo_nomeDessa forma se tentarmos criar uma nova instância dessa classe, temos um erro:
ev1 = Evento()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: __init__() missing 1 required positional argument: 'nome'Pois agora somos obrigados a informar esse parâmetro quando tentamos criar uma nova instância:
ev1 = Evento('Teste Nome')Quando falamos sobre métodos ou funções de uma classe em Python, temos três tipos:
-
Método de instância:
class Evento: def metodo_instancia(self): return ("Método de instância chamado", self) ev = Evento() ev.metodo_instancia() # Evento.metodo_instancia(ev)
Por padrão, os métodos definidos dentro de uma classe, serão métodos de instância e recebem o
selfcomo primeiro argumento. Recebe como argumento a instância o objeto dessa classe peloself. E é chamdo a partir de uma instância de um objeto desssa classe. -
Método de classe:
class Evento: @classmethod def metodo_classe(cls): return ("Método de classe chamado", cls) Evento.metodo_classe() # Evento.metodo_classe(Evento)
É criado com decorator
@classmethod, logo acima da definição do método. Assim o Python sabe que esse método recebe com primeiro argumento, uma referência da classe. Recebe como argumento a referencia da classe pelocls, abreviação paraclassno Python. E é chamdo a partir de uma instância de uma classe ou sem a necessidade de criar uma instância. Entras linguagens de programação, temos a opção de sobreescrever construtores, assim temos forma de instanciar um objeto, dada a situação de quais argumentos temos ou queremos iniciar. Mas no Python não temos essa possibilidade, para saber mais clique aqui. Por isso utilizamos o@classmethod, como opções de construtores que recebem outras opções de argumentos. -
Método estático:
class Evento: @staticmethod def metodo_estatico(): return ("Método estático chamado") Evento.metodo_classe() # Evento.metodo_estatico()
É criado com decorator
@staticmethod, logo acima da definição do método. Esse tipo de método, pode ou não, receber argumentos. E é chamdo a partir de uma instância de uma classe ou sem a necessidade de criar uma instância.