Chapter 2 - An array of sequences

Built-in sequences

  • Container sequĂȘncias: armazena referĂȘncias para os objetos que contĂ©m (aceita diferentes tipos)

    • list, tuple and collections.deque can hold items of different types.

  • Flat sequĂȘncias:

    • str, bytes, bytearray, memoryview and array.array hold items of one type.

Mutabilidade

  • Mutable sequĂȘncias

    • list, bytearray, array.array, collections.deque and memoryview

  • Immutable sequĂȘncias

    • tuple, str and byte

List Comprehensions in Python (aka listcomps)

Often seen as a part of functional programming in Python, list comprehensions allow you to create lists with a for loop with less code.

normal way

my_list = []
for x in range(10):
  my_list.append(x * 2)
print(my_list)

listcomps way

comp_list = [x * 2 for x in range(10)]
print(comp_list)

Listcomps x map and filter

symbols = '$¹£„₏€'
beyond_ascii = [ord(s) for s in symbols if ord(s) > 127]
beyond_ascii = list(filter(lambda c: c > 127, map(ord, symbols)))

Iterator protocol is implemented whenever you iterate over a sequence of data. For example, when you use a for loop the following is happening on a background:

  • first iter()method is called on the object to converts it to an iterator object.

  • next() method is called on the iterator object to get the next element of the sequence.

  • StopIteration exception is raised when there are no elements left to call.

O que sĂŁo generators?

Generators sĂŁo uma forma simples de criarmos iteradores. Ele irĂĄ retornar um objeto (iterador) para que nĂłs possamos iterar sobre este objeto (um valor de cada vez).

# generator expressions
def generator():
    n = 1
    print("Essa  uma funcao Generator")
    print(n)
    yield n

    n += 1
    print(n)
    yield n

    n += 1
    print(n)
    yield n


a = generator()

next(a)
# 1

next(a)
# 2

next(a)
# 3

next(a)
# StopIteration - ERROR

É muito simples criar uma função Generator, mas existem algumas peculiaridades. Por exemplo, nĂłs usamos a declaração yield ao invĂ©s de return. Se a função contĂ©m ao menos uma declaração yield entĂŁo ela se torna uma função Generator.

  • O yield pode ser lido como um pause, que retorna um objeto do tipo generator.

  • O objeto Generator sĂł pode ser iterado uma Ășnica vez.

Generator expressions

As Generators Expressions facilitam Ă  criação de Generators. Assim como uma função lambda cria uma função anĂŽnima, uma Generator Expression cria uma função Generator anĂŽnima. A sintaxe Ă© bem parecida com as famosas List Comprehensions com o pequeno detalhe de que os colchetes [ ] sĂŁo subsituĂ­dos pelos parĂȘnteses ().

O padrão da maioria dessas expressÔes é algo dessa forma:

genexpr = (expression for item in collection)

A expressão acima corresponde a função abaixo:

def generator():
    for item in collection:
        yield expression

Tuples

  • listas imutĂĄveis

  • armazenar registros

Tuples para agrupar dados

julia = ("Julia", "Roberts", 1967, "Duplicity", 2009, "Actress", "Atlanta, Georgia")

Tuple assignment and unpacking (Atribuição mĂșltipla)

O poder das tuplas estĂĄ no unpacking mechanism. Unpacking Ă© a separação de um tuple (ou qualquer sequĂȘncia) em vĂĄrias variĂĄveis. Cada variĂĄvel recebe um valor do tuple.

(name, surname, b_year, movie, m_year, profession, b_place) = julia

Parallel Assignment

>>> lax_coordinates = (33.9425, -118.408056)
>>> latitude, longitude = lax_coordinates # tuple unpacking 
>>> latitude
33.9425
>>> longitude
-118.408056

Trocar os valores de 2 variĂĄveis

Nesse caso, nĂŁo precisa de um terceira variĂĄvel temporĂĄria.

  • Antes:

    temp = a
    a = b
    b = temp
  • Tuplas:

name, surname = surname, name

Podemos usar (*) to unpack tuple

Função retorna (2 , 4) um par de nĂșmeros que consiste em seu quociente e o resto da divisĂŁo.

divmod(20, 8)
# (2, 4)

