Introdução à programação orientada a objetos em Python

Aprenda a escrever suas próprias classes em Python. Defina uma classe com um construtor e atributos, crie objetos a partir dela e dê a eles métodos que usam e alteram seus dados com segurança. Faça objetos exibirem texto claro e serem comparados com ==, ordenados e somados com + por meio de métodos especiais, crie programas em que objetos armazenam e chamam outros objetos e avalie quando uma classe torna um programa mais claro do que funções e dicionários.

BásicoAula 10 de 10 do curso Curso de Introdução à programação com Python

O que você vai aprender

  1. Veja que todo valor em Python é um objeto com seus próprios dados e métodos, depois escreva sua própria classe e crie objetos a partir dela.
    • Reconheça os objetos que você já usa
      Descreva strings, listas e dicionários como objetos que têm um tipo, armazenam dados e oferecem métodos que você chama com a notação com ponto.
    • Defina uma classe e crie instâncias
      Escreva uma instrução de classe, crie vários objetos distintos a partir dela e defina e leia seus atributos com a notação com ponto.
  2. Use um construtor para dar a cada novo objeto os atributos de que ele precisa, escolha entre atributos de instância e de classe e impeça a criação de objetos inválidos.
    • Escreva um construtor com __init__ e self
      Defina __init__ para que cada novo objeto comece com seus próprios atributos, obtidos dos argumentos ou definidos com valores iniciais.
    • Escolha entre atributos de instância e atributos de classe
      Decida se um valor pertence a cada objeto ou à classe como um todo e preveja como o Python encontra e altera cada tipo.
    • Mantenha objetos válidos desde o início
      Verifique os argumentos que um construtor recebe, lance uma exceção clara para valores inválidos e mantenha as regras que dão sentido a um objeto.
  3. Escreva métodos que usam e alteram os dados de um objeto e proteja esses dados contra alterações que tornariam o objeto inválido.
    • Defina e chame métodos de instância
      Escreva métodos que trabalham com self, chame-os em objetos e retorne resultados em vez de imprimi-los.
    • Altere o estado de um objeto com métodos
      Escreva métodos que atualizam atributos mantendo o objeto válido e preveja quando uma alteração fica visível por meio de outros nomes para o mesmo objeto.
    • Proteja atributos com convenções de nomenclatura e propriedades
      Marque atributos como internos com um sublinhado e use propriedades para verificar novos valores ou calcular valores que permanecem atualizados.
  4. Escreva métodos especiais que permitam aos seus objetos mostrar texto legível, ser comparados com == e < e funcionar com operadores e funções embutidas.
    • Dê aos objetos representações em string legíveis
      Escreva __str__ e __repr__ para que os objetos mostrem texto útil quando forem impressos, inspecionados ou armazenados em uma lista.
    • Compare e combine objetos com operadores
      Defina __eq__, __lt__, __add__ e __len__ para que seus objetos funcionem com ==, ordenação, + e len(), e decida quando um operador é mais claro do que um método nomeado.
  5. Combine objetos para que cada um cuide de seus próprios dados, rastreie programas em que objetos chamam uns aos outros e decida quando uma classe torna um programa mais claro.
    • Crie objetos que contenham outros objetos
      Armazene objetos como atributos ou em listas dentro de outro objeto e escreva métodos que trabalhem com toda a coleção.
    • Faça objetos interagirem por meio de métodos
      Passe objetos para métodos e funções, retorne novos objetos e rastreie um programa curto em que vários objetos trabalham juntos.
    • Decida quando uma classe torna um programa mais claro
      Avalie quando reunir dados e comportamento em uma classe ajuda, planeje uma classe pequena a partir de uma descrição e reescreva código baseado em dicionários como uma classe.

Guia de estudo de Programação orientada a objetos em Python

5 capítulos

1

Entenda objetos e defina sua primeira classe

Objetos já aparecem em todos os programas Python que você escreve: uma string tem métodos, uma lista altera seu conteúdo e um dicionário responde a consultas por chave. Interpretar essas operações familiares como comportamento de objetos oferece um ponto de partida para suas próprias classes. Aprenda como tipos, identidade e referências compartilhadas afetam um programa, depois defina uma classe pequena e dê a instâncias distintas seus próprios atributos.

1.1 Reconheça os objetos que você já usa

Você já usou objetos sempre que trabalhou com um número, uma string, uma lista ou um dicionário. Um objeto tem um tipo, um valor ou estado e operações que aceita. Até funções e None são objetos. Escrever sua própria classe amplia esse sistema familiar.

Encontre o tipo e os métodos de um objeto

type("hello") retorna <class 'str'>; type([1, 2]) retorna <class 'list'>. As palavras tipo e classe descrevem o mesmo papel aqui. isinstance(3.5, float) é True, enquanto isinstance("3.5", float) é False: o segundo valor é texto, embora pareça numérico.

Um método é uma função fornecida por um tipo. Em "ada lovelace".title(), a string antes do ponto é o objeto, title é o método e os parênteses o chamam. O resultado é "Ada Lovelace". dir(value) lista os nomes oferecidos por um objeto; nomes como __len__ são métodos especiais que ligam objetos a operações do Python como len(value).

Acompanhe o resultado e o original

Uma chamada de método pode retornar um resultado, alterar um objeto ou fazer ambos. Verifique o comportamento do método antes de decidir o que atribuir.

word = "level"
print(word.count("l"))  # 2
print(word.upper())     # LEVEL
print(word)             # level

nums = [3, 1, 2]
result = nums.sort()
print(nums)             # [1, 2, 3]
print(result)           # None

Strings são imutáveis: seu conteúdo não pode mudar. upper() retorna outra string, então word mantém seu valor original. Para manter o resultado em maiúsculas sob esse nome, escreva word = word.upper().

Listas são mutáveis. sort() altera a própria lista e retorna None. Escrever nums = nums.sort() perde a referência à lista armazenada em nums e atribui None. Use nums.sort() em uma linha própria ou nums = sorted(nums) para obter uma nova lista ordenada. Para um dicionário, {"a": 1}.get("b", 0) retorna 0 porque a chave solicitada está ausente.

Parênteses também importam. word.upper é um objeto de método; word.upper() o executa. O mesmo comportamento pode ser oferecido por muitos objetos: "cat".upper() e "dog".upper() usam um mesmo tipo de método com dados de string diferentes.

Distinga igualdade de identidade

Igualdade verifica se os valores correspondem. Identidade verifica se dois nomes se referem a um mesmo objeto.

a = [1, 2]
b = [1, 2]
c = a
print(a == b)  # True
print(a is b)  # False
print(c is a)  # True
c.append(3)
print(a)       # [1, 2, 3]
print(b)       # [1, 2]

a e b começam com conteúdos iguais, mas são listas separadas. c = a cria uma referência alternativa, outro nome para a mesma lista. Adicionar um item por meio de c altera o objeto ao qual a também se refere. Isso não altera a lista separada armazenada em b. Ao rastrear um programa, desenhe uma caixa por objeto e uma seta de cada nome até sua caixa.

Principais conclusões

  • Todo valor em Python é um objeto com tipo, estado e operações que aceita.
  • Chame um método com parênteses; uma referência ao método, sozinha, não o executa.
  • Verifique se um método retorna um novo valor ou altera o objeto existente.
  • == compara valores; is compara identidade.
  • Uma atribuição pode fazer dois nomes se referirem ao mesmo objeto mutável.

1.2 Defina uma classe e crie instâncias

Uma classe define um tipo que você pode usar em seu próprio programa. Cada objeto criado a partir dela é uma instância. Uma classe descreve o tipo de coisa; as instâncias armazenam os dados de coisas específicas. Um formulário em branco e suas cópias preenchidas são uma comparação útil.

Escreva uma classe e chame-a

Uma instrução de classe tem a palavra reservada class, um nome, dois-pontos e um corpo indentado. pass fornece um corpo vazio enquanto você experimenta a estrutura.

class Book:
    pass

b1 = Book()
b2 = Book()
print(b1 is b2)           # False
print(isinstance(b1, Book))  # True

Cada chamada Book() cria uma instância separada. Em um script, type(b1) mostra <class '__main__.Book'>. A instrução de classe deve ser executada antes que o código tente usar Book. Você também pode definir uma classe em um módulo e importá-la com from library import Book, depois criar instâncias no programa chamador.

Siga as convenções de nomenclatura do Python: Book, BankAccount e ShoppingCart usam CapWords, com cada palavra iniciada por maiúscula. Variáveis e métodos usam palavras minúsculas separadas por sublinhados, como book_count e add_item. Use quatro espaços para cada nível de indentação.

Armazene dados em cada instância

Um atributo é um valor nomeado acessado com um ponto. Atribuir um atributo o armazena; ler o mesmo nome recupera seu valor.

b1.title = "Dune"
b2.title = "Emma"
b1.pages = 412
print(b1.title)  # Dune
print(b2.title)  # Emma
print(b1.pages)  # 412

Os dados pertencem à instância selecionada. Dar a b1 um atributo pages não dá um a b2. Ler b2.pages agora provoca AttributeError, pois esse objeto não tem um atributo chamado pages.

Atribuições podem criar novos nomes sem verificar sua intenção. Depois de b1.titel = "X", o b1.title original ainda armazena "Dune"; o objeto ganhou um segundo atributo com um nome escrito incorretamente. Se um valor parecer não mudar, compare a escrita no ponto em que ele foi atribuído e no ponto em que foi lido.

Mantenha a classe e suas instâncias distintas
ExpressãoSignificado
BookO objeto de classe, que define o tipo
Book()Uma chamada que cria uma instância
b1.titleO título armazenado neste livro
Book.titleUm atributo solicitado na própria classe

b = Book dá outro nome à classe. Isso não cria um livro. Se os títulos foram definidos apenas nas instâncias, pedir Book.title provoca AttributeError; não há um único título compartilhado pelo tipo.

Dê a todo objeto um início confiável

Definir atributos de fora é possível, mas deixa a configuração espalhada pelo programa. Um livro pode receber um título e uma contagem de páginas, enquanto outro recebe apenas um título. O código posterior não pode pressupor com segurança que ambos têm pages.

Um método de configuração chamado __init__ coloca as atribuições iniciais dentro da classe. Cada chamada de criação segue então o mesmo processo de configuração. A classe pode exigir um título e criar outros atributos com valores iniciais adequados, para que o código chamador não precise lembrar uma sequência separada de atribuições.

Principais conclusões

  • Uma classe define um tipo; uma instância é um objeto criado a partir dela.
  • Use ClassName() para criar uma instância e a notação com ponto para acessar seus atributos.
  • Cada instância pode armazenar dados diferentes sob os mesmos nomes de atributos.
  • Um atributo ausente provoca AttributeError; uma atribuição com nome escrito incorretamente pode criar um novo atributo.
2

Configure objetos com construtores e atributos

Um objeto precisa de um estado inicial confiável antes que seus métodos possam realizar tarefas úteis. A inicialização conecta os argumentos de uma chamada de classe aos atributos armazenados na nova instância. Escolha quais valores pertencem a um objeto e quais pertencem à classe, depois valide as entradas antes de aceitá-las. Rastrear buscas e falhas de configuração ajudará você a identificar compartilhamento acidental e dados ausentes.

2.1 Escreva um construtor com __init__ e self

Toda conta recém-criada precisa de um titular e de um saldo inicial. Colocar essa configuração em __init__ dá a cada objeto os atributos que os métodos posteriores esperam. O Python cria a instância primeiro e depois chama __init__ para inicializá-la.

Conecte argumentos a atributos
class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance
        self.transactions = []

acct = BankAccount("Maya", 50)
other = BankAccount("Leo")

Na primeira chamada, o Python passa a nova conta como self, "Maya" como owner e 50 como balance. As atribuições fazem acct.owner ser igual a "Maya" e acct.balance ser igual a 50. A segunda chamada usa o saldo padrão, então other.balance é 0.

self é o parâmetro que recebe o objeto atual. É uma convenção, não uma palavra reservada do Python. Use esse nome para que os leitores reconheçam o objeto imediatamente. O código chamador o deixa de fora: BankAccount("Maya", 50) fornece dois argumentos, não três.

Mantenha nomes locais e dados armazenados separados

Em self.owner = owner, o nome à direita é um parâmetro local. O nome à esquerda é um atributo armazenado no objeto. Quando a inicialização termina, outros métodos ainda podem usar self.owner.

