@Pythonetc compilation, julho de 2018

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)) .

Source: https://habr.com/ru/post/pt419025/


All Articles