Python Intermediário: Estruturas de Dados, Funções e OOP
Explique a diferença entre uma lista (list) e uma tupla (tuple) em Python.
Resposta:
Listas são mutáveis, o que significa que seus elementos podem ser alterados após a criação, e são definidas usando colchetes []. Tuplas são imutáveis, o que significa que seus elementos não podem ser alterados, e são definidas usando parênteses (). Tuplas são geralmente mais rápidas e podem ser usadas como chaves de dicionário.
O que é uma compreensão de dicionário (dictionary comprehension)? Forneça um exemplo.
Resposta:
Uma compreensão de dicionário é uma forma concisa de criar dicionários. Ela consiste em uma expressão seguida por uma cláusula for, e então zero ou mais cláusulas for ou if. Por exemplo: squares = {x: x*x for x in range(5)} cria {0: 0, 1: 1, 2: 4, 3: 9, 4: 16}.
Qual é o propósito de *args e **kwargs em definições de função?
Resposta:
*args permite que uma função aceite um número arbitrário de argumentos posicionais, que são coletados em uma tupla. **kwargs permite que uma função aceite um número arbitrário de argumentos nomeados (keyword arguments), que são coletados em um dicionário. Eles permitem assinaturas de função flexíveis.
Explique o conceito de decorador (decorator) em Python.
Resposta:
Um decorador é um padrão de design que permite modificar ou estender a funcionalidade de uma função ou método sem alterar explicitamente seu código-fonte. É essencialmente uma função que recebe outra função como argumento, adiciona alguma funcionalidade e retorna uma nova função. Eles são comumente usados para logging, medição de tempo (timing) ou controle de acesso.
Qual é a diferença entre os métodos __init__ e __new__ em classes Python?
Resposta:
__new__ é um método estático responsável por criar e retornar uma nova instância da classe antes que __init__ seja chamado. __init__ é um método de instância que inicializa o objeto recém-criado. __new__ raramente é sobrescrito, a menos que você precise controlar a própria criação do objeto, como em singletons.
Descreva a sobrescrita de métodos (method overriding) e o sobrecarregamento de métodos (method overloading) em Python.
Resposta:
A sobrescrita de métodos ocorre quando uma subclasse fornece uma implementação específica para um método que já está definido em sua superclasse. Python não suporta sobrecarregamento de métodos tradicional (múltiplos métodos com o mesmo nome, mas parâmetros diferentes) diretamente; em vez disso, você pode usar argumentos padrão ou *args/**kwargs para obter flexibilidade semelhante.
O que é um gerador (generator) em Python e por que você o usaria?
Resposta:
Um gerador é uma função que retorna um iterador que produz uma sequência de resultados um de cada vez usando a palavra-chave yield, em vez de retornar um único valor. Eles são eficientes em termos de memória porque não armazenam a sequência inteira na memória, tornando-os ideais para grandes conjuntos de dados ou sequências infinitas.
Explique o Global Interpreter Lock (GIL) em Python.
Resposta:
O GIL é um mutex que protege o acesso a objetos Python, impedindo que múltiplos threads nativos executem bytecode Python simultaneamente. Isso significa que, mesmo em processadores multi-core, apenas um thread pode executar bytecode Python em qualquer momento. Ele simplifica o gerenciamento de memória, mas pode limitar a execução paralela real para tarefas vinculadas à CPU.
Qual é o propósito de super() em Python?
Resposta:
super() é usado para chamar um método de uma classe pai ou irmã. Ele permite acessar métodos herdados que foram sobrescritos em uma subclasse, garantindo a ordem correta de resolução de métodos (MRO - Method Resolution Order) em hierarquias de herança complexas. É comumente usado em métodos __init__ de subclasses.
Como você lida com exceções (exceptions) em Python? Forneça um exemplo básico.
Resposta:
Exceções são tratadas usando blocos try, except, else e finally. O bloco try contém código que pode gerar uma exceção. except captura exceções específicas. else é executado se nenhuma exceção ocorrer, e finally é sempre executado, independentemente de ter ocorrido ou não uma exceção. Exemplo: try: 1/0 except ZeroDivisionError: print('Cannot divide by zero').
Qual é a diferença entre cópia rasa (shallow copy) e cópia profunda (deep copy)?
Resposta:
Uma cópia rasa cria um novo objeto composto, mas depois insere referências aos objetos encontrados no original. Se o original contiver objetos mutáveis, as alterações nesses objetos serão refletidas na cópia rasa. Uma cópia profunda cria um novo objeto composto e, em seguida, insere recursivamente cópias dos objetos encontrados no original, garantindo independência completa.
Explique o conceito de gerenciadores de contexto (context managers) e a instrução with.
Resposta:
Gerenciadores de contexto fornecem uma maneira limpa de gerenciar recursos, garantindo que as operações de configuração e desativação sejam tratadas corretamente, mesmo que ocorram erros. A instrução with é usada para gerenciar automaticamente a aquisição e liberação de recursos. Usos comuns incluem manipulação de arquivos, conexões de banco de dados e locks, garantindo que os recursos sejam fechados corretamente.