Escrever balance = balance apenas reatribui o nome local. Isso não cria um atributo, então ler acct.balance depois falha. Coloque self. antes de cada atributo que deve permanecer como parte do objeto.

Planeje a inicialização listando o que cada instância precisa. Valores obrigatórios tornam-se parâmetros após self; valores opcionais podem ter padrões. Valores que sempre começam da mesma forma podem ser atribuídos no corpo, como self.visits = 0. Uma lista criada por self.transactions = [] é criada novamente em cada chamada.

Evite uma lista padrão compartilhada

Um argumento padrão é avaliado quando o Python define o método. Com def __init__(self, items=[]), chamadas que omitem items recebem a mesma lista. Se o inicializador armazenar essa lista diretamente, alterar os itens de um objeto altera os itens vistos por outro.

Use None como padrão e crie uma nova lista durante cada chamada:

class Cart:
    def __init__(self, items=None):
        self.items = [] if items is None else list(items)

a = Cart()
b = Cart()
a.items.append("pen")
print(b.items)  # []

list(items) também cria uma nova lista externa quando o código chamador fornece uma. Os objetos dentro dessa lista ainda são referências compartilhadas; criar a lista não recria cada item.

Leia erros de configuração

Em BankAccount() falta o argumento obrigatório owner, o que provoca TypeError. Compare a chamada com os parâmetros após self.

A grafia __init__ tem dois sublinhados de cada lado. _init_ é um nome de método comum e não realiza essa configuração automaticamente. __init__ deve retornar None, então normalmente deixe de fora uma instrução de retorno. Retornar um saldo provoca TypeError; a própria chamada da classe fornece a nova instância ao código chamador.

Principais conclusões

  • __init__ inicializa a instância depois que o Python a cria.
  • O Python fornece self; o código chamador fornece os argumentos restantes.
  • self.name = name armazena um parâmetro como atributo de instância.
  • Crie listas individuais das instâncias durante a inicialização; use None em vez de um valor padrão mutável.
  • __init__ deve retornar None.

2.2 Escolha entre atributos de instância e atributos de classe

Alguns valores descrevem um objeto; outros descrevem o tipo como um todo. O tamanho de uma pizza pertence àquela pizza. Um limite de ingredientes ou uma contagem de pizzas criadas pode pertencer à classe. O local de uma atribuição determina que tipo de atributo ela cria.

Coloque o valor no lugar certo
class Pizza:
    MAX_TOPPINGS = 5
    count = 0

    def __init__(self, size):
        self.size = size
        self.toppings = []
        Pizza.count += 1

p1 = Pizza("large")
p2 = Pizza("small")

size e toppings são atributos de instância, definidos em self. Cada pizza tem seus próprios valores. MAX_TOPPINGS e count são atributos de classe, atribuídos no corpo da classe fora de um método. Letras maiúsculas indicam um valor destinado a ser uma constante; elas não impedem a reatribuição.

Após essas chamadas, Pizza.count é 2. Cada inicializador atualiza o mesmo atributo de classe. p1.size é "large", enquanto p2.size é "small".

Rastreie a busca e o sombreamento

Para atributos de dados comuns como esses, a leitura de p1.MAX_TOPPINGS verifica primeiro a instância e depois a classe. Como nenhum valor está armazenado em p1, o Python encontra Pizza.MAX_TOPPINGS, que é 5.

Uma atribuição por meio de uma instância tem outro efeito:

p1.MAX_TOPPINGS = 8
Pizza.MAX_TOPPINGS = 6
print(p1.MAX_TOPPINGS)     # 8
print(p2.MAX_TOPPINGS)     # 6
print(Pizza.MAX_TOPPINGS)  # 6

A primeira linha cria um atributo de instância em p1. Ele sombreia o valor de classe para esse objeto. Alterar o valor de classe depois alcança p2, que ainda lê da classe, mas não substitui o valor próprio de p1.

AtribuiçãoO que muda
self.size = sizeDados na instância atual
Pizza.count += 1O contador compartilhado da classe
p1.MAX_TOPPINGS = 8Um valor de instância que oculta o valor de classe
Observe duas formas de compartilhamento acidental

Se o inicializador usasse self.count += 1, o Python leria o 0 da classe, somaria um e armazenaria um atributo de instância. Cada nova pizza receberia seu próprio count de 1, enquanto Pizza.count permaneceria em 0. Use o nome da classe ao atualizar um contador de toda a classe.

Uma lista no corpo da classe causa um problema diferente. Com toppings = [], as instâncias que não têm um atributo separado para ingredientes encontram todas a mesma lista. p1.toppings.append("olives") altera essa lista compartilhada; não atribui um novo atributo de instância. Coloque self.toppings = [] em __init__ quando cada pizza precisar de seus próprios ingredientes.

Inspecione os dados próprios de um objeto

Para a p2 original, vars(p2) fornece {'size': 'small', 'toppings': []}. Os valores de classe estão ausentes porque ficam em Pizza. Isso é útil ao depurar uma busca com resultado inesperado: verifique se a instância tem um valor próprio e depois inspecione o valor da classe. Uma configuração compartilhada pertence à classe; um nome, tamanho, saldo ou coleção individual pertence à instância.

Principais conclusões

  • Atributos de instância armazenam dados de um objeto; atributos de classe armazenam configurações compartilhadas ou dados de toda a classe.
  • Um atributo de dados de instância pode sombrear um atributo de classe com o mesmo nome.
  • Atualize um contador compartilhado por meio do nome da classe.
  • Alterar uma lista no nível da classe modifica a única lista compartilhada.
  • vars(obj) mostra os próprios atributos armazenados do objeto, não os atributos de classe que ele pode encontrar.

2.3 Mantenha objetos válidos desde o início

Um objeto deve começar em um estado que seus métodos possam usar. Um retângulo com largura negativa não pode representar a forma pretendida. Uma regra que deve permanecer verdadeira para todos os objetos de uma classe é uma invariante. A validação verifica essas regras antes de aceitar dados.

Verifique antes de armazenar
class Rectangle:
    def __init__(self, width, height):
        if width <= 0:
            raise ValueError("width must be positive")
        if height <= 0:
            raise ValueError("height must be positive")
        self.width = width
        self.height = height

Rectangle(3, 4) passa pelas duas verificações e retorna um objeto inicializado. Rectangle(-2, 4) provoca ValueError na primeira verificação. As atribuições posteriores não são executadas. Se o programa não capturar essa exceção, a execução para nesse ponto.

Suponha que r já se refira a um retângulo válido. Em r = Rectangle(-2, 4), a chamada à direita falha antes da atribuição. r mantém seu valor anterior. Se r não tinha valor antes da chamada que falhou, a atribuição não cria um.

Escolha uma exceção útil
ExceçãoProblema que descreve
ValueErrorUma entrada de tipo aceitável tem um valor inaceitável
TypeErrorA entrada tem o tipo errado para a operação

Uma largura numérica negativa é um erro de valor. Um texto fornecido onde se exige um número é um erro de tipo. A comparação com 0 neste exemplo já provoca TypeError para texto. Uma classe também pode verificar um tipo explicitamente com isinstance quando precisa de uma mensagem mais clara.

Escreva mensagens que nomeiem a regra, como "width must be positive". Uma mensagem que diz apenas "bad input" deixa o código chamador sem saber qual valor precisa ser corrigido.

Deixe o código chamador tratar a recuperação
while True:
    try:
        width = float(input("Width: "))
        room = Rectangle(width, 3)
        break
    except ValueError as error:
        print("Try again:", error)

Digitar abc falha durante float; digitar -2 passa pela conversão, mas falha em Rectangle. Ambos provocam ValueError, então esse laço trata os dois casos. A classe estabelece sua regra de dados, enquanto o código chamador decide solicitar a entrada novamente.

Imprimir um aviso dentro de __init__ não rejeita a chamada. Se a execução continuar, o programa pode receber um objeto inválido e falhar mais tarde ao usá-lo. Lançar uma exceção interrompe a configuração malsucedida onde o problema ocorre.

Ajuste dados deliberadamente e mantenha a regra

Um ajuste documentado pode ser útil. self.name = name.strip() remove espaços das extremidades; valide o nome ajustado se nomes em branco forem proibidos. self.items = list(items) cria uma lista externa separada, então adicionar um item à lista do código chamador não o adiciona a esta lista. Alterar uma largura inválida para seu valor absoluto mudaria o significado pretendido pelo código chamador, então isso exige uma regra de projeto explícita.

As verificações de inicialização ocorrem no momento da criação. Uma atribuição comum como r.width = -5 ainda as ignora depois. Métodos que alteram uma largura devem verificá-la novamente. Uma propriedade pode colocar uma verificação na própria atribuição ao atributo, permitindo que a mesma regra seja executada durante a configuração e em atualizações posteriores.

Principais conclusões

  • Uma invariante é uma regra que o estado válido de um objeto deve satisfazer.
  • Valide antes de aceitar dados e lance uma exceção clara em caso de falha.
  • Uma chamada de criação que falha não atribui um novo objeto ao nome do código chamador.
  • As verificações do construtor também precisam ser aplicadas em métodos ou propriedades posteriores que alteram o estado.
3

Dê comportamento aos objetos com métodos de instância

Métodos transformam dados armazenados em comportamento útil. Um retângulo pode calcular sua área, e uma conta pode atualizar seu saldo enquanto registra uma transação. Acompanhe como o Python passa o objeto receptor a cada método e depois distinga um resultado retornado de uma mudança de estado. Propriedades permitem que uma classe mantenha o acesso legível aos atributos enquanto verifica atribuições ou calcula valores sob demanda.

3.1 Defina e chame métodos de instância

Um método de instância é uma função definida dentro de uma classe que trabalha com o objeto passado como self. O objeto fornece seus dados armazenados; parâmetros extras fornecem as informações adicionais necessárias para a ação.

Leia atributos por meio de self

Adicione estes métodos dentro da classe Rectangle validada, no mesmo nível de indentação de __init__:

    def area(self):
        return self.width * self.height

    def perimeter(self):
        return 2 * (self.width + self.height)

    def is_square(self):
        return self.width == self.height

Com r = Rectangle(3, 4), r.area() retorna 12, r.perimeter() retorna 14 e r.is_square() retorna False. Cada método lê os atributos do mesmo objeto. Um segundo retângulo usa as mesmas definições de método com sua própria largura e altura.

Dentro de um método, self.width nomeia um atributo. width sozinho nomeia uma variável local ou outra variável acessível; o Python não a procura automaticamente no objeto atual. Se essa variável não existir, return width * height provoca NameError. Escrever self. explicita a quem pertencem os dados pretendidos.

Acompanhe a chamada

Para essa classe, r.area() funciona como Rectangle.area(r). O Python encontra a função na classe e fornece r como seu primeiro argumento. Uma referência a método como r.area é um método vinculado: ela guarda a função e esse retângulo. Adicionar () realiza a chamada.

Se um método for definido como def resize(self, factor), chame-o como r.resize(2). r preenche self, e 2 preenche factor. Você não passa r novamente. Se um método de instância for escrito acidentalmente como def area():, uma chamada em r provoca TypeError porque o Python ainda fornece o objeto a uma função que não aceita argumentos.

Um método pode usar outro por meio de self:

    def double_area(self):
        return self.area() * 2

Aqui self.area() calcula usando o mesmo retângulo. Deixar self. de fora pediria uma função separada chamada area, não o método do objeto.

Retorne resultados ao código chamador

Um método de cálculo normalmente deve retornar seu resultado. O retorno dá ao código chamador um valor para armazenar, combinar, testar ou exibir:

r1 = Rectangle(3, 4)
r2 = Rectangle(2, 5)
total = r1.area() + r2.area()
print(total)  # 22

Se area apenas imprimisse sua resposta, cada chamada retornaria None. A adição então falharia com TypeError, embora dois números tivessem aparecido na tela. Imprimir é uma ação; retornar fornece um valor à expressão que contém a chamada.

Use nomes de métodos que mostrem a tarefa. deposit e resize nomeiam ações; is_square e has_room sugerem verificações booleanas. Um método não precisa de parâmetros para dados já armazenados em seu objeto. Ele precisa de parâmetros para novas entradas, como um fator de redimensionamento ou um valor de depósito.

