DEV Community

Cover image for Conceitos de POO em Python para Programadores C++98
Vinnícius Cedraz Ribeiro
Vinnícius Cedraz Ribeiro

Posted on

Conceitos de POO em Python para Programadores C++98

Aqui está uma demonstração abrangente dos conceitos de POO em Python para um programador C++98:

Definição de Classe e Criação de Objetos

Python

# Privado por convenção: _underscore_simples
# "Realmente privado": __underscore_duplo (name mangling)
# Público: sem underscore

from abc import abstractmethod
class Animal(ABC):
    # Em python, variáveis declaradas no escopo da classe e não dentro de um
    # método específico, são automaticamente compartilhadas por todas instâncias.
    species_count = 0 # além disso, elas podem ser inicializadas diretamente dentro da classe.

    # Construtor
    def __init__(self, name):
        # Variáveis de instância
        self.name = name       # público
        self._age = 0          # protegido por convenção
        self.__id = id(self)   # privado (mas você consegue acessar com name mangling)
        Animal.species_count += 1

    # Destrutor
    def __del__(self):
        Animal.species_count -= 1

    # Método regular
    @abstractmethod
    def make_sound(self):
        pass  # Equivalente a um método abstrato/virtual (deve ser implementado apenas nas classes filhas)

    # Método estático (não precisa da instância para ser utilizado, nem utiliza seus atributos)
    @staticmethod
    def get_kingdom():
        return "Animalia"

    # Método de classe (recebe a classe como primeiro argumento, pode acessar atributos da classe)
    @classmethod
    def get_species_count(cls):
        return cls.species_count

    # Decorador de propriedade (getter)
    @property
    def age(self):
        return self._age

    # Decorador de propriedade (setter)
    @age.setter
    def age(self, value):
        if value >= 0:
            self._age = value

    # Métodos especiais (sobrecarga de operadores)
    def __str__(self):                # Como toString() - para string legível
        return f"Animal named {self.name}"

    def __repr__(self):               # Para debugging
        return f"Animal(name='{self.name}')"

    def __eq__(self, other):          # Operador de comparação ==
        return isinstance(other, Animal) and self.name == other.name

    def __len__(self):                # Função len()
        return self._age

    def __getitem__(self, key):       # Operador de acesso []
        if key == 'name':
            return self.name
        raise KeyError(key)
Enter fullscreen mode Exit fullscreen mode

C++98

#include <iostream>
#include <string>
#include <sstream>

class Animal {
public:
    static int species_count;

    Animal(const std::string& name) : name(name), _age(0), __id(++id_counter) { // construtor
        ++species_count;
    }

    ~Animal() {    // destrutor
        --species_count;
    }

    virtual void make_sound() = 0; // Método não implementável na classe base (virtual/abstrato)

    static std::string get_kingdom() {  // Não existe distinção entre
    //  @classmethod e @staticmethod em cpp, apenas static methods.
        return "Animalia";
    }

    // static methods podem ser utilizados sem instanciar uma classe e têm
    // acesso às propriedades estáticas da classe:
    static int get_species_count() {
        return species_count;
    }

    // getter:
    int get_age() const {
        return _age;
    }

    // setter:
    void set_age(int age) {
        if (age >= 0) {
            _age = age;
        }
    }

    // Implementação dos métodos especiais que vimos em python:
    std::string to_string() const {
        return "Animal named " + name;
    }

    std::string repr() const {
        std::ostringstream oss;
        oss << "Animal(name='" << name << "', age=" << _age << ", id=" << __id << ")";
        return oss.str();
    }

    bool operator==(const Animal& other) const {
        return name == other.name;
    }

    // Sobrecarga do operador []
    std::string operator[](const std::string& key) const {
        if (key == "name") {
            return name;
        }
        throw std::out_of_range("Invalid key");
    }

    // Método isinstance
    template <typename T>
    bool isinstance() const {
        return dynamic_cast<const T*>(this) != nullptr;
    }

protected:
    std::string name;
    int _age;

private:
    int __id;
    static int id_counter;
};

// variáveis estáticas de classe são compartilhadas por todas as instâncias mas
// precisam ser inicializadas separadamente.
int Animal::species_count = 0;
int Animal::id_counter = 0;
Enter fullscreen mode Exit fullscreen mode

Herança

Python

class Dog(Animal):
    def __init__(self, name, breed):
        # Chama o construtor da classe pai
        super().__init__(name)
        self.breed = breed

    # Sobrescreve o método da classe pai
    def make_sound(self):
        return "Woof!"
Enter fullscreen mode Exit fullscreen mode

C++98

class Dog : public Animal {
public:
    Dog(const std::string& name, const std::string& breed) : Animal(name), breed(breed) {}

    void make_sound() override {
        std::cout << "Woof!" << std::endl;
    }

private:
    std::string breed;
};
Enter fullscreen mode Exit fullscreen mode

