Criando Novos Objetos com Classes

Beginner

This tutorial is from open-source community. Access the source code

Introdução

Esta seção introduz a declaração de classe (class statement) e a ideia de criar novos objetos.

Este é um Lab Guiado, que fornece instruções passo a passo para ajudá-lo a aprender e praticar. Siga as instruções cuidadosamente para completar cada etapa e ganhar experiência prática. Dados históricos mostram que este é um laboratório de nível intermediário com uma taxa de conclusão de 73%. Recebeu uma taxa de avaliações positivas de 100% dos estudantes.

Programação Orientada a Objetos (POO) (Object Oriented (OO) programming)

Uma técnica de programação onde o código é organizado como uma coleção de objetos.

Um objeto consiste em:

  • Dados. Atributos
  • Comportamento. Métodos, que são funções aplicadas ao objeto.

Você já utilizou alguma POO durante este curso.

Por exemplo, ao manipular uma lista.

>>> nums = [1, 2, 3]
>>> nums.append(4)      ## Method
>>> nums.insert(1,10)   ## Method
>>> nums
[1, 10, 2, 3, 4]        ## Data
>>>

nums é uma instância de uma lista.

Os métodos (append() e insert()) estão anexados à instância (nums).

A declaração class

Use a declaração class para definir um novo objeto.

class Player:
    def __init__(self, x, y):
        self.x = x
        self.y = y
        self.health = 100

    def move(self, dx, dy):
        self.x += dx
        self.y += dy

    def damage(self, pts):
        self.health -= pts

Em poucas palavras, uma classe é um conjunto de funções que realizam várias operações nas chamadas instâncias.

Instâncias (Instances)

Instâncias são os objetos reais que você manipula em seu programa.

Elas são criadas chamando a classe como uma função.

>>> a = Player(2, 3)
>>> b = Player(10, 20)
>>>

a e b são instâncias de Player.

Enfatizando: A declaração da classe é apenas a definição (ela não faz nada por si só). Semelhante a uma definição de função.

Dados da Instância (Instance Data)

Cada instância tem seus próprios dados locais.

>>> a.x
2
>>> b.x
10

Esses dados são inicializados pelo __init__().

class Player:
    def __init__(self, x, y):
        ## Qualquer valor armazenado em `self` é dado da instância
        self.x = x
        self.y = y
        self.health = 100

Não há restrições sobre o número total ou o tipo de atributos armazenados.

Métodos de Instância (Instance Methods)

Métodos de instância são funções aplicadas a instâncias de um objeto.

class Player:
    ...
    ## `move` é um método
    def move(self, dx, dy):
        self.x += dx
        self.y += dy

O próprio objeto é sempre passado como o primeiro argumento.

>>> a.move(1, 2)

## corresponde `a` a `self`
## corresponde `1` a `dx`
## corresponde `2` a `dy`
def move(self, dx, dy):

Por convenção, a instância é chamada de self. No entanto, o nome real usado não é importante. O objeto é sempre passado como o primeiro argumento. É apenas um estilo de programação Python chamar este argumento de self.

Escopo de Classe (Class Scoping)

Classes não definem um escopo de nomes.

class Player:
    ...
    def move(self, dx, dy):
        self.x += dx
        self.y += dy

    def left(self, amt):
        move(-amt, 0)       ## NÃO. Chama uma função global `move`
        self.move(-amt, 0)  ## SIM. Chama o método `move` de cima.

Se você deseja operar em uma instância, você sempre se refere a ela explicitamente (por exemplo, self).

Começando com este conjunto de exercícios, começamos a fazer uma série de mudanças no código existente das seções anteriores. É fundamental que você tenha uma versão funcional do Exercício 3.18 para começar. Se você não tiver isso, trabalhe a partir do código da solução encontrado no diretório Solutions/3_18. É bom copiá-lo.

Exercício 4.1: Objetos como Estruturas de Dados

Nas seções 2 e 3, trabalhamos com dados representados como tuplas e dicionários. Por exemplo, uma participação em ações poderia ser representada como uma tupla assim:

s = ('GOOG',100,490.10)

ou como um dicionário assim:

s = { 'name'   : 'GOOG',
      'shares' : 100,
      'price'  : 490.10
}

Você pode até escrever funções para manipular esses dados. Por exemplo:

def cost(s):
    return s['shares'] * s['price']