Principais conclusões

  • Coloque self primeiro em um método de instância; o Python fornece o objeto receptor.
  • Leia atributos e chame outros métodos por meio de self..
  • Parênteses executam um método vinculado.
  • Retorne valores calculados para que o código chamador possa combiná-los, testá-los ou exibi-los.

3.2 Altere o estado de um objeto com métodos

O estado de um objeto é o conjunto atual de valores de seus atributos. Um método de acesso informa esse estado. Um método de alteração o modifica. Um método de alteração útil verifica suas entradas e mantém todos os atributos relacionados, para que o código chamador não precise coordenar várias atualizações.

Valide toda a alteração primeiro

Adicione estes métodos dentro da classe BankAccount, com seu titular, saldo e lista de transações:

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("deposit must be positive")
        self.balance += amount
        self.transactions.append(("deposit", amount))

    def withdraw(self, amount):
        if amount <= 0:
            raise ValueError("withdrawal must be positive")
        if amount > self.balance:
            raise ValueError("insufficient funds")
        self.balance -= amount
        self.transactions.append(("withdraw", amount))

Um depósito altera o saldo e registra o evento. Um saque verifica tanto o sinal do valor quanto o saldo disponível antes de subtrair. Essas verificações impedem que um saque rejeitado altere o saldo ou seu histórico.

Começando com BankAccount("Maya", 50), depositar 25 resulta em 75, e sacar 30 resulta em 45. Uma tentativa posterior de sacar 100 provoca ValueError; ela deixa o saldo em 45 e não adiciona nenhuma transação. Colocar a subtração antes da verificação de saldo alteraria o estado mesmo em caso de falha.

Distinga uma alteração de um valor retornado

Esses métodos de alteração não têm instrução de retorno, então retornam None. Depois de new_balance = acct.deposit(25), o depósito foi realizado, mas new_balance é None. Leia acct.balance depois para obter o número atualizado.

Uma classe pode oferecer outro tipo de método que retorna um novo objeto. resize sugere alterar um retângulo; resized pode ser escolhido para um método que retorna um novo retângulo e deixa o original intacto. Leia o contrato do método: o nome ajuda, mas sua definição determina o comportamento.

Rastreie referências compartilhadas
a = BankAccount("Maya", 50)
b = a
b.deposit(10)
print(a.balance)  # 60
print(a is b)     # True

A atribuição associa um nome a um objeto; ela não copia esse objeto. Ambos os nomes acessam a conta cujo saldo mudou. Passar a a uma função também permite acesso a esse objeto. Uma função que chama account.withdraw(5) pode, portanto, alterar a conta vista pelo código chamador.

Para uma conta independente com o mesmo titular e saldo, crie BankAccount(a.owner, a.balance). Esse construtor também fornece um novo histórico vazio. É uma nova conta, não uma cópia completa do histórico.

copy.copy(a) cria um novo objeto externo, mas mantém referências aos mesmos objetos internos, incluindo a lista de transações. copy.deepcopy(a) também copia objetos aninhados. Quando a independência importa, verifique os objetos dentro dos atributos, além da instância externa.

Atribuir diretamente acct.balance = -500 ignora as verificações dos dois métodos. Convenções para atributos internos e propriedades ajudam a deixar claro o caminho previsto para atualizações e a manter a validação próxima das mudanças de estado.

Principais conclusões

  • Verifique uma alteração solicitada antes de atualizar atributos.
  • Um método de alteração pode mudar o estado e retornar None.
  • A atribuição e a passagem de argumentos preservam referências ao mesmo objeto.
  • Uma cópia superficial ainda pode compartilhar objetos mutáveis aninhados.
  • Mantenha atualizações de estado relacionadas juntas no método responsável por elas.

3.3 Proteja atributos com convenções de nomenclatura e propriedades

O código chamador precisa de uma forma clara de ler e alterar um objeto. O encapsulamento mantém as regras dos dados junto aos métodos que os gerenciam. O Python usa convenções e propriedades para expressar essa interface.

Marque o armazenamento interno

Um sublinhado inicial em _balance indica um atributo interno. O código chamador deve usar os métodos da conta, mas o Python ainda permite acesso direto. Não há uma palavra reservada private que bloqueie o acesso ao valor.

Um nome como __balance, com dois sublinhados iniciais e sem dois sublinhados finais, aciona a transformação de nome. Dentro de class Acc, self.__balance é armazenado como _Acc__balance. Fora da classe, obj.__balance falha, mas obj._Acc__balance ainda pode funcionar. A transformação evita conflitos de nomes; ela não protege dados sensíveis.

Execute um método pela sintaxe de atributo

Uma propriedade faz um método ser executado quando um atributo é lido. A linha @property é um decorador: ela altera como o Python disponibiliza o método. Uma propriedade sem método de definição permite a leitura, mas rejeita a atribuição.

class Account:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self._balance = balance

    @property
    def balance(self):
        return self._balance

Depois de acct = Account("Maya", 50), leia acct.balance sem parênteses. Atribuir acct.balance = 10 provoca AttributeError porque essa propriedade não tem método de definição. Um método de depósito dentro da classe poderia atualizar _balance após suas verificações.

Verifique atribuições com um método de definição
class Student:
    def __init__(self, name, score):
        self.name = name
        self.score = score

    @property
    def score(self):
        return self._score

    @score.setter
    def score(self, value):
        if not 0 <= value <= 100:
            raise ValueError("score must be between 0 and 100")
        self._score = value

O método de leitura e o método de definição compartilham o nome score; o valor armazenado usa _score. Na criação, self.score = score executa o método de definição.

Para s = Student("Ana", 88), atribuir s.score = 92 armazena 92. Atribuir s.score = -1 provoca ValueError antes do armazenamento, então a nota continua sendo 92. Student("Ana", 105) falha durante a inicialização.

Dentro do método de definição, self.score = value chamaria esse método novamente, repetindo até ocorrer RecursionError. O método de leitura tem o mesmo risco se retornar self.score. Use o nome de armazenamento separado _score em ambos os corpos.

Calcule valores quando forem lidos

Uma propriedade pode calcular um valor a partir de outros atributos:

    @property
    def area(self):
        return self.width * self.height

Colocada dentro de uma classe de retângulo, ela permite r.area. Para largura 3 e altura 4, o valor é 12. Alterar a largura para 5 faz a próxima leitura retornar 20. Armazenar uma área uma vez durante a inicialização deixaria um valor antigo após uma alteração da largura.

Comece com um atributo comum quando o armazenamento simples atender à necessidade.

Principais conclusões

  • Um sublinhado inicial marca o armazenamento interno por convenção; ele não bloqueia o acesso.
  • A transformação de nome altera o nome de um atributo para evitar conflitos.
  • Uma propriedade executa um método de leitura por meio da sintaxe de atributo.
  • Um método de definição pode validar a inicialização e atribuições posteriores em um único lugar.
  • Use um atributo de armazenamento separado para evitar chamar a propriedade recursivamente.
  • Uma propriedade calculada deriva seu valor quando é lida.
4

Use métodos especiais para exibir e comparar objetos adequadamente

Um objeto útil deve se integrar naturalmente às expressões comuns do Python. Seus métodos especiais podem fornecer texto legível, igualdade de valores, uma ordenação, resultados aritméticos ou um comprimento. Aprenda qual método o Python solicita em cada situação e o que esse método deve retornar. Preste atenção especial à exibição dentro de contêineres, à estabilidade das chaves de dicionário e à diferença entre uma operação e sua forma refletida.

4.1 Dê aos objetos representações em string legíveis

Um objeto é mais fácil de inspecionar quando pode descrever seus próprios dados. Métodos especiais permitem que o Python solicite essa descrição por meio de ações familiares como print, str e repr. Seus nomes começam e terminam com dois sublinhados e são frequentemente chamados de nomes dunder.

Escolha textos para dois públicos

__str__ fornece texto legível para os usuários de um programa. __repr__ fornece uma descrição precisa para programadores. Ambos retornam strings; nenhum deles deve imprimir seu resultado em lugar de retorná-lo.

class Point:
    def __init__(self, x, y):
        self.x = x
        self.y = y

    def __str__(self):
        return f"({self.x}, {self.y})"

    def __repr__(self):
        return f"Point({self.x}, {self.y})"

p = Point(3, 4)
print(p)        # (3, 4)
print(repr(p))  # Point(3, 4)

str(p) retorna a string "(3, 4)". repr(p) retorna "Point(3, 4)". Quando possível, uma representação se parece com uma chamada de classe que recriaria o valor. Ela continua sendo texto: chamar repr não cria, por si só, outro ponto.

Sem uma representação personalizada, uma instância comum geralmente exibe um nome de classe e um identificador semelhante a um endereço. Isso ajuda a distinguir objetos, mas não mostra seus dados úteis. Você raramente chama esses métodos especiais diretamente; defina-os uma vez e use as operações comuns de exibição do Python.

Preveja o que um contêiner exibe
ExpressãoRepresentação solicitada
print(p) ou str(p)__str__
repr(p)__repr__
f"{p}"A forma usual em string para essa classe
f"{p!r}"__repr__
print([p])A exibição da lista, usando repr para seu item

Com a classe acima, print([Point(3, 4), Point(0, 0)]) mostra [Point(3, 4), Point(0, 0)]. A lista usa as representações de seus elementos, embora cada elemento também tenha __str__.

Se você definir apenas __repr__, o __str__ padrão também o usará. Definir apenas __str__ não altera repr nem a exibição de contêineres. Portanto, ao adicionar apenas um método de exibição, __repr__ atende a mais contextos de inspeção.

Torne os campos de string inequívocos

Para um livro que já armazena title, author e year, estes métodos oferecem diferentes formas úteis de exibição:

    def __str__(self):
        return f"{self.title} by {self.author} ({self.year})"

    def __repr__(self):
        return f"Book({self.title!r}, {self.author!r}, {self.year})"

Para o livro com título "Dune", autor "Frank Herbert" e ano 1965, o primeiro método fornece "Dune by Frank Herbert (1965)". O segundo fornece "Book('Dune', 'Frank Herbert', 1965)". !r solicita as representações dos campos de string, incluindo aspas.

Um corpo que contém apenas print(self.x) retorna None, então str(p) falha com TypeError. Retornar um número inteiro também falha. Construa o texto necessário e depois retorne essa string.

Principais conclusões

  • __str__ fornece texto para o usuário; __repr__ fornece uma representação útil para o programador.
  • Contêineres exibem seus elementos com repr.
  • __repr__ pode fornecer a forma padrão em string; __str__ não substitui repr.
  • Ambos os métodos devem retornar strings.
  • Use !r em uma f-string para mostrar representações de campos de string entre aspas.

4.2 Compare e combine objetos com operadores

Métodos especiais conectam uma classe a operadores e funções embutidas. Defina apenas operações com significado claro para esse tipo: somar vetores é compreensível; adicionar um jogador a um time fica mais claro como team.add_player(player).

Compare valores e mantenha os hashes seguros

Duas instâncias comuns e separadas são consideradas diferentes por padrão. Acrescente igualdade de valores a uma classe de ponto com:

    def __eq__(self, other):
        if not isinstance(other, Point):
            return NotImplemented
        return self.x == other.x and self.y == other.y

Agora dois pontos em (3, 4) são considerados iguais, embora suas identidades sejam diferentes. in, count, index e remove em listas podem encontrar esses valores iguais. O resultado usual de != decorre de __eq__.

NotImplemented é um valor de retorno que permite ao Python tentar o outro operando ou uma alternativa. Não é uma exceção a ser lançada. Quando nenhum dos operandos permite comparar valores, == recorre à identidade; um ponto e a string "3,4" são considerados diferentes.

Definir __eq__ sem __hash__ torna as instâncias não hasháveis. Assim, elas não podem ser elementos de conjuntos nem chaves de dicionários. Objetos iguais devem ter hashes iguais, e o hash de uma chave deve permanecer estável. Se os atributos comparados nunca mudarem, um hash correspondente pode retornar hash((self.x, self.y)). Mantenha objetos de valor mutáveis como não hasháveis quando seus campos de igualdade puderem mudar.

Defina uma ordenação

__lt__ fornece <. Uma classe de aluno pode comparar notas com return self.score < other.score. As notas 88, 72 e 95 são então ordenadas como 72, 88, 95. min escolhe a menor; max escolhe a maior usando > e sua comparação refletida <.