Herança Múltipla

Python

class Pet:
    def is_vaccinated(self):
        return True

class DomesticDog(Dog, Pet):
    pass
Enter fullscreen mode Exit fullscreen mode

C++98

class Pet {
public:
    bool is_vaccinated() const {
        return true;
    }
};

class DomesticDog : public Dog, public Pet {
public:
    DomesticDog(const std::string& name, const std::string& breed) : Dog(name, breed) {}
};
Enter fullscreen mode Exit fullscreen mode

Classe Abstrata

Python

from abc import ABC, abstractmethod

class Shape(ABC):
    @abstractmethod
    def area(self):
        pass
Enter fullscreen mode Exit fullscreen mode

C++98

class Shape {
public:
    virtual ~Shape() {}
    virtual double area() const = 0;
};
Enter fullscreen mode Exit fullscreen mode

Exemplo de Uso

Python

if __name__ == "__main__":
    # Cria objetos
    dog = Dog("Rex", "Golden Retriever")

    # Acessa atributos
    print(dog.name)          # Público
    print(dog._age)         # Protegido (ainda acessível)
    # print(dog.__id)       # Isso falhará 
    print(dog._Animal__id)  # Isso funciona (acessando attribute privado com name mangling)

    # Propriedades
    dog.age = 5             # Usa setter automaticamente
    print(dog.age)          # Usa getter automaticamente

    # Métodos estáticos e de classe
    print(Animal.get_kingdom())
    print(Animal.get_species_count())

    # Verifica herança
    print(isinstance(dog, Animal))  # True
    print(issubclass(Dog, Animal)) # True

    # Métodos especiais em ação
    print(str(dog))        # Usa __str__
    print(repr(dog))       # Usa __repr__
    print(len(dog))        # Usa __len__
    print(dog['name'])     # Usa __getitem__
Enter fullscreen mode Exit fullscreen mode

C++98

int main() {
    // Cria objetos
    Dog dog("Rex", "Golden Retriever");

    // Acessa atributos
    std::cout << dog.name << std::endl;          // Público
    std::cout << dog.get_age() << std::endl;     // Protegido (ainda acessível)
    // std::cout << dog.__id << std::endl;       // Isso falhará (privado)

    // Propriedades
    dog.set_age(5);             // Usa setter
    std::cout << dog.get_age() << std::endl;     // Usa getter

    // Métodos estáticos e de classe
    std::cout << Animal::get_kingdom() << std::endl;
    std::cout << Animal::get_species_count() << std::endl;

    // Equivalente aos "métodos especiais":

    // Verifica herança
    if (dog.isinstance<Animal>()) {
        std::cout << "dog é uma instância de Animal" << std::endl;
    }

    std::cout << dog.to_string() << std::endl;   // Usa to_string
    std::cout << dog.repr() << std::endl;        // Usa repr
    std::cout << dog["name"] << std::endl;       // Usa operador []
}
Enter fullscreen mode Exit fullscreen mode

Diferenças Chave entre Python e C++98

  1. Não há palavras-chave public/private/protected (use convenções de nomenclatura)
  2. A herança múltipla difere:
    • Python usa a Ordem de Resolução de Métodos (MRO) com linearização C3
    • Não há necessidade de herança virtual como em C++
    • super() segue automaticamente a MRO
    • A ordem das classes base importa em Python
    • Pode-se inspecionar a ordem de resolução com __mro__
  3. Todos os métodos são virtuais por padrão
  4. Não há distinção entre ponteiros/referências
  5. Não há necessidade de gerenciamento de memória (coletor de lixo)
  6. Tipagem dinâmica em vez de tipagem estática
  7. Decoradores de propriedade em vez de métodos getter/setter
  8. Métodos especiais usam o formato __name__ em vez da palavra-chave operator
  9. Sintaxe mais pythônica para sobrecarga de operadores (por exemplo, __eq__ vs operator==)

Use dir(object) para ver todos os atributos e métodos de um objeto, e help(object) para documentação.

Tópicos Especiais:

Problema da Herança Diamante

                              Animal

                           .    '    ,
                             _______
                        _  .`_|___|_`.  _
                    Pet     \ \   / /     WorkingAnimal
                             \ ' ' /
                              \ " /   
                               \./

                           DomesticDog
Enter fullscreen mode Exit fullscreen mode

Problemas com Herança Diamante em C++98

A herança diamante ocorre quando uma classe herda de duas classes que, por sua vez, herdam de uma classe base comum. Isso pode causar vários problemas:

  1. Ambiguidade: Métodos e atributos da classe base comum podem se tornar ambíguos.
  2. Duplicação de Dados: Cada classe derivada pode ter sua própria cópia dos membros da classe base comum, levando a duplicação de dados.

Exemplo de Herança Diamante em C++98

