Qual a forma mais correta de escrever este código?

Esta forma abaixo?

Ou esta forma abaixo?

Obrigado.

1 curtida

Acredito que vc não deve criar uma classe em python sem o init, ele é o contrutor da sua classe, então o segundo caso

1 curtida

Você pode, e não há problema algum em criar uma classe sem a definição de __init__ dependendo do objetivo. O que realmente @Passarinho_Azul deve se perguntar é se ele quer que variable1 e dictionary1 sejam atributos de classe (primeiro exemplo) ou atributos de instância (segundo exemplo).

Atributos de classe são compartilhados por todos os objetos dessa classe, atributos de instância, cada objeto tem o seu próprio. Outra questão é que atributos de classe não são como atributos estáticos em outras linguagens como C++, instâncias tem acesso a atributos de classe, logo se eu fizer isso:

foo = MyClass()
bar = MyClass()
print (bar.variable1)
foo.variable1 = [1, 2, 3]
print (foo.variable1)
print (bar.variable1)
print (MyClass.variable1)
MyClass.variable1 = [3, 2, 1]
print (foo.variable1)
print (bar.variable1)
print (MyClass.variable1)

Vou obter no primeiro exemplo:

[]
[1,2,3]
[]
[]
[1,2,3]
[3,2,1]
[3,2,1]

E no segundo:

[]
[1,2,3]
[]
AttributeError: ...
[1,2,3]
[]
[3,2,1]

Quanto aos exemplos, considerando todos os métodos, como o objetivo previsto por ele parece ser atributos de instância, o correto é inicializar os mesmos no __init__ para não gerar inconsistências afinal como dá pra ver isso pode gerar muita confusão.

6 curtidas

Olá @Passarinho_Azul

Excelente resposta @romulopb, só vou complementar deixando a documentação do Python em português que aborda essa questão:

Segue o exemplo que está na documentação:

class Cachorro:
    # Esse valor será compartilhado com todos que instanciarem essa classe.
    truques = []

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

    def adicionar_truque(self, truque):
        self.truques.append(truque)


if __name__ == '__main__':
    cachorro1 = Cachorro(nome='Cachorro1')
    cachorro1.adicionar_truque(truque='rolar')

    cachorro2 = Cachorro(nome='Cachorro2')
    cachorro2.adicionar_truque(truque='fingir de morto')

    print(cachorro1.nome)
    print(cachorro1.truques)
    print('---')
    print(cachorro2.nome)
    print(cachorro2.truques)

Retorno do terminal:

Cachorro1
['rolar', 'fingir de morto']
---
Cachorro2
['rolar', 'fingir de morto']

No código acima temos um comportamento não desejado onde todos os cachorros sabem o mesmo truque.

OBS: É um comportamento indesejado para esse caso, em outras situações você pode querer que as instancias compartilhem valores entre si.

Já para o código abaixo cada cachorro sabe apenas o truque que foi passado para ele:

class Cachorro:

    def __init__(self, nome):
        self.nome = nome
        # Agora cada cachorro terá o seu truque.
        self.truques = []

    def adicionar_truque(self, truque):
        self.truques.append(truque)


if __name__ == '__main__':
    cachorro1 = Cachorro(nome='Cachorro1')
    cachorro1.adicionar_truque(truque='rolar')

    cachorro2 = Cachorro(nome='Cachorro2')
    cachorro2.adicionar_truque(truque='fingir de morto')

    print(cachorro1.nome)
    print(cachorro1.truques)
    print('---')
    print(cachorro2.nome)
    print(cachorro2.truques)

Retorno do terminal:

Cachorro1
['rolar']
---
Cachorro2
['fingir de morto']
4 curtidas

Eu sou um novato em OOP e repostas cuidadosas como a de vocês são muito valiosas para o meu crescimento.

Obrigado @romulopb , @natorsc e @doretox

1 curtida

O exemplo do @natorsc é um bom exemplo de efeito indesejado.

Falando de usos mais genéricos, atributos e métodos de classeou estáticos em algumas linguagens tecnicamente são considerados OPP impuro, pois violam alguns princípios do paradigma, como polimorfismo em tempo de execução e comunicação entre objetos isolados.

Abuso de atributos estáticos costuma virar um gargalo de performance em programação concorrente, e costuma ser uma fonte de efeitos colaterais já que em ambos os casos vira um ponto em comum entre diversos componentes no seu código, onde estes precisam esperar para manipular esse atributo ou podem ser afetados pela manipulação desse atributo.

No geral, o melhor é dar preferência para atributos/métodos de instância pois isso torna seu código, como já discutimos em outra pergunta sua, mais encapsulado, fracamente acoplado.

Só cuide para não “demonizar” atributos/métodos de classe, não existem panaceias em TI e praticamente tudo em algum momento pode virar um anti-pattern e isso inclui OOP, nem sempre OOP ou OOP puro é a melhor solução para um problema.

2 curtidas

Estou deixando esse post como referência futura para mim. Alguns conceitos ainda ficam meio vagos para mim, mas estou mesmo assim me atrevendo a escrever código e ir aprendendo os conceitos com o tempo. Sempre que escrevo algo eu tento dar uma estudada nos conceitos para fundamentar.

Obrigado pelos fundamentos.

Bons estudos, se você quiser dar uma olhada em usos benignos de métodos de classe de uma olhada no factory pattern é um pattern útil para controlar a criação de instâncias de uma classe. Um uso benigno de atributos estáticos é em Enums.

Outra questão é que volta e meia algumas abstrações não são bem representadas por orientação a objetos, esse é um dos motivos da programação funcional estar emergindo com força na maioria das linguagens, uma área de desenvolvimento que se beneficia de não exagerar no purismo em OOP é o setor financeiro.

1 curtida