Com apenas __lt__, a > b pode usar b < a. O Python não infere <= ou >= dessa definição; eles precisam de seus próprios métodos. Se uma ordem for útil apenas para uma tarefa, passe uma função de chave:

def by_score(student):
    return student.score

ordered = sorted(students, key=by_score)
Retorne novos resultados de operações aritméticas

Dentro de uma classe de vetor com atributos x e y:

    def __add__(self, other):
        if not isinstance(other, Vector):
            return NotImplemented
        return Vector(self.x + other.x, self.y + other.y)

    def __mul__(self, factor):
        if not isinstance(factor, (int, float)):
            return NotImplemented
        return Vector(self.x * factor, self.y * factor)

    def __rmul__(self, factor):
        return self * factor

Vector(1, 2) + Vector(3, 4) produz um novo vetor (4, 6). Multiplicar o primeiro vetor por 3 produz (3, 6), deixando-o inalterado. __mul__ trata o vetor à esquerda; __rmul__ aceita um número à esquerda. O Python não presume que as duas ordens tenham o mesmo significado.

Forneça um comprimento significativo

Para uma lista de reprodução que armazena self.songs, defina __len__ para retornar len(self.songs). O resultado deve ser um número inteiro não negativo. len(playlist) então conta as músicas. Nessa classe, comprimento zero também faz if playlist: ser falso; um __bool__ personalizado teria prioridade sobre o comprimento ao testar o valor lógico.

Principais conclusões

  • __eq__ define igualdade de valores; retorne NotImplemented para tipos de operando não aceitos.
  • Objetos hasháveis iguais precisam ter hashes iguais e estáveis.
  • __lt__ permite a ordenação, mas não fornece <= ou >=.
  • Use uma função de chave para uma ordem específica de uma tarefa.
  • Métodos aritméticos podem criar novos objetos sem alterar os operandos.
  • __rmul__ permite a multiplicação com o objeto à direita.
  • __len__ retorna um número inteiro não negativo e pode fornecer o valor lógico em contexto quando não existe __bool__.
5

Crie programas com objetos que trabalham juntos

Programas maiores conectam objetos com funções diferentes. Um carrinho pode pedir subtotais a itens; uma conta pode pedir a outra que receba um depósito. Rastreie as referências e a sequência de chamadas para ver qual estado muda e onde um erro interrompe a execução. Depois, use essas ideias para escolher um projeto claro, planejar uma classe pequena e testar seu comportamento público em etapas manejáveis.

5.1 Crie objetos que contenham outros objetos

Os atributos de um objeto podem se referir a outros objetos. Um carrinho contém itens, uma biblioteca contém livros e um curso tem alunos. A composição constrói um objeto maior a partir de objetos menores por meio dessa relação de posse.

Dê a cada objeto sua própria responsabilidade
class Item:
    def __init__(self, name, price, quantity):
        self.name = name
        self.price = price
        self.quantity = quantity

    def subtotal(self):
        return self.price * self.quantity

class ShoppingCart:
    def __init__(self):
        self.items = []

    def add(self, item):
        if not isinstance(item, Item):
            raise TypeError("a cart holds Item objects")
        self.items.append(item)

    def total(self):
        total = 0
        for item in self.items:
            total += item.subtotal()
        return total

O carrinho tem uma lista separada criada durante a inicialização. Seu método add verifica o objeto recebido antes de adicioná-lo. cart.add("pen") provoca TypeError em vez de armazenar texto que falhará quando um subtotal for solicitado.

Crie os itens antes ou durante as chamadas:

cart = ShoppingCart()
cart.add(Item("notebook", 2.50, 4))
cart.add(Item("pen", 1.25, 3))
print(cart.total())         # 13.75
print(cart.items[0].name)   # notebook

Os subtotais são 10.0 e 3.75; o carrinho os soma para obter 13.75. A cadeia cart.items[0].name acessa primeiro a lista do carrinho, depois seu primeiro objeto e, por fim, o nome desse objeto.

Delegue o trabalho ao objeto que sabe realizá-lo

O carrinho pede subtotal() a cada item. Isso é delegação: um objeto chama o método de outro objeto para realizar parte de uma tarefa. Se um desconto por quantidade mudar a forma de calcular um subtotal, essa regra pertence a Item.subtotal. O carrinho pode continuar somando os subtotais retornados sem duplicar as regras de preço.

Passar Item em vez de Item(...) passa a classe, não um item comprado. A verificação de tipo rejeita esse objeto de classe. Faça todas as adições pelo caminho verificado; o acesso direto a cart.items.append(...) ignoraria add.

Busque um objeto

Adicione este método dentro de ShoppingCart:

    def find(self, name):
        for item in self.items:
            if item.name == name:
                return item
        return None

A primeira correspondência retorna o item existente. Se não houver correspondência, retorna None. Verifique found is None antes de usar found.name; None não tem esse atributo. Um método __len__ que retorna len(self.items) também poderia fazer len(cart) contar os objetos de item armazenados, enquanto __str__ poderia unir as linhas dos itens em um recibo.

Acompanhe referências dentro de coleções

Uma lista armazena referências, então colocar o mesmo item em dois carrinhos não o copia. Atualizar o preço desse item afeta o próximo total calculado por ambos os carrinhos. Listas separadas para os carrinhos evitam o compartilhamento acidental da lista, mas não tornam independentes os objetos dentro delas. Crie itens separados quando os carrinhos precisarem armazenar dados independentes.

Principais conclusões

  • A composição armazena objetos dentro de outro objeto.
  • A delegação pede a um objeto contido que realize sua parte de uma tarefa.
  • Verifique um objeto antes de armazená-lo quando métodos posteriores exigirem seu tipo.
  • Uma busca pode retornar o objeto correspondente ou None.
  • Contêineres separados ainda podem se referir ao mesmo objeto contido.

5.2 Faça objetos interagirem por meio de métodos

Objetos podem ser argumentos e valores de retorno de métodos, assim como números ou strings. Acompanhe separadamente o receptor, os argumentos e o resultado: um método pode alterar um objeto existente, retornar um novo objeto ou apenas informar um valor.

Use outro objeto como entrada

Dentro de uma classe de ponto com x e y, um método pode ler ambos os pontos:

    def midpoint(self, other):
        return Point((self.x + other.x) / 2,
                     (self.y + other.y) / 2)

Para p = Point(3, 4) e q = Point(1, 0), middle = p.midpoint(q) cria um terceiro ponto em (2.0, 2.0). Durante a chamada, self se refere a p e other se refere a q. O método calcula coordenadas e constrói um novo ponto; ele não atribui nada a nenhum dos originais.

Com import math, um método de distância pode retornar math.hypot(self.x - other.x, self.y - other.y). Point(0, 0).distance_to(Point(3, 4)) então fornece 5.0. Essa chamada retorna um número, enquanto midpoint retorna um objeto.

Deixe cada receptor gerenciar suas alterações

Dentro de uma classe de conta com métodos withdraw e deposit que fazem verificações:

    def transfer_to(self, other, amount):
        self.withdraw(amount)
        other.deposit(amount)

Se Maya tem 100 e Leo tem 20, maya.transfer_to(leo, 30) primeiro saca da conta de Maya, deixando 70, e depois deposita na conta de Leo, resultando em 50. Na primeira chamada, self é a conta de Maya. Na chamada de depósito, seu próprio self é a conta de Leo.

Uma transferência posterior de 500 falha em withdraw porque Maya tem apenas 70. A execução para antes de deposit, então ambos os saldos mantêm seus valores anteriores. A ordem das chamadas importa. O Python não desfaz uma mudança de estado anterior se uma chamada posterior lançar uma exceção; esse método curto depende de a conta receptora poder aceitar o valor do depósito.

Chamar other.deposit(amount) executa as regras da conta receptora em um único lugar. Alterar diretamente other.balance as ignoraria.

Rastreie os objetos, não apenas os nomes das variáveis

Desenhe cada instância como uma caixa que contém seus atributos. Desenhe uma seta de cada variável até sua caixa. Em uma chamada, identifique qual caixa é recebida como self e quais caixas são passadas como argumentos. Atualize a caixa afetada após cada linha bem-sucedida que altera o estado. Duas setas para uma caixa significam que uma alteração por qualquer um dos nomes alcança o mesmo objeto.

Uma função comum também pode trabalhar com vários objetos. total_balance(accounts) pode percorrer contas e somar seus saldos. Esse cálculo sobre a lista inteira não precisa pertencer a uma única conta.

Represente uma relação ausente

Um livro pode começar com self.borrower = None e depois armazenar um objeto de membro. Antes de ler um atributo do membro, verifique a relação:

if book.borrower is not None:
    print(book.borrower.name)

Caso contrário, book.borrower.name falha quando o livro não tem uma pessoa que o tomou emprestado. Classes podem ser definidas acima de uma função main() que cria e conecta os objetos. Chamar main() sob if __name__ == "__main__": impede que essas ações sejam executadas quando as classes são importadas.

Principais conclusões

  • Identifique o receptor como self e o objeto passado como argumento em seu próprio parâmetro.
  • Um método pode retornar um novo objeto e deixar suas entradas inalteradas.
  • As chamadas acontecem em ordem; uma exceção interrompe chamadas posteriores, mas não desfaz alterações anteriores.
  • Use os métodos públicos de outro objeto para aplicar suas regras.
  • Verifique se há None antes de acessar uma relação opcional com outro objeto.
  • Uma função pode organizar cálculos que envolvem vários objetos.

5.3 Decida quando uma classe torna um programa mais claro

Uma classe justifica seu uso quando dados relacionados e operações repetidas ficam mais fáceis de entender juntos. Você não precisa transformar toda função ou registro em uma classe. Escolha a estrutura que deixa claras as responsabilidades do programa.

Procure dados e comportamentos que andam juntos

Classes ajudam quando várias funções recebem repetidamente o mesmo grupo de valores, muitos registros compartilham uma estrutura e mudanças de estado devem seguir regras comuns. Várias contas que aceitam depósitos e saques são um bom caso. Um único cálculo pode continuar sendo uma função. Um registro fixo e somente leitura pode continuar sendo uma tupla ou um dicionário.

Uma classe com apenas configuração e uma operação única muitas vezes acrescenta mais formalidade do que estrutura útil. Pergunte o que uma instância representa e quais ações repetidas pertencem a ela. Se você não conseguir responder a essas perguntas de forma simples, reconsidere o projeto.

Mova uma operação para junto de seus dados

Uma conta baseada em dicionário pode começar assim:

account = {"owner": "Maya", "balance": 50}

def deposit(account, amount):
    account["balance"] += amount

A versão com classe pode colocar a mesma operação ao lado de seu estado e de suas regras:

class BankAccount:
    def __init__(self, owner, balance=0):
        self.owner = owner
        self.balance = balance

    def deposit(self, amount):
        if amount <= 0:
            raise ValueError("deposit must be positive")
        self.balance += amount

    def __repr__(self):
        return f"BankAccount({self.owner!r}, {self.balance})"

Depois de acct = BankAccount("Maya", 50) e acct.deposit(25), sua representação é BankAccount('Maya', 75). A regra de depósito positivo agora é executada sempre que o código chamador usa esse método. Ler account["balence"] provocaria KeyError; ler acct.balence provoca AttributeError. Uma classe não elimina erros de escrita, mas dá aos dados e a suas operações um lugar com nome.

Planeje a partir de uma breve descrição

Para “Uma biblioteca empresta livros a seus membros. Um membro pode pegar emprestados no máximo 3 livros”, os substantivos sugerem Book, Member e Library. Os verbos sugerem métodos de empréstimo e devolução.

  1. Liste o que cada instância sabe: título, autor e pessoa que tomou o livro emprestado; nome e livros emprestados de um membro; livros e membros de uma biblioteca.
  2. Identifique regras: um livro não pode já estar emprestado, e um membro não pode exceder MAX_LOANS = 3.
  3. Coloque cada regra na operação que altera a relação, como Library.check_out(book, member).
  4. Escreva a inicialização e uma representação útil, depois adicione e teste uma ação por vez.

Títulos diferentes precisam de instâncias diferentes de Book, não de classes separadas com o nome de cada título. Use substantivos no singular para objetos individuais, como Book; nomeie uma coleção de acordo com a coleção, como Library. Mantenha a função de cada classe clara o bastante para ser descrita em uma frase.