No entanto, à medida que seu programa cresce, você pode querer criar um senso melhor de organização. Assim, outra abordagem para representar dados seria definir uma classe. Crie um arquivo chamado stock.py e defina uma classe Stock que represente uma única participação em ações. Faça com que as instâncias de Stock tenham atributos name, shares e price. Por exemplo:

>>> import stock
>>> a = stock.Stock('GOOG',100,490.10)
>>> a.name
'GOOG'
>>> a.shares
100
>>> a.price
490.1
>>>

Crie mais alguns objetos Stock e manipule-os. Por exemplo:

>>> b = stock.Stock('AAPL', 50, 122.34)
>>> c = stock.Stock('IBM', 75, 91.75)
>>> b.shares * b.price
6117.0
>>> c.shares * c.price
6881.25
>>> stocks = [a, b, c]
>>> stocks
[<stock.Stock object at 0x37d0b0>, <stock.Stock object at 0x37d110>, <stock.Stock object at 0x37d050>]
>>> for s in stocks:
     print(f'{s.name:>10s} {s.shares:>10d} {s.price:>10.2f}')

... veja a saída ...
>>>

Uma coisa a enfatizar aqui é que a classe Stock age como uma fábrica para criar instâncias de objetos. Basicamente, você a chama como uma função e ela cria um novo objeto para você. Além disso, deve ser enfatizado que cada objeto é distinto - cada um tem seus próprios dados que são separados de outros objetos que foram criados.

Um objeto definido por uma classe é um pouco semelhante a um dicionário - apenas com uma sintaxe um pouco diferente. Por exemplo, em vez de escrever s['name'] ou s['price'], você agora escreve s.name e s.price.

Exercício 4.2: Adicionando alguns Métodos

Com classes, você pode anexar funções aos seus objetos. Estas são conhecidas como métodos e são funções que operam nos dados armazenados dentro de um objeto. Adicione um método cost() e sell() ao seu objeto Stock. Eles devem funcionar assim:

>>> import stock
>>> s = stock.Stock('GOOG', 100, 490.10)
>>> s.cost()
49010.0
>>> s.shares
100
>>> s.sell(25)
>>> s.shares
75
>>> s.cost()
36757.5
>>>

Exercício 4.3: Criando uma lista de instâncias

Tente estas etapas para criar uma lista de instâncias de Stock a partir de uma lista de dicionários. Em seguida, calcule o custo total:

>>> import fileparse
>>> with open('portfolio.csv') as lines:
...     portdicts = fileparse.parse_csv(lines, select=['name','shares','price'], types=[str,int,float])
...
>>> portfolio = [ stock.Stock(d['name'], d['shares'], d['price']) for d in portdicts]
>>> portfolio
[<stock.Stock object at 0x10c9e2128>, <stock.Stock object at 0x10c9e2048>, <stock.Stock object at 0x10c9e2080>,
 <stock.Stock object at 0x10c9e25f8>, <stock.Stock object at 0x10c9e2630>, <stock.Stock object at 0x10ca6f748>,
 <stock.Stock object at 0x10ca6f7b8>]
>>> sum([s.cost() for s in portfolio])
44671.15
>>>

Exercício 4.4: Usando sua classe

Modifique a função read_portfolio() no programa report.py para que ela leia um portfólio em uma lista de instâncias de Stock, como mostrado no Exercício 4.3. Depois de fazer isso, corrija todo o código em report.py e pcost.py para que ele funcione com instâncias de Stock em vez de dicionários.

Dica: Você não deve precisar fazer grandes alterações no código. Você estará principalmente alterando o acesso a dicionários, como s['shares'], para s.shares.

Você deve ser capaz de executar suas funções da mesma forma que antes:

>>> import pcost
>>> pcost.portfolio_cost('portfolio.csv')
44671.15
>>> import report
>>> report.portfolio_report('portfolio.csv', 'prices.csv')
      Name     Shares      Price     Change
---------- ---------- ---------- ----------
        AA        100       9.22     -22.98
       IBM         50     106.28      15.18
       CAT        150      35.46     -47.98
      MSFT        200      20.89     -30.34
        GE         95      13.48     -26.89
      MSFT         50      20.89     -44.21
       IBM        100     106.28      35.84
>>>

Resumo

Parabéns! Você concluiu o laboratório de Classes. Você pode praticar mais laboratórios no LabEx para aprimorar suas habilidades.