Cl

Classes em Python

49 exercícios

Sobre Classes

Classes combinam dados com comportamento. As classes usam-se para criar cópias, ou instances, de dados e comportamento agrupados, aquilo a que se chama vulgarmente objects. Os objetos podem representar entidades do mundo real (como carros ou gatos) ou conceitos mais abstratos (como números inteiros, veículos ou mamíferos). As classes são parte integrante de uma abordagem de programação orientada a objetos (POO), que pede ao programador para pensar em modelar um problema como um ou mais objetos que interagem entre si, mantêm estado e atuam sobre os dados.

Classes

As classes são as definições de novos tipos de objeto, a partir das quais se criam novas instances de objetos. Agrupam frequentemente dados (conhecidos como fields, attributes, properties, data members ou variables) com código ou funções (conhecidas como methods) que operam sobre esses dados. Neste sentido, uma classe é um modelo ou um conjunto de instruções a partir do qual se podem construir e usar muitos objetos de tipo semelhante. Um programa complexo pode ter muitas classes, cada uma a construir muitos tipos diferentes de objetos. O processo de construir e personalizar um objeto a partir de uma classe chama-se instantiation (ou criar uma instância da classe).

Uma definição de classe em Python é simples:

class MyClass:
    # Class body goes here

Atributos de classe

Os campos de classe (também conhecidos como propriedades, atributos, membros de dados ou variáveis) podem ser adicionados ao corpo da classe.

class MyClass:
    number = 5
    string = "Hello!"

É possível criar uma instância (objeto) de MyClass e associá-la a um nome:

>>> new_object = MyClass()

# Class is instantiated and resulting object is bound to the "new_object" name (variable).
# Note: the object address 'at 0x15adc55b0' will vary by computer.
>>> new_object
<__main__.MyClass at 0x15adc55b0>

Os Class attributes são partilhados por todos os objetos (ou instâncias) criados a partir de uma classe, e podem ser acedidos através da dot notation, um . colocado depois do nome do objeto e antes do nome do atributo:

>>> new_object = MyClass()

# Accessing the class attribute "number" via dot-notation.
>>> new_object.number
5

# Accessing the class attribute "string" via dot-notation.
>>> new_object.string
'Hello!'

# Instantiating an additional object and binding it to the "second_new_object" name.
>>> second_new_object = MyClass()

>>> second_new_object
<__main__.MyClass at 0x15ad99970>

# Second_new_object shares the same class attributes as new_object.
>>> new_object.number == second_new_object.number
True

Os atributos de classe são definidos no corpo da própria classe, antes de quaisquer outros métodos. Pertencem à classe, o que permite que sejam partilhados entre instâncias. Como estes atributos são partilhados por todas as instâncias da classe, o seu valor pode ser acedido e manipulado diretamente a partir da classe. Alterar o valor dos atributos de classe altera o valor para todos os objetos instanciados a partir da classe:

>>> obj_one = MyClass()
>>> obj_two = MyClass()

# Accessing a class attribute from an object.
>>> obj_two.number
5

# Accessing the class attribute from the class itself.
>>> MyClass.number
5

# Modifying the value of the "number" class attribute.
>>> MyClass.number = 27

# Modifying the "number" class attribute changes the "number" attribute for all objects.
>>> obj_one.number
27

>>> obj_two.number
27

Ter um conjunto de objetos com dados sincronizados o tempo todo não é propriamente útil. Felizmente, os objetos criados a partir de uma classe podem ser personalizados com os seus próprios instance attributes (ou propriedades, variáveis ou campos de instância). Como o nome sugere, os atributos de instância são únicos de cada objeto e podem ser modificados de forma independente.

Personalizar a instanciação de objetos com __init__()

O especial "método dunder" (abreviação de "double underscore method") __init__() serve para personalizar as instâncias de uma classe e pode ser usado para inicializar atributos ou propriedades de instância dos objetos. Pelo seu papel na inicialização dos atributos de instância, __init__() também é conhecido como class constructor ou initializer. __init__() recebe um parâmetro obrigatório chamado self, que se refere ao objeto acabado de criar. Os dados para os atributos ou propriedades de instância podem então ser passados como argumentos de __init__(), a seguir ao parâmetro self.

Abaixo, MyClass tem agora atributos de instância chamados location_x e location_y. Como podes ver, os dois atributos foram atribuídos ao primeiro e ao segundo índices do argumento location (um tuplo) que foi passado a __init__(). Os atributos location_x e location_y de uma instância da classe serão agora inicializados quando instancias a classe e é criado um objeto:

class MyClass:
    # These are class attributes, variables, or fields.
    number = 5
    string = "Hello!"

    # This is the class "constructor", with a "location" parameter that is a tuple.
    def __init__(self, location):

        # This is an instance or object property, attribute, or variable.
        # Note that we are unpacking the tuple argument into two separate instance variables.
        self.location_x = location[0]
        self.location_y = location[1]

# Create a new object "new_object_one", with object property (1, 2).
>>> new_object_one = MyClass((1, 2))

# Create a second new object "new_object_two" with object property (-8, -9).
>>> new_object_two = MyClass((-8, -9))