Teste o comportamento observável
acct = BankAccount("Maya", 50)
acct.deposit(25)
assert acct.balance == 75

assert provoca AssertionError se sua condição for falsa. Teste também ações rejeitadas: capture a exceção esperada e verifique se o estado não mudou. Use validação explícita com raise para entradas do código chamador; asserções são verificações durante o desenvolvimento e podem ser desativadas quando o Python é executado com otimização.

Principais conclusões

  • Escolha uma classe quando estado relacionado e operações repetidas se beneficiarem de um lugar comum.
  • Mantenha um cálculo simples como função quando não for necessário um estado duradouro de objeto.
  • Planeje atributos a partir dos dados e métodos a partir das ações e regras.
  • Modele valores de dados diferentes como instâncias de uma mesma classe.
  • Teste tanto atualizações bem-sucedidas quanto alterações rejeitadas.

Exercícios resolvidos de Programação orientada a objetos em Python

5 Lições Interativas

1

De valores familiares à sua primeira classe

11 passos
Rastreie chamadas de métodos e referências compartilhadas, depois crie dois objetos com seus próprios atributos.
  1. 1
    IntroduçãoComece com objetos que você conhece

    Uma lista, uma string e um número são todos objetos. Seus tipos determinam quais operações eles aceitam, e cada valor tem sua própria identidade.

    Comece acompanhando um método de string e um método de lista. Depois, use as mesmas ideias para criar uma classe simples cujas instâncias armazenam dados diferentes. Continue fazendo duas perguntas: a qual objeto este nome se refere e o que esta chamada retorna?

  2. 2
    ExemploConsulte o tipo de um valor

    O Python pode informar que tipo de objeto é um valor.

    text = "river"
    values = [4, 2]
    print(type(text))              # <class 'str'>
    print(isinstance(values, list))  # True
    

    A string e a lista oferecem métodos diferentes porque têm tipos diferentes. Você pode usar dir(values) para inspecionar os nomes disponíveis na lista, incluindo métodos comuns e métodos especiais.

  3. 3
    ExemploAcompanhe uma string retornada

    Um método de string pode produzir um novo valor sem substituir o original.

    name = "robin"
    result = name.title()
    print(result)  # Robin
    print(name)    # robin
    

    title() retorna uma nova string. A atribuição armazena esse resultado em result, enquanto name ainda se refere a "robin". Escrever name = name.title() faria name se referir à string retornada.

  4. 4
    ExemploAcompanhe uma alteração na própria lista

    Compare a chamada de string com uma ordenação de lista.

    values = [4, 2]
    result = values.sort()
    print(values)  # [2, 4]
    print(result)  # None
    

    A própria lista muda, e o valor de retorno é None. Atribuir a chamada de volta a values substituiria a referência à lista desse nome por None. Use values.sort() sozinho quando quiser alterar a lista existente.

  5. 5
    DesafioOnde aparece o item adicionado?

    Preveja as duas listas impressas.

    a = [7]
    b = a
    c = [7]
    b.append(2)
    print(a, c)
    

    ---reveal---

    A saída é [7, 2] [7]. a e b se referem a uma mesma lista, então adicionar um item por meio de b altera a lista vista por meio de a. c armazena uma lista separada. As listas eram inicialmente iguais, mas a is c era falso.

  6. 6
    DicaDesenhe referências antes de rastrear alterações

    Desenhe uma caixa para cada lista criada separadamente. Aponte tanto a quanto b para a primeira caixa e aponte c para a segunda. Adicionar um item altera a caixa; uma atribuição altera para onde um nome aponta.

    Distinga também um método de sua chamada: name.title fornece um objeto de método, enquanto name.title() o executa. Os parênteses podem fazer a diferença entre armazenar um comportamento e obter seu resultado.

  7. 7
    AnalogiaUm formulário, várias cópias preenchidas

    Pense em uma classe como um formulário em branco que descreve as informações armazenadas por um tipo de objeto. Uma instância é uma cópia preenchida com seus próprios dados.

    Uma classe Badge pode representar todos os crachás de um programa. Uma instância armazena o nome de um visitante; outra armazena um nome diferente. A classe descreve o tipo de objeto compartilhado. Você não precisa de uma nova classe para cada valor escrito no formulário.

  8. 8
    ExemploDefina o tipo e crie duas instâncias

    Use uma classe mínima enquanto aprende como as instâncias funcionam.

    class Badge:
        pass
    
    first = Badge()
    second = Badge()
    first.name = "Rae"
    second.name = "Ivo"
    

    pass fornece um corpo vazio. Cada chamada de classe cria uma instância, e as atribuições com ponto armazenam dados no objeto selecionado. first is second é falso. first = Badge daria outro nome à classe sem criar uma instância.

  9. 9
    DesafioEncontre um atributo escrito incorretamente

    Continue com os dois crachás:

    first.nmae = "Tess"
    print(first.name, second.name)
    

    O que aparece?

    ---reveal---

    A saída é Rae Ivo. A atribuição cria um atributo separado chamado nmae; ela não altera name. Ler um atributo que não existe provoca AttributeError, mas atribuir um novo nome é permitido. Confira a escrita nos dois pontos quando uma leitura produzir um resultado inesperado.

  10. 10
    DesafioCombine instâncias e referências alternativas

    Continue com first e second do exemplo dos crachás:

    copy_name = first
    copy_name.name = "Noor"
    print(first.name, second.name)
    

    ---reveal---

    A saída é Noor Ivo. copy_name é uma referência alternativa para o primeiro crachá, então a atribuição altera esse objeto. O segundo crachá permanece separado. Criar instâncias controla quantos objetos existem; atribuir nomes controla quais objetos esses nomes acessam.

  11. 11
    RecapitulaçãoRastreie mais um programa no papel

    Experimente um programa curto com duas chamadas de classe e um terceiro nome atribuído à primeira instância. Dê atributos diferentes aos objetos e depois altere um deles por meio da referência alternativa.

    Antes de executá-lo, desenhe as caixas dos objetos e preveja cada leitura. Se uma previsão diferir da saída, inspecione a escrita dos atributos e as setas dos nomes para os objetos. Isso distingue um erro de configuração de um erro de referência.

2

Crie objetos com dados iniciais confiáveis

14 passos
Inicialize uma pequena coleção de tarefas, distinga configurações compartilhadas de dados individuais e rejeite configurações inválidas.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoConfigure uma coleção de tarefas
  2. 2
    ExplicaçãoForneça um inicializador ao Python
  3. 3
    ExemploRelacione uma chamada a seus parâmetros
  4. 4
    DesafioIdentifique o atributo ausente
  5. 5
    ExemploAceite uma lista inicial opcional
  6. 6
    DicaVerifique o alcance da cópia
  7. 7
    ExplicaçãoAdicione dados sobre a classe
  8. 8
    ExemploAcompanhe uma atribuição que causa sombreamento
  9. 9
    DesafioCorrija o contador compartilhado
  10. 10
    ExplicaçãoRejeite um limite que não pode funcionar
  11. 11
    ExemploEscolha o erro e o caminho de recuperação
  12. 12
    DesafioImprimir rejeita a configuração?
  13. 13
    DesafioMantenha o objeto anterior em caso de falha
  14. 14
    RecapitulaçãoVerifique a configuração como código chamador
3

Controle o estado com métodos e propriedades

16 passos
Crie um contador de estoque com verificações, rastreie uma referência alternativa e disponibilize acesso validado a um atributo.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoTorne um contador de estoque útil
  2. 2
    ExemploComece com a quantidade armazenada
  3. 3
    ExplicaçãoDeixe o receptor fornecer self
  4. 4
    DesafioTorne um resultado reutilizável
  5. 5
    ExemploAdicione estoque após verificar a quantidade
  6. 6
    ExemploRejeite uma retirada antes da subtração
  7. 7
    DesafioRastreie uma alteração por uma referência alternativa
  8. 8
    ConexãoCopie o nível adequado do estado
  9. 9
    ExplicaçãoMarque o armazenamento interno
  10. 10
    ExemploLeia por meio de uma propriedade
  11. 11
    ExemploPermita atribuição pública com verificação
  12. 12
    DesafioEvite um método de definição recursivo
  13. 13
    Ideia-chaveCalcule um valor quando ele for lido
  14. 14
    DesafioCombine um método de definição com uma referência alternativa
  15. 15
    DicaLeia a definição e a chamada juntas
  16. 16
    RecapitulaçãoTeste uma operação por vez
4

Faça objetos funcionarem em expressões Python

17 passos
Dê a um par de coordenadas exibições, comparações, operações aritméticas e comportamento integrado úteis e claros.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoEscolha o que um objeto deve representar
  2. 2
    ExemploComece com um ponto
  3. 3
    ExemploAdicione texto para inspeção
  4. 4
    DesafioPreveja a exibição de uma lista
  5. 5
    DicaRetorne texto nos dois métodos de exibição
  6. 6
    ExemploCompare valores de coordenadas
  7. 7
    Ideia-chaveDeixe comparações não aceitas para o Python
  8. 8
    DesafioPares iguais podem entrar em um conjunto?
  9. 9
    ExemploEscolha uma ordenação de coordenadas
  10. 10
    ConexãoUma ordem pode pertencer a uma tarefa
  11. 11
    ExemploSome sem alterar os operandos
  12. 12
    ExemploPermita ambas as ordens de multiplicação
  13. 13
    DesafioDistinga um novo resultado de uma referência alternativa
  14. 14
    ExplicaçãoUse comprimento para uma coleção
  15. 15
    DesafioPreveja o ramo para a fila vazia
  16. 16
    DicaMantenha o significado visível
  17. 17
    RecapitulaçãoVerifique cada expressão separadamente
5

Conecte objetos com funções claras

15 passos
Construa um pequeno pedido a partir de objetos que representam linhas, acompanhe referências compartilhadas e teste uma operação rejeitada.

Salve esta aula para seguir cada Lição Interativa passo a passo.

  1. 1
    IntroduçãoConstrua um pedido a partir de partes menores
  2. 2
    ExemploDê a uma linha seu próprio cálculo
  3. 3
    ExemploArmazene linhas em um pedido
  4. 4
    ExemploDelegue cada cálculo de custo
  5. 5
    DesafioRastreie uma linha compartilhada por dois pedidos
  6. 6
    DicaVerifique um resultado de busca ausente
  7. 7
    ExplicaçãoPasse um objeto para outro método
  8. 8
    ExemploAcompanhe o receptor e o resultado
  9. 9
    DesafioOnde uma combinação malsucedida para?
  10. 10
    ConexãoRastreie chamadas que alteram dois objetos
  11. 11
    ExplicaçãoDecida quais comportamentos pertencem uns aos outros
  12. 12
    DesafioEvite uma classe para cada produto
  13. 13
    ExemploTeste o ponto de entrada público com verificação
  14. 14
    DesafioEscolha um teste para o projeto combinado
  15. 15
    RecapitulaçãoPlaneje sua própria interação entre objetos

Questões de Programação orientada a objetos em Python

50 Questões de Prática em 5 conjuntos

Conjunto 1: Objetos, referências e configuração confiável10 Questões de Prática

Questão 1 de 10

O que este código imprime?

label = "cedar"
label.upper()
print(label)
  1. Texto em maiúsculas: CEDAR
  2. O valor None
  3. Texto original: cedar
  4. Texto com inicial maiúscula: Cedar
Mostrar respostaOcultar resposta
Resposta: C
upper() retorna uma nova string, mas essa chamada não armazena o resultado. O nome label ainda se refere à string original, então a saída é cedar. Esperar CEDAR trata o método de string como uma atualização no próprio objeto; strings são imutáveis.

Questão 2 de 10

O que este código imprime?

a = [6, 2]
b = a
a = sorted(a)
b.append(9)
print(a, b)
  1. [2, 6] [6, 2, 9]
  2. [2, 6, 9] [2, 6, 9]
  3. [6, 2, 9] [2, 6]
  4. [2, 6] [2, 6, 9]
Mostrar respostaOcultar resposta
Resposta: A
sorted(a) cria uma nova lista ordenada, e a atribuição faz a se referir a ela. b ainda se refere à lista original, então a adição altera essa lista para [6, 2, 9]. Manter ambos os nomes na mesma lista ignora a reatribuição de a.

