|
| 1 | +Title: Orientação a objetos de outra forma: Métodos estáticos e de classes |
| 2 | +Slug: oo-de-outra-forma-2 |
| 3 | +Date: 2021-04-19 17:00 |
| 4 | +Category: Python |
| 5 | +Tags: python, orientação a objetos |
| 6 | +Author: Eduardo Klosowski |
| 7 | + |
| 8 | +Github: eduardoklosowski |
| 9 | +Twitter: eduklosowski |
| 10 | +Site: https://dev.to/eduardoklosowski |
| 11 | +About_author: Programador, formado em redes de computadores e estuda DevOps |
| 12 | + |
| 13 | +Na [postagem anterior](https://dev.to/acaverna/orientacao-a-objetos-de-outra-forma-classes-e-objetos-3mfd) foi apresentado o `self`, nessa postagem será discutido mais a respeito desse argumento, considerando opções para ele e suas aplicações. |
| 14 | + |
| 15 | +## Métodos estáticos |
| 16 | + |
| 17 | +Nem todas as funções de uma classe precisam receber uma referência de um objeto para lê-lo ou alterá-lo, muitas vezes uma função pode fazer o seu papel apenas com os dados passados como argumento, por exemplo, receber um nome e validar se ele possui pelo menos três caracteres sem espaço. Dessa forma, essa função poderia ser colocada fora do escopo da classe, porém para facilitar sua chamada, e possíveis alterações (que será discutido em outra postagem), é possível colocar essa função dentro da classe e informar que ela não receberá o argumento `self` com o decorador `@staticmethod`: |
| 18 | + |
| 19 | +```python |
| 20 | +class Pessoa: |
| 21 | + ... # Demais funções |
| 22 | + |
| 23 | + @staticmethod |
| 24 | + def valida_nome(nome): |
| 25 | + return len(nome) >= 3 and ' ' not in nome |
| 26 | +``` |
| 27 | + |
| 28 | +Dessa forma, essa função pode ser chamada diretamente de um objeto pessoa, ou até mesmo diretamente da classe, sem precisar criar um objeto primeiro: |
| 29 | + |
| 30 | +```python |
| 31 | +# Chamando diretamente da classe |
| 32 | +print(Pessoa.valida_nome('João')) |
| 33 | + |
| 34 | +# Chamando através de um objeto do tipo Pessoa |
| 35 | +p1 = Pessoa('João', 'da Silva', 20) |
| 36 | +print(p1.valida_nome(p1.nome)) |
| 37 | +``` |
| 38 | + |
| 39 | +E essa função também pode ser utilizada dendro de outras funções, como validar o nome na criação de uma pessoa, de forma que caso o nome informado seja válido, será criado um objeto do tipo Pessoa, e caso o nome seja inválido, será lançado uma exceção: |
| 40 | + |
| 41 | +```python |
| 42 | +class Pessoa: |
| 43 | + def __init__(self, nome, sobrenome, idade): |
| 44 | + if not self.valida_nome(nome): |
| 45 | + raise ValueError('Nome inválido') |
| 46 | + |
| 47 | + self.nome = nome |
| 48 | + self.sobrenome = sobrenome |
| 49 | + self.idade = idade |
| 50 | + |
| 51 | + ... # Demais funções |
| 52 | + |
| 53 | + @staticmethod |
| 54 | + def valida_nome(nome): |
| 55 | + return len(nome) >= 3 and ' ' not in nome |
| 56 | + |
| 57 | + |
| 58 | +p1 = Pessoa('João', 'da Silva', 20) # Cria objeto |
| 59 | +p2 = Pessoa('a', 'da Silva', 20) # Lança ValueError: Nome inválido |
| 60 | +``` |
| 61 | + |
| 62 | +## Métodos da classe |
| 63 | + |
| 64 | +Entretanto algumas funções podem precisar de um meio termo, necessitar acessar o contexto da classe, porém sem necessitar de um objeto. Isso é feito através do decorador `@classmethod`, onde a função decorada com ele, em vez de receber um objeto como primeiro argumento, recebe a própria classe. |
| 65 | + |
| 66 | +Para demonstrar essa funcionalidade será implementado um *id* auto incremental para os objetos da classe `Pessoa`: |
| 67 | + |
| 68 | +```python |
| 69 | +class Pessoa: |
| 70 | + total_de_pessoas = 0 |
| 71 | + |
| 72 | + @classmethod |
| 73 | + def novo_id(cls): |
| 74 | + cls.total_de_pessoas += 1 |
| 75 | + return cls.total_de_pessoas |
| 76 | + |
| 77 | + def __init__(self, nome, sobrenome, idade): |
| 78 | + self.id = self.novo_id() |
| 79 | + self.nome = nome |
| 80 | + self.sobrenome = sobrenome |
| 81 | + self.idade = idade |
| 82 | + |
| 83 | +p1 = Pessoa('João', 'da Silva', 20) |
| 84 | +print(p1.id) # Imprime 1 |
| 85 | +p2 = Pessoa('Maria', 'dos Santos', 18) |
| 86 | +print(p2.id) # Imprime 2 |
| 87 | +print(Pessoa.total_de_pessoas) # Imprime 2 |
| 88 | +print(p1.total_de_pessoas) # Imprime 2 |
| 89 | +print(p2.total_de_pessoas) # Imprime 2 |
| 90 | +``` |
| 91 | + |
| 92 | +Nesse código é criado uma variável `total_de_pessoas` dentro do escopo da classe `Pessoas`, e que é compartilhado tanto pela classe, como pelos objetos dessa classe, diferente de declará-la com `self.` dentro do `__init__`, onde esse valor pertenceria apenas ao objeto, e não é compartilhado com os demais objetos. Declarar variáveis dentro do contexto da classe é similar ao se declarar variáveis com `static` em outras linguagens, assim como o `@classmethod` é semelhante a declaração de funções com `static`. |
| 93 | + |
| 94 | +As funções declaradas com `@classmethod` também podem ser chamadas sem a necessidade de se criar um objeto, como `Pessoa.novo_id()`, embora que para essa função específica isso não faça muito sentido, ou receber outros argumentos, tudo depende do que essa função fará. |
| 95 | + |
| 96 | +## Considerações |
| 97 | + |
| 98 | +Embora possa parecer confuso identificar a diferença de uma função de um objeto (função sem decorador), função de uma classe (com decorador `@classmethod`) e função sem acesso a nenhum outro contexto (com decorador `@staticmethod`), essa diferença fica mais clara ao se analisar o primeiro argumento recebido por cada tipo de função. Podendo ser a referência a um objeto (`self`) e assim necessitando que um objeto seja criado anteriormente, ser uma classe (`cls`) e não necessitando receber um objeto, ou simplesmente não recebendo nenhum argumento especial, apenas os demais argumentos necessários para a função. Sendo diferenciados pelo uso dos decoradores. |
| 99 | + |
| 100 | +Na orientação a objetos implementada pelo Python, algumas coisas podem ficar confusas quando se mistura com nomenclaturas de outras linguagens que possuem implementações diferentes. A linguagem Java, por exemplo, utiliza a palavra-chave `static` para definir os atributos e métodos de classe, enquanto no Python um método estático é aquele que não acessa nem um objeto, nem uma classe, devendo ser utilizado o escopo da classe e o decorador `@classmethod` para se criar atributos e métodos da classe. |
| 101 | + |
| 102 | +--- |
| 103 | + |
| 104 | +Esse artigo foi publicado originalmente no [meu blog](https://eduardoklosowski.github.io/blog/), passe por lá, ou siga-me no [DEV](https://dev.to/eduardoklosowski) para ver mais artigos que eu escrevi. |
0 commit comments