EntĂŁo, podemos tentar utilizar a tupla como parĂąmetro.

# criamos a tupla t
t = (20, 8)
divmod(t)
#TypeError: divmod expected 2 arguments, got 1

No entanto, a função divmod() recebe 2 parùmetros e não apenas 1. Podemos desestrutura a tupla, utilizando simplesmente o caractere (*).

divmod(*t)

Além disso, jå podemos usar o assingment:

quotient, remainder = divmod(*t)
print(quotient, remainder)

Namedtuple

Nesse caso, temos que passar para a função 2 parùmetros:

  • nome da classe

  • lista de campos

City = namedtuple('City', 'name country population coordinates')

tokyo = City('Tokyo', 'JP', 36.933, (35.689722, 139.691667))

tokyo.population

novos atributos

  • ._fields: exibe o nome dos campos da classe

  • ._make(): pode instanciar uma namedtuple a partir de outra namedtuple.

>>> LatLong = namedtuple('LatLong', 'lat long')
>>> delhi_data = ('Delhi NCR', 'IN', 21.935, LatLong(28.613889, 77.208889))
>>> delhi = City._make(delhi_data)
>>> delhi
city(name='Delhi NCR', country='IN', population=21.935, coordinates=LatLong(lat=28.613889, long=77.208889))
  • ._asdict(): retorna collections.OrderedDict exibindo tambĂ©m os campos da named tuple.

>>> delhi._asdict()
OrderedDict([('name', 'Delhi NCR'), ('country', 'IN'), ('population', 21.935), ('coordinates', LatLong(lat=28.613889, long=77.208889))])

Uso do * para capturar itens excedentes

>>> a, b, *rest = range(15)
>>> a, b, rest
(0, 1, [2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14])

>>> type(rest)
<class 'list'>
>>> type(a)
<class 'int'>

args e *kwargs

Antes de mais nada, eles não precisam se chamar args ou kwargs, mas precisam ter o e *, respectivamente! **Os nomes “args” são apenas uma convenção.

https://www.geeksforgeeks.org/args-kwargs-python/

*args exemplo

O interessante Ă© que o *ags Ă© lido como uma tupla, entĂŁo podemos iterĂĄ-lo para pegar os valores.

# Python program to illustrate 
# *args for variable number of arguments 
def myFun(*args): 
    print(type(args))
    for arg in args: 
        print (arg) 

myFun('Hello', 'Welcome', 'to', 'GeeksforGeeks') 

# <class 'tuple'>
# Hello
# Welcome
# to
# GeeksforGeeks

**kwargs exemplo

A diferença aqui é que não passamos somente uma tupla, mas uma lista de chave-valores.Então, nesse caso temo um dicionårio.

# Python program to illustrate   
# *kargs for variable number of keyword arguments 

def myFun(**kwargs):  
    print(type(kwargs))
    for key, value in kwargs.items(): 
        print ("%s == %s" %(key, value)) 

# Driver code 
myFun(first ='Geeks', mid ='for', last='Geeks')

# <class 'dict'>
# first == Geeks
# mid == for
# last == Geeks

Passar uma lista como parùmetro para uma função que recebe n valores

def myFun(arg1, arg2, arg3): 
    print("arg1:", arg1) 
    print("arg2:", arg2) 
    print("arg3:", arg3)

Podemos passar a lista como parùmetro para invocar a função. usando *

# Now we can use *args or **kwargs to 
# pass arguments to this function : 
args = ("Geeks", "for", "Geeks") 
myFun(*args)

Ou podemos passar uma lista chave-valor na qual as chaves devem ter o nome dos argumentos da função.

kwargs = {"arg1" : "Geeks", "arg2" : "for", "arg3" : "Geeks"} 
myFun(**kwargs)

Slicing

https://www.youtube.com/watch?v=ajrtAuDg3yw

a[start:stop:step]  # items start through start and skip numbers considering number passed on step
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
list = ['Carla','Leonardo','Carlos']
#input
list[:2]
['Carla', 'Leonardo']

Multi-dimensional slicing and ellipsis

Assigning to slices

Podemos modificar (alterar valores, adicionar, substituir) sequĂȘncias mutĂĄveis utilizando slice.

l = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