Questão 3 de 10

O que first.name contém ao final?

class Badge:
    pass
first = Badge()
second = Badge()
first.name = "Rae"
second.name = "Ivo"
first.nmae = "Sol"
  1. "Sol"
  2. "Ivo"
  3. Nenhum valor; a atribuição falha.
  4. "Rae"
Mostrar respostaOcultar resposta
Resposta: D
Foi atribuído "Rae" a first.name, e ele nunca recebe outra atribuição. O nome escrito incorretamente nmae cria um atributo separado em first, enquanto second.name pertence a outro objeto. A atribuição de um novo atributo é permitida, então o erro de escrita não provoca um erro por si só.

Questão 4 de 10

Um programa precisa de três sensores com identificações diferentes, mas com os mesmos métodos de leitura e reinicialização. Como suas classes e seus objetos devem ser organizados?
  1. Uma classe de sensor por identificação, com um objeto cada.
  2. Uma classe de sensor, com três instâncias separadas.
  3. Uma instância de sensor, com três nomes para ela.
  4. Três classes de identificação, compartilhando uma instância de sensor.
Mostrar respostaOcultar resposta
Resposta: B
Os sensores compartilham um tipo de comportamento, então uma classe pode definir seus métodos de leitura e reinicialização. Instâncias separadas podem armazenar suas diferentes identificações e leituras atuais. Três nomes para uma única instância acessariam o mesmo estado, então reinicializar um reinicializaria o sensor compartilhado.

Questão 5 de 10

O que este código imprime?

class Counter:
    def __init__(self, start=4):
        self.value = start
c = Counter(9)
print(c.value)
  1. 4
  2. 0
  3. 9
  4. None
Mostrar respostaOcultar resposta
Resposta: C
O Python fornece o novo objeto como self e passa 9 como start. O inicializador armazena esse valor em self.value, então a saída é 9. O valor padrão 4 é usado somente quando o código chamador omite start; ele não substitui um argumento explícito.

Questão 6 de 10

Por que a última linha falha?

class Lamp:
    def __init__(self, brightness):
        brightness = brightness
lamp = Lamp(7)
print(lamp.brightness)
  1. A atribuição altera apenas um parâmetro local.
  2. A chamada da classe deixa o parâmetro indefinido.
  3. O atributo é armazenado na classe.
  4. O inicializador deve retornar o brilho.
Mostrar respostaOcultar resposta
Resposta: A
brightness = brightness reatribui o parâmetro local e nunca cria um atributo na lâmpada. Portanto, a leitura provoca AttributeError; a configuração deve armazenar self.brightness = brightness. Retornar o número de __init__ causaria outro TypeError, não criaria o atributo.

Questão 7 de 10

O que este código imprime?

class Folder:
    def __init__(self, tags=[]):
        self.tags = tags
a = Folder()
b = Folder()
a.tags.append("urgent")
a.tags = []
print(b.tags, a.tags)
  1. [] []
  2. ['urgent'] ['urgent']
  3. [] ['urgent']
  4. ['urgent'] []
Mostrar respostaOcultar resposta
Resposta: D
Os argumentos omitidos inicialmente fazem ambos os objetos armazenarem a mesma lista padrão. A adição coloca "urgent" nessa lista compartilhada; a atribuição posterior dá apenas a a uma nova lista vazia. Esperar duas listas vazias confunde substituir a referência de um atributo com esvaziar o objeto original compartilhado.

Questão 8 de 10

O que este código imprime?

class Ticket:
    fee = 3
x = Ticket()
y = Ticket()
x.fee = 8
Ticket.fee = 5
print(x.fee, y.fee)
  1. 5 5
  2. 8 5
  3. 8 3
  4. 3 5
Mostrar respostaOcultar resposta
Resposta: B
Atribuir a x.fee cria um atributo de instância que sombreia o valor da classe. y não tem uma taxa própria, então, após a atualização da classe, encontra 5 em Ticket. Esperar 5 5 ignora o valor separado armazenado em x, que a atribuição à classe não substitui.

Questão 9 de 10

O que este código imprime?

class Job:
    count = 0
    def __init__(self):
        self.count += 1
a = Job()
Job.count = 10
b = Job()
print(a.count, b.count, Job.count)
  1. 1 11 10
  2. 11 11 10
  3. 1 1 10
  4. 1 11 11
Mostrar respostaOcultar resposta
Resposta: A
Criar a lê a contagem de classe 0 e armazena a contagem de instância 1. Depois que a classe muda para 10, criar b armazena seu próprio 11; nenhum dos inicializadores atualiza Job.count. O resultado 1 11 11 trata a atribuição aumentada na instância como uma atualização do contador de classe.

Questão 10 de 10

O que este código imprime?

class Batch:
    def __init__(self, size):
        if size <= 0:
            raise ValueError("size must be positive")
        self.size = size
batch = Batch(6)
try:
    batch = Batch(0)
except ValueError:
    pass
print(batch.size)
  1. 0
  2. None
  3. 6
  4. ValueError
Mostrar respostaOcultar resposta
Resposta: C
A chamada Batch(0) lança uma exceção antes de retornar, então a atribuição a batch nunca acontece. A exceção é capturada, e batch ainda se refere ao objeto válido anterior, com tamanho 6. Uma chamada de criação que falha não substitui a referência existente do código chamador por um resultado parcial.
Conjunto 2: Métodos, mudanças de estado e propriedades10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Qual argumento o Python fornece como self quando o programa chama panel.area()?
  1. O objeto de classe Panel.
  2. O objeto armazenado em panel.
  3. O valor retornado por area.
  4. O primeiro atributo em panel.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Qual alteração permite que o código chamador calcule double?

class Pace:
    def __init__(self, minutes):
        self.minutes = minutes
    def seconds(self):
        print(self.minutes * 60)
p = Pace(3)
double = p.seconds() * 2
  1. Chamar p.seconds sem parênteses.
  2. Passar p explicitamente em p.seconds(p).
  3. Retornar self do inicializador.
  4. Retornar o produto dentro de seconds.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Um construtor exige uma lista de leituras e rejeita qualquer outro tipo antes de armazená-la. Qual exceção descreve melhor o caso de o código chamador passar a string "12,14"?
  1. TypeError
  2. ValueError
  3. AttributeError
  4. AssertionError
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

O que este código imprime?

class Tank:
    def __init__(self):
        self.level = 5
    def add(self, amount):
        self.level += amount
a = Tank()
b = a
result = b.add(4)
print(a.level, result)
  1. 5 None
  2. 9 9
  3. 9 None
  4. 5 9
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

O que este código imprime?

import copy
class Log:
    def __init__(self):
        self.entries = []
a = Log()
b = copy.copy(a)
b.entries.append("open")
b.entries = ["closed"]
print(a.entries, b.entries, a is b)
  1. [] ['closed'] False
  2. ['closed'] ['closed'] True
  3. ['open'] ['open'] False
  4. ['open'] ['closed'] False
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Uma sessão rejeita nomes que ficam em branco após a remoção dos espaços das extremidades. Qual corpo de inicializador aplica essa regra a name?
  1. if not name: raise ValueError("name required")

    self.name = name.strip()

  2. name = name.strip()

    if not name: raise ValueError("name required")

    self.name = name

  3. self.name = name.strip()

    if not name: raise ValueError("name required")

  4. if name == "": raise ValueError("name required")

    self.name = name

Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Por que atribuir a reading.value chama repetidamente este método de definição?

class Reading:
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, number):
        self.value = number
  1. A atribuição usa novamente o nome da propriedade.
  2. O método de leitura lê outro nome de atributo.
  3. O método de definição precisa de um retorno explícito.
  4. O decorador deve nomear a classe.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um contador deve permanecer não negativo. Seu método remove(n) deve rejeitar quantidades não positivas e retiradas maiores que o valor atual. Qual sequência deixa o estado inalterado em qualquer uma das rejeições?
  1. Subtrair n; verificar seu sinal; verificar o novo valor.
  2. Verificar o sinal; subtrair n; verificar o novo valor.
  3. Verificar o sinal; verificar o valor disponível; subtrair n.
  4. Verificar o valor disponível; subtrair n; verificar o sinal.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

O que este código imprime?

class Dial:
    def __init__(self, value):
        self.value = value
    @property
    def value(self):
        return self._value
    @value.setter
    def value(self, number):
        if not 0 <= number <= 9:
            raise ValueError("out of range")
        self._value = number
d = Dial(4)
try:
    d.value = 12
except ValueError:
    pass
print(d.value)
  1. 12
  2. 4
  3. 9
  4. None
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Uma classe calcula area com uma propriedade somente leitura a partir de width e height. Um objeto tem largura 6 e altura 2; depois, é atribuído 8 à sua largura. O que a leitura de obj.area retorna?
  1. 12
  2. 6
  3. 16
  4. 8
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 3: Exibições legíveis e operadores úteis10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Uma classe define tanto __str__ quanto __repr__. Qual método fornece o texto de uma instância quando ela aparece dentro de uma lista impressa?
  1. __str__
  2. __init__
  3. __len__
  4. __repr__
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

O que este código imprime?

class Tag:
    def __init__(self, text):
        self.text = text
    def __repr__(self):
        return f"Tag({self.text!r})"
t = Tag("ok")
print(t)
  1. Tag('ok')
  2. ok
  3. Tag(ok)
  4. 'ok'
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Por que str(marker) provoca TypeError quando seu método __str__ apenas chama print(self.code)?
  1. O nome do método tem sublinhados demais.
  2. O código impresso deve usar aspas simples.
  3. O método retorna None em vez de uma string.
  4. O código chamador deve chamar marker.__str__() diretamente.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

Uma classe mutável Label define igualdade pelo texto e não define __hash__. Duas etiquetas contêm o mesmo texto. O que acontece quando são colocadas em um conjunto?
  1. Elas formam duas entradas porque suas identidades diferem.
  2. Criar o conjunto provoca um erro de tipo não hashável.
  3. Elas formam uma entrada porque seus textos são iguais.
  4. Criar o conjunto converte seus textos em chaves.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Uma classe Mark define apenas __lt__, comparando notas numéricas, e nenhuma outra comparação personalizada. Para a com nota 2 e b com nota 7, o que acontece?
  1. a > b é falso; a <= b é verdadeiro.
  2. a > b provoca uma exceção; a <= b é verdadeiro.
  3. a > b provoca uma exceção; a <= b também.
  4. a > b é falso; a <= b provoca uma exceção.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Um Vector define __mul__ para vector * number, mas não tem um método de multiplicação refletida. Qual método deve ser adicionado para permitir também number * vector?
  1. __rmul__
  2. __add__
  3. __lt__
  4. __len__
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Uma classe define __eq__ usando code e __hash__ como hash(self.code). Seus objetos são usados como chaves de dicionário. Qual alteração de projeto torna confiável o comportamento das chaves?
  1. Alterar o hash sempre que uma consulta falhar.
  2. Usar id(self) como hash para todo objeto.
  3. Manter code inalterado enquanto um objeto for chave.
  4. Comparar a identidade mantendo o hash de code.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Uma classe Queue define __len__ como o número de tarefas pendentes e não define __bool__. Sem tarefas pendentes, quais são os valores de len(q) e bool(q)?
  1. 0 e True
  2. 0 e False
  3. None e False
  4. 1 e True
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

O que a última linha imprime?

class Offset:
    def __init__(self, value):
        self.value = value
    def __add__(self, other):
        return Offset(self.value + other.value)
a = Offset(6)
b = a
a = a + Offset(4)
print(a.value, b.value, a is b)
  1. 10 6 False
  2. 10 10 True
  3. 6 10 False
  4. 10 10 False
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

Rank.__lt__ ordena registros por prioridade. Um relatório deve ordenar esses registros por momento de criação sem mudar sua ordem usual. Qual abordagem é adequada?
  1. Substituir __lt__ por uma comparação de tempo para todos os registros.
  2. Dar a cada registro uma nova classe para a ordem por tempo do relatório.
  3. Alterar primeiro a prioridade de cada registro para seu momento de criação.
  4. Chamar sorted com uma função de chave que retorne o momento de criação.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 4: Objetos que colaboram e projetos claros10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Uma Workshop armazena objetos Tool em self.tools. O que workshop.tools[0].label lê?
  1. A identificação da própria oficina.
  2. As identificações de todas as ferramentas armazenadas.
  3. A identificação da primeira ferramenta armazenada.
  4. O número de identificações armazenadas.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

