Python Data Model
=================
O modelo de dados python
------------------------
- Nesses ultimo dias vc pode ter achado estranho escrever len(collection)
**em vez de collection.len()...**
.. nextslide::
A ponta do iceberg...
---------------------
O iceberg é chamado de python data model e descreve a API que você pode usar
para fazer com que seus próprios objetos interajam bem com os recursos mais
"malukos" da linguagem.
- É como se fosse um framework, formalizando a contrução da propria linguagem
- O interpretador python chama metodos especiais para realizar operações básicas.
- Os metodos especiais são sempre descritos com underscores duplos
Metodos especiais
-----------------
obj[key]
obj.__getitem__(key)
Exemplo Um Baralho Pythonico
----------------------------
.. code-block:: python
import collections
Card = collections.namedtuple('Card', ['rank', 'suit'])
class FrenchDeck:
ranks = [str(n) for n in range(2, 11)] + list('JQKA')
suits = 'spades diamonds clubs hearts'.split()
def __init__(self):
"""
O método __init__ é o construtor da classe, ou seja
é o método que será executado quando a classe for instanciada.
"""
self._cards = [Card(rank, suit) for suit in self.suits
for rank in self.ranks]
def __len__(self):
return len(self._cards)
def __getitem__(self, position):
return self._cards[position]
.. nextslide::
.. code-block:: python
>>> from frenchdeck import FrenchDeck, Card
>>> beer_card = Card('7', 'diamonds')
>>> beer_card
Card(rank='7', suit='diamonds')
>>> deck = FrenchDeck()
>>> len(deck)
52
>>> deck[:3]
[Card(rank='2', suit='spades'), Card(rank='3', suit='spades'), Card(rank='4', suit='spades')]
>>> deck[12::13]
[Card(rank='A', suit='spades'), Card(rank='A', suit='diamonds'), Card(rank='A', suit='clubs'), Card(rank='A', suit='hearts')]
>>> Card('Q', 'hearts') in deck
True
>>> Card('Z', 'clubs') in deck
False
>>> for card in deck: # doctest: +ELLIPSIS
... print(card)
Card(rank='2', suit='spades')
Card(rank='3', suit='spades')
Card(rank='4', suit='spades')
...
>>> for card in reversed(deck): # doctest: +ELLIPSIS
... print(card)
Card(rank='A', suit='hearts')
Card(rank='K', suit='hearts')
Card(rank='Q', suit='hearts')
.. nextslide::
.. code-block:: python
>>> for n, card in enumerate(deck, 1): # doctest: +ELLIPSIS
... print(n, card)
1 Card(rank='2', suit='spades')
2 Card(rank='3', suit='spades')
3 Card(rank='4', suit='spades')
Como os metodos especiais sao usados
------------------------------------
- Eles foram criados para serem chamados pelo interpretador e não por você.
- Não escrevemos my_object.__len__(), escrevemos len(my_object)
Se my_object for uma instancia de uma classe definida por você, o python chamara
o metodo __len__ que voce implementou.
Para muitos tipos embutidos o interpretador usará um atalho: a implementação de len()
do CPython, retorna o valor do ob_size da Scruct C PyVarObject que representa qualquer
objeto embutido de tamanho váriavel na memoria. Isto é muito mais rapido que chamar um metodo.
Emulando tipos numéricos
------------------------
.. code-block:: 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)
Mecanismo flexivel de parametros
--------------------------------
Um dos melhores recursos das funções python:
- "*" faz com que o vetor seja utilizado como argumentos ordenados.
- "**" faz com que o dicionario seja usado como argumentos nomeados.
.. code-block:: python
def func_var_args(*args):
print(args)
func_var_args(1, 2, '3')
# (1, 2, '3')
Isto é usado quando não sabemos a quantidade de parâmetros.
.. code-block:: python
def func_keyword_arg(**kwargs):
print(kwargs)
func_keyword_arg(keyword1=10, keyword2='foo')
# {'keyword2': 'foo', 'keyword1': 10}
.. nextslide::
.. code-block:: python
def tag(name, cls=None, *content, **attrs):
"""Generate one or more HTML tags"""
if cls is not None:
attrs['class'] = cls
if attrs:
attr_str = ''.join(' %s="%s"' % (attr, value) for attr, value in sorted(attrs.items()))
else:
attr_str = ''
if content:
return '\n'.join('<%s%s>%s%s>' % (name, attr_str, c, name) for c in content)
else:
return '<%s%s />' % (name, attr_str)
.. code-block:: python
>>> tag('br') # <1>
'
'
>>> tag('p', 'hello') # <2>
'
hello
' >>> print(tag('p', 'hello', 'world'))hello
world
>>> tag('p', 'hello', id=33) # <3> 'hello
' >>> print(tag('p', 'hello', 'world', cls='sidebar')) # <4> >>> tag(content='testing', name="img") # <5> '