SubstituĂ­mos o valores nos Ă­ndices 2, 3 e 4 por apenas 2 valores, reduzindo o tamanho da lista.

l[2:5] = [20, 30]

len(l)
>>> 19
l[2:5] = [115,120,123,125]

len(l)
>>> 21

Operations + * with sequences

Concatenação de sequĂȘncias

lista_1 = [1, 2, 3]
lista_2 = [4, 5, 6]

lista_1 + lista_2

>>> [1, 2, 3, 4, 5, 6]

Repetição

lista_1 * 2
>>> [1, 2, 3, 1, 2, 3]

Nessas operaçÔes novos objetos são criados.

my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
id(my_list)
>>> 4561859520

my_list = my_list * 2
id(my_list)
>>> 4562997328

my_list = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
id(my_list)
>>> 4561859520

Building lists of lists

Correto

Uma lista que contém 3 listas de 4 itens cada

board = [['_'] * 4 for i in range(3)]

board
>>> [['_', '_', '_','_'], ['_', '_', '_','_'], ['_', '_', '_','_']] 

board[1][2] = 'X'

board
>>> [['_', '_', '_','_'], ['_', '_', 'X','_'], ['_', '_', '_','_']]

Errado

Uma lista com 3 referĂȘncis para mesma lista, o que nĂŁo era nossa intenção.

weird_board = [['_'] * 3] * 3
weird_board
>>> [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']] 
weird_board[1][2] = 'O'
weird_board
>>> [['_', '_', 'O'], ['_', '_', 'O'], ['_', '_', 'O']]

Operations += *= with sequences

  • += invoca o mĂ©todo especial __iadd__

  • *= invoca o mĂ©todo especial __imul__

Qual a diferença das operaçÔes em listas mutåveis e imutåveis?

Então, vårias concatenaçÔes em listas imutåveis é ineficiente porque ao invés de apenas adicionar itens na lista, um novo objeto é criado com uma cópia do original.

A+= (corner case)

  • o interpretador consegue alterar o valor da lista t[2], pois Ă© uma lista

  • mas nĂŁo consegue adicionar t[2] na lista t, pois t Ă© uma tupla.

DĂĄ para ver melhor com bytecode:

t=(1,2,[30,40])

type(t)
# <class 'tuple'>

type(t[2])
# <class 'list'>

type(t[0])
# <class 'int'>

Alguns aprendizados a partir disso:

  • nĂŁo inserir items mutĂĄveis dentro de uma tupla

  • += nĂŁo Ă© uma operação atĂŽmica

  • entender melhor o bytecode pode ser algo Ăștil

list.sort

O método list.sort() ordena a lista in-place - não cria uma cópia do objeto. Tanto que não podemos atribuí-lo para outra variåvel:

t = [10, 15, 20, 5]

t.sort()

x = t.sort()

t
# [5, 10, 15, 20]

type(x)
# <class 'NoneType'>

Convenção do Python: FunçÔes ou métodos que alteram objetos in-place sempre retornam None.

sorted function

Algoritmo de ordenação Timsort baseado no Insertion Sort e Merge sort.

A built-in functionsortedcria uma nova lista e a retorna - possui 2 argumentos possĂ­veis (reversed ekey).

t = [10, 15, 20, 5]

sorted(t)
# [5, 10, 15, 20]

x = sorted(t)
x
# [5, 10, 15, 20]

Bisect - binary search method in Python

Pode ser usado para buscar um elemento em uma lista.

Ex 1: nesse caso, a função bisect_left retorna o Ă­ndice da primeira ocorrĂȘncia do elemento 285.

import bisect

a = [-14,-10,2,108,108,285,285,285,401]

print(bisect.bisect_left(a, 285))

# 5

Ex 2: nesse caso, a função bisect_left retorna o Ă­ndice da Ășltima ocorrĂȘncia do elemento 285.

print(bisect.bisect_right(a, 285))

# 5

lo/hi arguments

bisect.insort

insort(seq, item) - insere um item na sequĂȘncia de forma a mantĂȘ-la ordenada de forma crescente.

Arrays

  • mais eficiente para listas com um Ășnico tipo

array(data type, value list)

  • append

  • insert

  • pop

  • remove

  • index

  • reverse

array.tofile()

Memory views

Last updated

Was this helpful?