# Note that new_object_one.location_x and new_object_two.location_x two different values.
>>> new_object_one.location_x
1

>>> new_object_two.location_x
-8

Repara que só precisas de passar um argumento ao inicializar MyClass acima; o Python encarrega-se de passar self quando a classe é chamada.

Advanced

Outra forma de criar uma variável de instância é simplesmente aceder a uma variável de classe através de um objeto e alterá-la para outra coisa:

>>> obj_one = MyClass()
>>> obj_two = MyClass()
>>> MyClass.string
'Hello!'

>>> obj_two.string = "Hi!"
>>> obj_two.string
'Hi!'

>>> obj_one.string
'Hello!'

Agora, repara no que acontece quando a variável de classe muda:

>>> MyClass.string = "World!"
>>> obj_two.string

# obj_two.string has not changed
'Hi!'

>>> obj_one.string

# obj_one.string changed
'World!'

O atributo string é agora uma variável de instância no caso de obj_two, mas continua a ser uma variável de classe em obj_one.

Isto também pode ser feito de dentro da classe:

class Demo:
    new_var = 3

    def add_two(self):
        self.new_var += 2

No momento em que <object>.add_two() é chamado e self.new_var += 2 é lido, new_var passa de variável de classe a variável de instância com o mesmo nome.

Isto pode ser útil durante a inicialização, quando todas as instâncias de uma classe precisarem de ter algum(ns) atributo(s) com o mesmo valor inicial. No entanto, a variável de instância passa então a ocultar a variável de classe, tornando a variável de classe inacessível a partir da instância onde é ocultada. Face a esta situação, pode ser mais seguro e mais claro definir os atributos de instância a partir do método __init__() como self.<attribute>.

Métodos

Um method é uma function que está associada à própria classe (conhecida como método de classe, que será abordada em profundidade num exercício posterior) ou a uma instância da classe (objeto). Os métodos que operam sobre um objeto ou instância têm de ser definidos com self como primeiro parâmetro. Podes depois definir os restantes parâmetros como farias numa função "normal" ou não associada. Os métodos que operam sobre uma classe têm de ser definidos com o decorador @classmethod e (por convenção) com cls como primeiro parâmetro. Os métodos de classe são chamados diretamente sobre uma classe, sem criar primeiro um objeto a partir dela.

class MyClass:
    number = 5
    string = "Hello!"

    # Class constructor.
    def __init__(self, location):

        # Instance properties
        self.location_x = location[0]
        self.location_y = location[1]

    # Class method. Uses the @classmethod decorator, and cls as the first parameter.
    # Can be called without first making an instance of the class.
    @classmethod
    def change_string(cls, new_string):
        #Class attributes are referred to with cls.
        cls.string = new_string
        return cls.string

    # Instance method.  Note "self" as first parameter.
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

Tal como acontece com o acesso a atributos, chamar um método é tão simples como colocar um . depois do nome do objeto e antes do nome do método. O método chamado não precisa de uma cópia do objeto como primeiro parâmetro; o Python preenche o self automaticamente:

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

# Make a new test_object with location (3,7)
>>> test_object = MyClass((3,7))
>>> (test_object.location_x, test_object.location_y)
(3,7)

# Call change_location to increase location_x and location_y by 7.
>>> test_object.change_location(7)
(10, 14)

Os atributos de classe podem ser acedidos de dentro dos métodos de instância da mesma forma que são acedidos fora da classe:

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    # Alter instance variable location_x and location_y
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return self.location_x, self.location_y

    # Alter class variable number for all instances from within an instance.
    def increment_number(self):
        # Increment the 'number' class variable by 1.
        MyClass.number += 1


>>> test_object_one = MyClass((0,0))
>>> test_object_one.number
5

>>> test_object_two = MyClass((13, -3))
>>> test_object_two.increment_number()
>>> test_object_one.number
6

Implementação provisória ou em stub com pass

Nos exercícios de conceitos anteriores e nos stubs dos exercícios de prática, já terás visto a palavra-chave pass usada dentro do corpo das funções em vez do código real.

A palavra-chave pass é um marcador de posição sintaticamente válido: impede que o Python lance um erro de sintaxe por causa de uma função ou definição de classe vazia. Basicamente, é uma forma de dizer ao intérprete do Python: "Não te preocupes! Vou mesmo pôr código aqui, só que ainda não o fiz."

class MyClass:
    number = 5
    string = "Hello!"

    def __init__(self, location):
        self.location_x = location[0]
        self.location_y = location[1]

    # Alter instance variable location_x and location_y.
    def change_location(self, amount):
        self.location_x += amount
        self.location_y += amount
        return  self.location_x, self.location_y

    # Alter class variable number for all instances.
    def increment_number(self):
        # Increment the 'number' class variable by 1.
        MyClass.number += 1

    # This will compile and run without error, but has no current functionality.
    def pending_functionality(self):
       # Stubbing or place-holding the body of this method.
       pass
Editar via GitHub A ligação abre numa nova janela ou separador

Aprende Classes

A prática está bloqueada

Desbloqueia mais 6 exercícios para praticares Classes