Uma cesta calcula seu total chamando line.cost() em cada linha armazenada. Um desconto por quantidade muda a forma de calcular o custo de cada linha. Onde essa regra deve ficar?
  1. Na inicialização da lista da cesta.
  2. No método cost da classe de linha.
  3. Em todo código chamador que imprime uma cesta.
  4. No laço da cesta após cada chamada.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

Qual total o código imprime?

class Line:
    def __init__(self, rate, count):
        self.rate = rate
        self.count = count
    def cost(self):
        return self.rate * self.count
x = Line(4, 3)
a = [x]
b = [x, Line(2, 5)]
a[0].count = 6
print(sum(line.cost() for line in b))
  1. 22
  2. 34
  3. 24
  4. 28
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

O método find(name) de uma coleção retorna um objeto em caso de sucesso e None em caso de falha. Qual código chamador trata ambos os casos antes de ler result.name?
  1. Ler result.name e depois verificar result is None.
  2. Usar if result.name: antes de imprimir o nome.
  3. Usar if type(result) is None: antes da leitura.
  4. Usar if result is not None: antes da leitura.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Um método Position.midpoint(other) retorna uma nova posição cujas coordenadas são as médias. Se a está em (2, 8) e b em (10, 4), qual é o resultado de c = a.midpoint(b)?
  1. c em (6, 6); a e b inalterados.
  2. a em (6, 6); c refere-se a a.
  3. b em (6, 6); c refere-se a b.
  4. c em (12, 12); a e b inalterados.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

send_to(other, n) chama self.remove(n) e depois other.add(n). remove rejeita uma quantidade maior que o estoque do remetente antes de alterar qualquer coisa; add aceita quantidades positivas. Começando com estoque 9 no remetente e 4 no destinatário, um envio de 3 é bem-sucedido, depois tenta-se enviar 8 e a exceção é capturada. Quais estoques restam?
  1. Remetente 9, destinatário 4.
  2. Remetente 6, destinatário 15.
  3. Remetente 6, destinatário 7.
  4. Remetente -2, destinatário 15.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Uma transferência subtrai estoque do remetente e depois chama o método add do destinatário. O destinatário rejeita a quantidade antes de alterar seu próprio estoque. Não há código de reversão, e o código chamador captura a exceção. Qual estado permanece?
  1. Remetente inalterado; destinatário inalterado.
  2. Remetente reduzido; destinatário inalterado.
  3. Remetente reduzido; destinatário aumentado.
  4. Remetente inalterado; destinatário aumentado.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

Um script converte uma temperatura de entrada e imprime o resultado uma vez. Ele não precisa de estado armazenado nem de comportamento repetido de objetos. Qual estrutura é mais clara?
  1. Uma classe para cada temperatura possível.
  2. Uma classe que contém cada etapa aritmética.
  3. Vários objetos com um resultado compartilhado.
  4. Uma função que retorna a conversão.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Um sistema de aluguel tem muitas bicicletas. Cada bicicleta tem uma identificação e um estado de disponibilidade, além das mesmas ações de aluguel e devolução com verificações. Qual é um plano de classe útil?
  1. Uma classe Bike com estado e métodos com verificações.
  2. Uma classe para cada identificação com código de ação copiado.
  3. Um objeto de bicicleta com todas as identificações como referências alternativas.
  4. Uma classe que contém apenas instruções de impressão.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

A estante deve rejeitar um terceiro livro e manter seus dois livros existentes. Após o erro capturado abaixo, qual asserção revela a implementação defeituosa?

class Shelf:
    def __init__(self):
        self.limit = 2
        self.books = []
    def add(self, title):
        self.books.append(title)
        if len(self.books) > self.limit:
            raise ValueError("shelf full")
shelf = Shelf()
shelf.add("Atlas")
shelf.add("Birch")
try:
    shelf.add("Cove")
except ValueError:
    pass
  1. assert len(shelf.books) == 3
  2. assert shelf.limit == 2
  3. assert len(shelf.books) == 2
  4. assert shelf.books[-1] == "Cove"
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Conjunto 5: Aplique as ideias em novas situações10 Questões de Prática

Salve esta aula para ver todas as respostas e praticar cada conjunto.

Questão 1 de 10

Para uma lista embutida items, o que isinstance(items, list) retorna?
  1. O número de itens da lista.
  2. Os nomes de seus métodos de lista.
  3. O objeto de classe chamado list.
  4. O valor booleano True.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 2 de 10

O que este código imprime?

class Tray:
    labels = []
a = Tray()
b = Tray()
a.labels.append("red")
b.labels = ["blue"]
Tray.labels.append("green")
print(a.labels, b.labels)
  1. ['red', 'green'] ['blue']
  2. ['red', 'green'] ['blue', 'green']
  3. ['red'] ['blue', 'green']
  4. ['blue', 'green'] ['blue', 'green']
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 3 de 10

O que este código imprime?

class Kit:
    def __init__(self, parts=None):
        self.parts = [] if parts is None else list(parts)
source = [[1]]
k = Kit(source)
source.append([2])
source[0].append(3)
print(k.parts)
  1. [[1]]
  2. [[1, 3], [2]]
  3. [[1, 3]]
  4. [[1], [2]]
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 4 de 10

O método de definição de um medidor rejeita valores negativos antes de armazená-los em _reading. Seu inicializador atribui diretamente a self._reading, então Meter(-2) funciona. Qual alteração faz a criação usar a mesma verificação das atribuições públicas posteriores?
  1. Adicionar uma instrução de retorno ao método de leitura.
  2. Atribuir self.reading = reading em __init__.
  3. Atribuir self.reading = reading dentro do método de definição.
  4. Remover a propriedade e manter _reading público.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 5 de 10

Uma Key compara igualdade apenas por name, e seu hash também usa name. Duas instâncias diferentes começam com o nome "north"; uma se torna chave de dicionário. Por que o nome dessa chave deve permanecer estável?
  1. Alterar um nome muda as identidades dos dois objetos.
  2. Todos os atributos de instância devem ser imutáveis em Python.
  3. Chaves de dicionário são copiadas automaticamente na inserção.
  4. Um hash alterado pode impedir a busca da chave armazenada.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 6 de 10

Dois clubes têm, cada um, uma lista members separada, mas ambas as listas contêm a mesma instância de Member. Um clube altera o nome desse membro. Agora os clubes precisam de nomes de membros independentes. Qual alteração é necessária?
  1. Criar um objeto de membro separado para cada clube.
  2. Copiar apenas uma das listas externas members.
  3. Atribuir outra variável ao membro compartilhado.
  4. Mover members para o corpo da classe do clube.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 7 de 10

Uma classe de mensagem tem um __str__ útil, mas uma lista impressa de mensagens ainda mostra descrições padrão de objetos. Qual adição melhora essas entradas da lista?
  1. Um __len__ que retorna o comprimento do texto.
  2. Um __init__ que retorna o valor do texto.
  3. Um __repr__ que retorna texto útil da mensagem.
  4. Uma chamada extra a print dentro de __str__.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 8 de 10

a.combine(b) deve deixar ambas as entradas inalteradas. Seu corpo chama self.parts.extend(other.parts) e depois retorna self. Qual comportamento essa implementação realmente tem?
  1. Ela cria um novo contêiner com partes copiadas.
  2. Ela altera a e retorna esse mesmo objeto.
  3. Ela altera b e retorna esse mesmo objeto.
  4. Ela deixa ambos inalterados e retorna None.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 9 de 10

Dentro de class Vault, a configuração atribui self.__code = "x". O que explica por que vault.__code falha enquanto vault._Vault__code pode funcionar?
  1. O código é criptografado durante a inicialização.
  2. Os sublinhados tornam o valor um atributo de classe.
  3. O método de leitura só é chamado com parênteses.
  4. A transformação de nome altera o nome do atributo armazenado.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Questão 10 de 10

O método take de uma classe usa assert n > 0 como única verificação da entrada do código chamador. A regra deve valer mesmo quando o Python é executado com otimização ativada. Qual alteração é necessária?
  1. Retornar o resultado da asserção ao código chamador.
  2. Mover a asserção para o laço chamador.
  3. Usar uma condição explícita que lance uma exceção.
  4. Colocar a asserção após a atualização do estado.
Mostrar respostaOcultar resposta

Salve esta aula para ver esta resposta.

Travou em uma questão sua? Ajuda com Tarefas

Termos-Chave

62 Termos-Chave, cada um com sua definição

A

Argumento padrão
Um argumento padrão é um valor usado quando o código chamador omite um argumento; ele é avaliado quando a função ou o método é definido.
Com balance=0 no inicializador, Account("Rae") começa com zero sem fornecer um argumento de saldo.
Asserção
Uma asserção é uma verificação de desenvolvimento que provoca AssertionError se sua condição for falsa; o Python pode omiti-la na execução otimizada.
Após um depósito, assert acct.balance == 75 verifica o comportamento; entradas rejeitadas do código chamador ainda precisam de validação explícita.
Atributo
Um atributo é um valor nomeado acessado por meio de um objeto ou classe com a notação com ponto, como o título armazenado de um livro.
Atribuir book.title = "Emma" armazena um título, que pode ser lido avaliando book.title.
Atributo de armazenamento
Um atributo de armazenamento guarda o valor gerenciado por uma propriedade sob um nome separado, evitando o acesso recursivo à propriedade.
Um método de leitura de score lê _score; armazenar em score dentro de seu próprio método de definição o chamaria repetidamente.
Atributo de classe
Um atributo de classe é armazenado na classe e pode ser encontrado por instâncias que não têm um atributo de mesmo nome que o sombreie.
Uma configuração MAX_TOPPINGS de toda a classe pode fornecer o limite padrão lido por meio de vários objetos de pizza.
Atributo de instância
Um atributo de instância é armazenado em um objeto específico, dando a essa instância seus próprios dados sob o nome do atributo.
Duas pizzas podem armazenar, cada uma, um size diferente e uma lista de ingredientes separada por meio de atribuições em self.
Atributo interno
Um atributo interno destina-se ao uso dentro da implementação de uma classe, geralmente indicado por um sublinhado inicial em seu nome.
Em uma classe de conta, o código chamador lê uma propriedade pública balance, enquanto os próprios métodos da classe gerenciam o _balance armazenado.

B

Busca de atributo
A busca de atributo é o processo de encontrar um atributo pelo nome; dados comuns da instância podem ser encontrados antes de um valor da classe.
Se p não tiver um limit próprio, ler p.limit pode encontrar o valor compartilhado limit definido em sua classe.

C

Classe
Uma classe define um tipo Python, fornecendo uma definição comum para os atributos e o comportamento de suas instâncias.
Uma classe Book pode criar muitos livros, cada um armazenando um título diferente e compartilhando as definições de métodos.
Composição
A composição constrói um objeto maior a partir de outros objetos armazenados em seus atributos ou coleções, modelando uma relação de posse.
Uma biblioteca pode armazenar objetos de livro, com cada livro mantendo seu título e informações sobre quem o tomou emprestado atualmente.
Condição de execução principal
Uma condição de execução principal verifica se um módulo está sendo executado diretamente, impedindo que ações de inicialização sejam executadas quando o módulo é importado.
Coloque main() sob if __name__ == "__main__": para que outro programa possa importar as classes sem iniciá-lo.
Contêiner
Um contêiner é um objeto que contém referências a outros objetos, permitindo que sejam armazenados e processados juntos.
A lista de itens de um carrinho contém objetos de item; acessar cart.items[0] alcança seu primeiro item armazenado.
Cópia profunda
Uma cópia profunda copia um objeto e seus objetos aninhados, separando o estado mutável aninhado que uma cópia superficial pode deixar compartilhado.
Se uma conta armazena uma lista de transações, copy.deepcopy(account) também copia essa lista, então adicionar um item ao histórico da cópia deixa o original intacto.
Cópia superficial
Uma cópia superficial cria um novo objeto externo mantendo referências aos objetos aninhados existentes, que podem permanecer compartilhados.
Se uma conta armazena uma lista de transações, a conta criada por copy.copy(account) ainda pode compartilhar essa lista com a original.

D

