Boa noite, pessoal!
Hoje na aula vimos o código do experimento para gerar os gráficos de tempo dos algoritmos de ordenação, e surgiu uma dúvida relativa às funções "wrapper" dos algoritmos de ordenação implementados em outros lugares. Apesar dessas funções serem simples adaptações dos códigos originais e não terem muito a ver com a classe, ao definí-las dentro da classe o Python nos obrigava a chamá-las como métodos (com a sintaxe objeto.método), o que nos forçava a incluir o argumento "self" na especificação delas, mesmo que não fôssemos usar mais nada do escopo do objeto:
def f(self,v): # código da função que não usa self pra nada
Embora não haja nada de errado nessa versão, e faça bastante sentido definir esses wrappers dentro da classe (pois não são usados em nenhum outro lugar), a questão de como definir funções "auxiliares" que não aumentassem a coleção de métodos/atributos dos objetos da classe permaneceu. Há outras situações naturais pra isso, além do exemplo que vimos hoje. Algumas soluções alternativas:
1) Definir as funções fora da classe, tendo como escopo o módulo. Nesse caso elas podem ser usadas sem a notação objeto.método e sem os selfs.
2) Definir as funções dentro da classe mas como "global". Pra isso devemos fazer (dentro da classe):
global f
def f(v): # código da função
Na prática, essa solução é idêntica à (1), pois o "global" torna a função um atributo do módulo. A única diferença é na leitura do código, pois elas aparecem dentro do código da classe (sugerindo uma espécie de dependência, embora fake) ao invés de fora (como entidades independentes, como de fato são).
3) Definir as funções dentro do método da classe que vai usá-las. No nosso exemplo, elas eram usadas no construtor __init__, então definido-as aí elas podem ser usadas sem self's e notações tipo objeto.método. Mas isso só funciona se elas forem usadas em um único método da classe, pois os nomes delas ficam restritos ao escopo desse método.
4) [spoiler: essa é mais esotérica] As funções poderiam ser definidas dentro da classe e "decoradas" com @staticmethod:
@staticmethod
def f(v): # código da função
Nessa versão, elas ainda só serão acessíveis pela sintaxe objeto.f, mas a diferença é que esse tipo de método não passa o objeto como primeiro argumento, eliminando a necessidade do atributo inútil self. Não acho uma solução particularmente interessante (por requerer a sintaxe objeto.f), mas menciono aqui por completude.
O código experimento.py já está no PACA. Completei o que faltou no final da aula e incluí um método de ordenação simples e interessante de conhecer, o counting sort. Ele não é um método geral, só serve para vetores com conteúdo dentro de um universo limitado de símbolos. Também inclui a parte de repetir 100 vezes cada N e cada método, mas de um jeito diferente do que discutimos em aula (vejam o código!).
O gráfico gerado para M=100.000 e N=10,20,30,...490 é esse (se você está lendo no e-mail, a imagem não aparece, só mesmo entrando no fórum do PACA):
As curvas representam, do mais lento ao mais rápido, os métodos insertionsort, mergesort e countingsort. Experimentem com outros valores de M e também indo até vetores maiores!
Abraços,
Marcelo