class Animal {
public:
    Animal() {
        std::cout << "Animal constructor" << std::endl;
    }
    virtual void make_sound() {
        std::cout << "Some generic animal sound" << std::endl;
    }
};

class Pet : public Animal {
public:
    Pet() : Animal() {
        std::cout << "Pet constructor" << std::endl;
    }
    void make_sound() override {
        std::cout << "Pet sound" << std::endl;
    }
};

class WorkingAnimal : public Animal {
public:
    WorkingAnimal() : Animal() {
        std::cout << "WorkingAnimal constructor" << std::endl;
    }
    void make_sound() override {
        std::cout << "Working animal sound" << std::endl;
    }
};

class DomesticDog : public Pet, public WorkingAnimal {
public:
    DomesticDog() : Animal(), Pet(), WorkingAnimal() {
        std::cout << "DomesticDog constructor" << std::endl;
    }
    void make_sound() override {
        Pet::make_sound();  // Ou WorkingAnimal::make_sound(), dependendo do comportamento desejado
    }
};

int main() {
    DomesticDog dog;
    dog.make_sound();
    return 0;
}
Enter fullscreen mode Exit fullscreen mode

Comportamento Esperado

Animal constructor
Pet constructor
WorkingAnimal constructor
DomesticDog constructor
Pet sound
Enter fullscreen mode Exit fullscreen mode

Neste exemplo, DomesticDog herda de Pet e WorkingAnimal, ambos herdando de Animal. Isso cria uma herança diamante. A herança virtual é usada para evitar duplicação de dados e ambiguidade.

Como Python Previne Automaticamente a Herança Diamante

Python usa a Ordem de Resolução de Métodos (MRO) com linearização C3 para resolver automaticamente os problemas de herança diamante. A MRO determina a ordem em que as classes são verificadas quando se procura por um método ou atributo.

Exemplo de Herança Diamante em Python

class Animal:
    def make_sound(self):
        print("Some generic animal sound")

class Pet(Animal):
    def make_sound(self):
        print("Pet sound")

class WorkingAnimal(Animal):
    def make_sound(self):
        print("Working animal sound")

class DomesticDog(Pet, WorkingAnimal):
    pass

dog = DomesticDog()
dog.make_sound()
Enter fullscreen mode Exit fullscreen mode

Comportamento Esperado

Pet sound
Enter fullscreen mode Exit fullscreen mode

Neste exemplo, Python resolve automaticamente a herança diamante usando a MRO. Você pode verificar a MRO usando o atributo __mro__:

print(DomesticDog.__mro__)
Enter fullscreen mode Exit fullscreen mode

A MRO em Python garante que DomesticDog herde corretamente de Pet e WorkingAnimal, e que Animal seja resolvida antes de object. Portanto, a ordem de declaração influencia a MRO, mas a linearização C3 garante que a hierarquia seja respeitada.

Explicação:

  1. Ordem de Declaração: A MRO começa pela classe mais derivada e segue a ordem em que as classes base são declaradas.
  2. Linearização C3: Garante que cada classe apareça antes de suas superclasses e que a ordem de herança seja mantida.

Estruturas de Dados: Stack, Queue e Map

Stack

Python

stack = [] # we could just use a list as a stack
stack.append(1)  # push
stack.append(2)
print(stack.pop())  # pop
Enter fullscreen mode Exit fullscreen mode

C++98

#include <stack> // we have to import the stack type
std::stack<int> stack;
stack.push(1);  // push
stack.push(2);
std::cout << stack.top() << std::endl;  // top
stack.pop();  // pop
Enter fullscreen mode Exit fullscreen mode

Queue

Python

from collections import deque
queue = deque()
queue.append(1)  # enqueue
queue.append(2)
print(queue.popleft())  # dequeue
Enter fullscreen mode Exit fullscreen mode

C++98

#include <queue>
std::queue<int> queue;
queue.push(1);  // enqueue
queue.push(2);
std::cout << queue.front() << std::endl;  // front
queue.pop();  // dequeue
Enter fullscreen mode Exit fullscreen mode

Map

Python

map = {} # this is automatically creating a map (which is called a dictionary in python)
map['key1'] = 'value1'
map['key2'] = 'value2'
print(map['key1'])
Enter fullscreen mode Exit fullscreen mode

C++98

#include <map>
std::map<std::string, std::string> map;
map["key1"] = "value1";
map["key2"] = "value2";
std::cout << map["key1"] << std::endl;
Enter fullscreen mode Exit fullscreen mode

Obrigado por acompanhar este guia sobre conceitos de POO em Python e C++98. Esperamos que tenha sido útil para sua jornada de aprendizado. Se você gostou do conteúdo, por favor, deixe seu comentário, curta e compartilhe com seus amigos e colegas. Se encontrou algum erro, deixe seu comentário e eu vou corrigir! Até a próxima!

Top comments (0)