Delegação
A delegação passa parte de uma tarefa ao método de outro objeto, permitindo que esse objeto aplique as regras para seus próprios dados.
Um carrinho delega cada subtotal ao seu item e depois soma os valores retornados ao total do carrinho.

E

Encapsulamento
O encapsulamento mantém juntos os dados de um objeto e o comportamento que os gerencia, dando ao código chamador uma interface clara para alterações válidas.
O método de depósito de uma conta verifica o valor e atualiza o armazenamento interno sem que o código chamador repita a regra.
Estado
O estado de um objeto é o conjunto atual de dados armazenados em seus atributos, que os métodos podem inspecionar e atualizar durante a execução do programa.
Após um depósito aceito em uma conta que mantém um histórico, o estado da conta inclui tanto seu novo saldo quanto a entrada adicionada ao histórico.

F

Função de chave
Uma função de chave extrai o valor que uma operação de ordenação deve comparar para cada item, selecionando uma ordem para essa tarefa específica.
Um relatório pode chamar sorted(records, key=by_date) sem alterar o método usual de comparação da classe de registro.

H

Hashabilidade
A hashabilidade permite que um objeto sirva como elemento de conjunto ou chave de dicionário usando um hash estável e consistente com sua regra de igualdade.
Uma classe que define __eq__ sem __hash__ torna-se não hashável, mesmo que suas instâncias funcionem em uma lista.

I

Identidade
Identidade distingue um objeto de todos os outros; o operador is testa se duas referências acessam esse mesmo objeto.
Duas listas criadas separadamente podem satisfazer a == b enquanto a is b é falso, pois suas identidades diferem.
Igualdade de valores
A igualdade de valores compara objetos por dados significativos, geralmente definidos com __eq__, em vez de exigir a mesma identidade de objeto.
Dois pontos separados nas mesmas coordenadas podem ser considerados iguais e corresponder em um teste de pertencimento a uma lista.
Inicialização
A inicialização define o estado inicial de um objeto recém-criado, geralmente armazenando argumentos e valores iniciais como atributos.
Um inicializador de conta pode armazenar seu titular, aceitar um saldo inicial e criar uma lista de transações vazia.
Instância
Uma instância é um objeto que pertence a uma classe e armazena o estado específico associado a esse objeto.
As chamadas Book() e Book() criam duas instâncias de livro separadas, embora ambas tenham o mesmo tipo.
Invariante
Uma invariante é uma regra que deve permanecer verdadeira para que o estado de um objeto seja válido, inclusive após alterações bem-sucedidas.
Uma classe de notas pode exigir que toda nota permaneça entre 0 e 100 durante a configuração e em atribuições posteriores.

M

Método
Um método é uma função fornecida por uma classe que pode ser chamada em um objeto para trabalhar com esse objeto ou seus dados.
Chamar "cedar".upper() executa um método de string, enquanto scores.append(7) executa um método em uma lista.
Método de acesso
Um método de acesso é um método que informa dados sobre o estado de um objeto sem alterá-lo intencionalmente.
O método de acesso area() de um retângulo lê sua largura e altura e retorna um número sem redimensioná-lo.
Método de alteração
Um método de alteração é um método que altera o estado de um objeto existente, muitas vezes verificando entradas antes de atualizar atributos relacionados.
Um método de alteração para saque com verificação pode reduzir o saldo e registrar uma transação somente depois que o valor passar pela validação.
Método de definição
Um método de definição é o método chamado pela atribuição a uma propriedade, muitas vezes validando o valor proposto antes de armazená-lo separadamente.
Um método de definição de score que rejeita 105 antes de atribuir a _score deixa uma nota anteriormente válida inalterada.
Método de instância
Um método de instância é uma função definida em uma classe que recebe o objeto no qual foi chamada, por convenção por meio de seu primeiro parâmetro, self.
Para r.area(), o Python fornece r ao método para que ele possa multiplicar a largura e a altura desse retângulo.
Método especial
Um método especial tem um nome com dois sublinhados de cada lado e fornece comportamento para a sintaxe do Python ou operações embutidas.
O Python solicita __str__ ao converter um objeto em sua forma legível como string e __len__ para seu comprimento.
Método vinculado
Um método vinculado é um objeto de método que guarda tanto a função quanto a instância a ser fornecida quando o método for chamado.
r.area obtém um método vinculado; adicionar parênteses, como em r.area(), executa-o e retorna a área calculada.
Mutabilidade
Mutabilidade é a capacidade de um objeto de alterar seu conteúdo enquanto permanece o mesmo objeto, com a mesma identidade.
Adicionar um item a uma lista altera a lista existente; aplicar upper() a uma string retorna uma nova string.

N

None
None é o valor único do Python para um resultado ausente ou indisponível, também útil para uma relação à qual nenhum objeto foi atribuído ainda.
Um livro pode armazenar borrower = None até que um membro o tome emprestado, então o código verifica antes de ler o nome dessa pessoa.
Notação com ponto
A notação com ponto é a sintaxe que coloca um ponto entre um objeto ou classe e o nome de um atributo acessado.
Em cart.items, o nome antes do ponto identifica o objeto e o nome depois dele seleciona seu atributo de itens.
NotImplemented
NotImplemented é um valor de retorno especial que indica que um método de comparação ou numérico não aceita os operandos fornecidos.
O método de igualdade de um ponto pode retornar NotImplemented para uma string, deixando o Python lidar com o par não aceito.

O

Objeto
Um objeto é um valor Python com identidade, tipo e estado, juntamente com as operações aceitas por seu tipo.
A string "hello" e a lista [2, 5] são objetos, embora seus tipos ofereçam operações diferentes.
Objeto passado como argumento
Um objeto passado como argumento fornece uma referência a uma instância a uma função ou método, permitindo que o código chamado use esse objeto.
O parâmetro other em p.midpoint(q) se refere a q, cujas coordenadas o método lê para calcular seu resultado.
Operação refletida
Uma operação refletida permite que o operando à direita trate uma operação quando o operando à esquerda não consegue lidar com o par.
Para 3 * vector, o Python pode usar o __rmul__ do vetor quando a multiplicação de inteiros não consegue lidar com esse vetor.

P

Projeto de classe
O projeto de classe escolhe os tipos, estados, ações e regras de validade necessários para modelar claramente um problema com objetos que colaboram.
Planejar uma biblioteca começa com livros e membros e depois coloca um limite de empréstimos em uma operação de empréstimo.
Propriedade
Uma propriedade fornece acesso controlado por métodos pela sintaxe de atributo, permitindo que uma leitura calcule um resultado ou uma atribuição execute verificações.
Ler student.score pode chamar um método de leitura, e atribuir student.score = 90 pode chamar um método de definição que valida o valor.

R

Receptor
O receptor é o objeto antes do ponto em uma chamada de método de instância, passado automaticamente como argumento self do método.
Em sender.transfer_to(receiver, 5), sender recebe a chamada de transferência; o argumento de conta é o destino.
Referência
Uma referência conecta um nome ou valor armazenado a um objeto, permitindo que vários nomes ou contêineres acessem o mesmo objeto.
Colocar um item em duas listas de carrinhos compartilha esse item, então alterar seu preço pode afetar ambos os totais calculados.
Referência alternativa
Uma referência alternativa é outro nome que se refere a um objeto existente, de modo que alterações em um objeto mutável compartilhado podem ser vistas por qualquer um dos nomes.
Depois de backup = entries, adicionar um item por meio de backup também altera a lista acessada por meio de entries.
Representação alternativa
A representação alternativa permite que a conversão padrão para string use um __repr__ personalizado quando uma classe não define um __str__ personalizado.
Adicionar apenas __repr__ pode melhorar tanto print(point) quanto a aparência do ponto dentro de uma lista impressa.
Representação em string
Uma representação em string é o texto legível retornado por __str__, normalmente destinado aos usuários do programa.
Um ponto pode ser exibido como "(3, 4)" quando impresso diretamente, deixando seus atributos numéricos de coordenadas inalterados.
Representação para o programador
Uma representação para o programador é o texto inequívoco retornado por __repr__, usado para inspeção e dentro de exibições de contêineres.
A representação de um livro pode mostrar "Book('Emma', 'Jane Austen', 1815)", incluindo campos de texto entre aspas.
Responsabilidade
A responsabilidade de uma classe é a função clara à qual seus dados e métodos servem, ajudando a decidir quais comportamentos pertencem uns aos outros.
Um item calcula seu próprio subtotal, enquanto um carrinho organiza os itens e soma esses subtotais em um total.
Resultado da busca
Um resultado da busca pode ser o objeto correspondente existente ou um valor especial como None, indicando que nenhuma correspondência foi encontrada.
O código chamador verifica found is not None antes de ler found.name de uma busca de item no carrinho.

S

self
O parâmetro self recebe, por convenção, a instância atual, permitindo que um método leia ou altere os atributos desse objeto.
Em self.owner = owner, o lado esquerdo armazena dados na instância e o lado direito lê um parâmetro local.
Sobrecarga de operadores
A sobrecarga de operadores define o significado de um operador para uma classe por meio da implementação do método especial associado a essa operação.
O __add__ de um vetor pode fazer a + b produzir um novo vetor formado pela soma de suas coordenadas correspondentes.
Sombreamento
O sombreamento ocorre quando um atributo de dados comum da própria instância oculta um atributo de classe com o mesmo nome durante a busca.
Depois de p.limit = 8, alterar Pizza.limit não substitui o limite separado já armazenado em p.

T

Teste de comportamento
Um teste de comportamento cria objetos, realiza uma ação e verifica um resultado observável, incluindo o estado preservado após uma entrada rejeitada.
Um teste de saque captura o erro esperado e verifica se o saldo e o histórico ainda correspondem aos valores anteriores.
Tipo
Um tipo é a classe de um objeto, que define quais operações ele aceita e como os valores desse tipo se comportam.
type([2, 5]) retorna a classe list, e isinstance([2, 5], list) confirma que o valor é uma lista.
Transformação de nome
A transformação de nome reescreve nomes no corpo da classe com dois sublinhados iniciais e sem dois sublinhados finais para incluir o nome da classe.
Dentro de Acc, self.__balance torna-se _Acc__balance, que continua acessível pelo nome transformado.
TypeError
Um TypeError informa que uma operação recebeu um tipo inadequado ou foi chamada com argumentos que sua definição não aceita.
Passar texto a uma comparação numérica de largura provoca TypeError porque essa comparação não pode ordenar texto e zero.

V

Validação
A validação verifica se dados recebidos ou uma alteração solicitada satisfazem as regras exigidas por uma classe antes de aceitá-los.
Um retângulo valida uma largura numérica rejeitando zero ou valores negativos antes de armazenar a dimensão.
Valor de retorno
Um valor de retorno é o objeto que uma chamada fornece à expressão que a contém, disponível para atribuição, cálculo ou teste.
Retornar a área permite que r1.area() + r2.area() some resultados, enquanto apenas imprimir deixa None como cada resultado.
Valor lógico em contexto
O valor lógico em contexto é a interpretação booleana de um objeto em uma condição; sem __bool__, um comprimento definido como zero o torna falso.
Uma lista de reprodução vazia com __len__ e sem __bool__ personalizado faz if playlist: ignorar seu corpo.
Valor padrão mutável
Um valor padrão mutável é um objeto padrão que pode ser alterado e reutilizado entre chamadas, causando compartilhamento indesejado se for armazenado ou alterado.
Se items=[] for armazenado diretamente, dois objetos criados sem esse argumento podem se referir à mesma lista.
ValueError
Um ValueError informa que um argumento tem um tipo aceitável, mas seu valor está fora do que a operação aceita.
Uma largura numérica de -2 pode provocar ValueError com a mensagem "width must be positive".

#

__init__
O método __init__ inicializa uma instância depois que o Python a cria e deve retornar None quando a inicialização termina.
Chamar Counter(5) passa a nova instância e 5 ao inicializador antes de retornar o contador configurado.
__lt__
O método __lt__ implementa a comparação menor que, fornecendo uma ordenação que operações como sorted podem usar.
Um __lt__ baseado em notas permite que uma lista de objetos de alunos seja ordenada da menor para a maior nota.

Sobre esta aula

Por StudeliRelatar um erro

Crie seus próprios Materiais de Estudo

Transforme suas anotações, slides e documentos em Guias de Estudo, Questões de Prática, Flashcards e mais 6 ferramentas.

Começar