# Chapter 1 - The Python Data Model

<https://docs.python.org/3/reference/datamodel.html>

* **Dunder methods:** special methods such as `.__getitem__` ou `.__dict__`

### French Deck example

```python
class FrenchDeck:
    ranks = [str(n) for n in range(2, 11)] + list('JQKA') 
    suits = 'spades diamonds clubs hearts'.split()

    def __init__(self):
        self._cards = [Card(rank, suit) for suit in self.suits

    def __len__(self):
        return len(self._cards)

    def __getitem__(self, position): 
        return self._cards[position]
```

* o uso dos atributos `.__getitem__` e `.__len__` torna essa classe uma sequência que pode ser **iterable**. Então, podemos criar laços, sortear um item da instância classe etc.

### Metódo especial `.__getitem__`

In Python’s data model, when you write `foo[i]`, `foo.__getitem__(i)` is called.

```python
list = ['Carla','Leonardo','Carlos']
```

#### Slicing

```python
a[start:stop]  # items start through stop-1
a[start:]      # items start through the rest of the array
a[:stop]       # items from the beginning through stop-1
a[:]           # a copy of the whole array
```

```python
list = ['Carla','Leonardo','Carlos']
```

```python
#input
list[:2]
```

```
['Carla', 'Leonardo']
```

#### Iterable

Just because a method implements `.__getitem__`, it is also iterable

```python
for i in list:
    print(i)
```

```
Carla
Leonardo
Carlos
```

#### `in`

Uses the method `.__contains__`

```python
'Carla' in list
```

```
True
```

#### Sorting

```python
for i in sorted(list):
    print(i)
```

```
Carla
Carlos
Leonardo
```

### Chamada dos métodos especiais

No geral, não precisamos chamar diretamente os métodos especiais - quem faz isso é o interpretador python.

Um dos únicos métodos que usamos diretamente é o `.__init__` - para invocar o inicializador da superclasse - é um **construtor de objetos**.

**Java**

```java
int age
int name

public void abc(int age,int name) {
    this.age = age;
    this.name = xyz;
}
```

**Python**

```python
class Main:
    def __init__(self):
        self._age = None
        self._name = None
    def method(self):
        print self._age, self._name

test = Main()
test.method()
# None None
```

[\* Stackoverflow: Quando devo usar **init** em funções dentro de classes?](https://pt.stackoverflow.com/questions/109013/quando-devo-usar-init-em-fun%C3%A7%C3%B5es-dentro-de-classes)

[\* Java Class Variables & **init** method in Python](https://stackoverflow.com/questions/19303146/java-class-variables-init-method-in-python)

### Managed contexts

<https://book.pythontips.com/en/latest/context_managers.html>

### Operators overloading

> Em java, não é possível fazer isso. Então, temos que criar métodos específicos da classe para realizar essas operações (ex.: `sum`)

<https://pense-python.caravela.club/17-classes-e-metodos/07-sobrecarga-de-operadores.html>

#### Exemplo `Vector`

Nesse caso, implementamos operadores aritméticos para o formato de vetor e mostramos qual deve ser o comportamento se for chamada uma multiplicação ou adição de vetores.

```python
from math import hypot class Vector:

def __init__(self, x=0, y=0): 
    self.x = x
    self.y = y

def __repr__(self):
    return 'Vector(%r, %r)' % (self.x, self.y) 

def __abs__(self):
    return hypot(self.x, self.y) 

def __bool__(self):
    return bool(abs(self))

def __add__(self, other): 
    x = self.x + other.x 
    y = self.y + other.y 
    return Vector(x, y)

def __mul__(self, scalar):
    return Vector(self.x * scalar, self.y * scalar)
```

#### `__string__` x `__repr__`

<https://www.geeksforgeeks.org/str-vs-repr-in-python/>

<https://stackoverflow.com/questions/1436703/difference-between-str-and-repr>

O [`__str__`](https://docs.python.org/2/library/string.html) serve para exibir o objeto para usuário final, usada pelo comando print e pela função str.

O [`__repr__`](https://docs.python.org/2/library/functions.html#repr) serve para exibir o objeto para o programador, usada pelo console do Python e pela funçao repr.

\* Dar preferência à implementação do `__repr__` - fallback

### Method overloading

Diferentes formas de chamar o mesmo método.

![](https://i.imgur.com/pBAPVT4.png)

<https://pythonspot.com/method-overloading/>

#### `__bool__` and `__len__`

O tipo bool em python aceita qualquer tipo de objeto através de `bool(x)` que chama `x.__bool__()`.

* Se o **bool** não estiver implementado:
  * Python invoca `x.__len__()` (se retornar zero, será **false**)

### Why len is not a method

> No method is called for the built-in objects of CPython: the length is simply read from a field in a C struct.


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://carlaprv.gitbook.io/python-fluente/part-1-prologue/chapter-1-the-python-data-model.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
