Esta é a segunda coleção de dicas e programação em Python do meu
feed @pythonetc . Seleções anteriores:
Línguas regulares
Uma linguagem regular é uma linguagem formal que pode ser representada como uma
máquina de estados finitos . Em outras palavras, para o processamento de texto caractere por caractere, é necessário lembrar apenas o estado atual e o número desses estados é finito.
Um exemplo perfeito: uma máquina que verifica se a entrada é primordial como –3, 2.2 ou 001. No início do artigo, uma máquina de estados finitos é mostrada. Círculos duplos indicam o estado final, no qual a máquina pode parar.
A máquina inicia na posição . Talvez encontre um sinal de menos, depois um dígito e, na posição processes, processa o número necessário de dígitos. Depois disso, o separador decimal (③ → ④) pode ser verificado, seguido por um dígito (④ → ⑤) ou mais (⑤ → ⑤).
Um exemplo clássico de uma linguagem irregular é uma família de expressões de string no formato:
ab
aaa-bbb
aaaaa-bbbbb
Formalmente, precisamos de uma string contendo N instâncias de
a
, então
–
, então - N instâncias de
b
, onde N é um número inteiro maior que 0. Você não pode implementar isso com uma máquina de estado, pois precisará lembrar o número de caracteres que achou que poderia feito apenas usando um número infinito de estados.
Expressões regulares podem especificar apenas idiomas regulares. Antes de usá-los, verifique se a sua string pode ser processada usando uma máquina de estado. Por exemplo, eles não são adequados para processar expressões JSON, XML ou mesmo aritméticas com parênteses.
É engraçado que muitos mecanismos modernos de regex não sejam regulares. Por exemplo, o módulo regex para Python suporta recursão (o que
ajudará a resolver o problema com
aaa-bbb
).
Planejamento dinâmico
Quando o Python faz uma chamada de método, digamos
af(b, c, d)
, ele deve primeiro selecionar a função correta
f
. Em virtude do polimorfismo,
a
determina o que será finalmente escolhido. O processo de seleção de um método é chamado de despacho dinâmico.
O Python suporta apenas polimorfismo de despacho único. Isso significa que apenas o próprio objeto afeta a escolha do objeto (no nosso exemplo,
a
). Em outros idiomas, os tipos
b
,
c
d
podem ser levados em consideração - esse mecanismo é chamado de despacho múltiplo. Um exemplo impressionante é a linguagem C #.
No entanto, vários agendamentos podem ser emulados usando um único. É por isso que o modelo de design do visitante foi criado: ele usa duas vezes um único envio para simular um duplo.
Lembre-se de que os métodos de sobrecarga (como em Java e C ++) não são análogos do envio múltiplo. O despacho dinâmico funciona em tempo de execução e a sobrecarga é realizada apenas durante a compilação.
Estes exemplos ajudarão você a entender melhor o tópico:
Nomes embutidos
No Python, você pode modificar facilmente todas as variáveis padrão disponíveis no escopo global:
>>> print = 42 >>> print(42) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: 'int' object is not callable
Isso é útil se o seu módulo definir funções cujos nomes correspondam aos nomes das funções internas. Isso também acontece em situações nas quais você pratica metaprogramação e assume um valor arbitrário de string como identificador.
Mas, mesmo se você duplicar os nomes de algumas funções internas, poderá precisar de acesso ao que elas se referiam originalmente. É por isso que o módulo builtins existe:
>>> import builtins >>> print = 42 >>> builtins.print(1) 1
Também na maioria dos módulos, a variável
__builtins__
está disponível. Mas há um truque. Primeiro, esse é um recurso da implementação do cpython e, geralmente, não deve ser usado. Em segundo lugar,
__builtins__
pode se referir tanto a
builtins
quanto a
builtins.__dict__
, dependendo de como o módulo atual foi carregado.
traço
Às vezes, o aplicativo começa a se comportar estranhamente em batalha. Em vez de reiniciá-lo, você pode entender a causa dos problemas enquanto isso é possível.
A solução óbvia é analisar as ações do programa e tentar entender qual parte do código está sendo executada. O registro adequado facilita essa tarefa, mas seus registros podem não ser detalhados o suficiente devido à arquitetura ou ao nível de registro selecionado nas configurações.
Nesses casos, o traço pode ser útil. Este é um utilitário Unix que rastreia as chamadas do sistema. Você pode executá-lo anteriormente -
strace python script.py
- mas geralmente é mais conveniente conectar-se a um aplicativo já em execução:
strace -p PID
.
$ cat test.py with open('/tmp/test', 'w') as f: f.write('test') $ strace python test.py 2>&1 | grep open | tail -n 1 open("/tmp/test", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
Cada linha do rastreio contém o nome da chamada do sistema, os argumentos entre colchetes e o valor de retorno. Como alguns argumentos são usados para retornar o resultado de uma chamada do sistema e não transmitir dados a ela, a saída da linha pode ser pausada até que a chamada do sistema termine.
Neste exemplo, a saída é interrompida até que a gravação em STDIN seja concluída:
$ strace python -c 'input()' read(0,
Literais de tupla
Uma das partes mais inconsistentes da sintaxe do Python são literais de tupla.
Para criar uma tupla, basta listar os valores separados por vírgulas:
1, 2, 3
. Que tal uma tupla de elemento único? Basta adicionar uma vírgula:
1,
,. Parece feio e muitas vezes leva a erros, mas é bastante lógico.
Que tal uma tupla vazia? Esta é uma vírgula -? Não, é
()
. E colchetes criam uma tupla, como vírgulas? Não,
(4)
não é uma tupla, é apenas
4
.
In : a = [ ...: (1, 2, 3), ...: (1, 2), ...: (1), ...: (), ...: ] In : [type(x) for x in a] Out: [tuple, tuple, int, tuple]
Para confundir as coisas ainda mais, os literais das tuplas geralmente exigem parênteses extras. Se você precisar que a tupla seja o único argumento para a função, então obviamente
f(1, 2, 3)
não funcionará - você terá que escrever
f((1, 2, 3))
.