You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: capitulos/cap08.adoc
+90-34Lines changed: 90 additions & 34 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -506,14 +506,21 @@ Isso é mais flexível que a _tipagem nominal_, ao preço de permitir mais erros
506
506
Tipagem nominal::
507
507
É a ((("nominal typing"))) perspectiva adotada em {cpp}, Java, e C#, e suportada em Python anotado.
508
508
Objetos e variáveis tem tipos.
509
-
Mas objetos só existem durante a execução, e o verificador de tipo só se importa com o código-fonte, onde as variáveis (incluindo parâmetros de função) tem anotações com dicas de tipo.
509
+
Mas objetos só existem durante a execução, e o verificador de tipo só se importa com o código-fonte,
510
+
onde as variáveis (incluindo parâmetros de função) tem anotações com dicas de tipo.
510
511
Se `Duck` é uma subclasse de `Bird`, você pode atribuir uma instância de `Duck` a um parâmetro anotado como `birdie: Bird`.
511
-
Mas no corpo da função, o verificador considera a chamada `birdie.quack()` ilegal, pois `birdie` é nominalmente um `Bird`, e aquela classe não fornece o método `.quack()`.
512
-
Não interessa que o argumento real, durante a execução, é um `Duck`, porque a tipagem nominal é aplicada de forma estática.
512
+
Mas no corpo da função, o verificador considera a chamada `birdie.quack()` ilegal,
513
+
pois `birdie` é nominalmente um `Bird`, e aquela classe não fornece o método `.quack()`.
514
+
Não interessa que o argumento real, durante a execução, é um `Duck`,
515
+
porque a tipagem nominal é aplicada de forma estática.
513
516
O verificador de tipo não executa qualquer pedaço do programa, ele apenas lê o código-fonte.
514
-
Isso é mais rígido que _duck typing_, com a vantagem de capturar alguns bugs durante o desenvolvimento, ou mesmo em tempo real, enquanto o código está sendo digitado em um IDE.
517
+
Isso é mais rígido que _duck typing_,
518
+
com a vantagem de capturar alguns bugs durante o desenvolvimento,
519
+
ou mesmo em tempo real, enquanto o código está sendo digitado em um IDE.
515
520
516
-
O <<birds_module_ex>> é um exemplo bobo que contrapõe duck typing e tipagem nominal, bem como checagem de tipos estática e comportamento durante a execução.footnote:[Muitas vezes a herança é sobreutilizada e difícil de justificar em exemplos que, apesar de realistas, são muito simples.
521
+
O <<birds_module_ex>> é um exemplo bobo que contrapõe duck typing e tipagem nominal,
522
+
bem como checagem de tipos estática e comportamento durante a execução.footnote:[Muitas
523
+
vezes a herança é sobreutilizada e difícil de justificar em exemplos pequenos.
517
524
Então por favor aceite esse exemplo com animais como uma rápida ilustração de sub-tipagem.]
518
525
519
526
[[birds_module_ex]]
@@ -538,7 +545,9 @@ birds.py:16: error: "Bird" has no attribute "quack"
538
545
Found 1 error in 1 file (checked 1 source file)
539
546
----
540
547
541
-
Só de analisar o código fonte, Mypy percebe que `alert_bird` é problemático: a dica de tipo declara o parâmetro `birdie` como do tipo `Bird`, mas o corpo da função chama `birdie.quack()` — e a classe `Bird` não tem esse método.
548
+
Só de analisar o código fonte, Mypy percebe que `alert_bird` é problemático:
549
+
a dica de tipo declara o parâmetro `birdie` como do tipo `Bird`,
550
+
mas o corpo da função chama `birdie.quack()` — e a classe `Bird` não tem esse método.
542
551
543
552
Agora vamos tentar usar o módulo `birds` em _daffy.py_ no <<daffy_module_ex>>.
<1> Chamada válida, pois `alert` não tem dicas de tipo.
554
563
<2> Chamada válida, pois `alert_duck` recebe um argumento do tipo `Duck` e `daffy` é um `Duck`.
555
-
<3> Chamada válida, pois `alert_bird` recebe um argumento do tipo `Bird`, e `daffy` também é um `Bird` — a superclasse de `Duck`.
564
+
<3> Chamada válida, pois `alert_bird` recebe um argumento do tipo `Bird`,
565
+
e `daffy` também é um `Bird` — a superclasse de `Duck`.
556
566
557
567
Mypy reporta o mesmo erro em _daffy.py_, sobre a chamada a `quack` na função `alert_bird` definida em _birds.py_:
558
568
@@ -580,9 +590,11 @@ Funciona perfeitamente! Viva o duck typing!
580
590
Durante a execução do programa, Python não se importa com os tipos declarados.
581
591
Ele usa apenas duck typing.
582
592
O Mypy apontou um erro em `alert_bird`, mas a chamada da função com `daffy` funciona corretamente quando executada.
583
-
À primeira vista isso pode surpreender muitos pythonistas: um verificador de tipo estático muitas vezes encontra erros em código que sabemos que vai funcionar quanto executado.
593
+
À primeira vista isso pode surpreender muitos pythonistas:
594
+
um verificador de tipo estático muitas vezes encontra erros em código que sabemos que vai funcionar quanto executado.
584
595
585
-
Entretanto, se daqui a alguns meses você for encarregado de estender o exemplo bobo do pássaro, você agradecerá ao Mypy.
596
+
Entretanto, se daqui a alguns meses você for encarregado de estender o exemplo bobo do pássaro,
597
+
você agradecerá ao Mypy.
586
598
Observe esse módulo _woody.py_ module, que também usa `birds`, no <<woody_module_ex>>.
587
599
588
600
[[woody_module_ex]]
@@ -606,11 +618,13 @@ Found 2 errors in 2 files (checked 1 source file)
606
618
----
607
619
608
620
O primeiro erro é em _birds.py_: a chamada a `birdie.quack()` em `alert_bird`, que já vimos antes.
609
-
O segundo erro é em _woody.py_: `woody` é uma instância de `Bird`, então a chamada `alert_duck(woody)` é inválida, pois aquela função exige um `Duck.`
621
+
O segundo erro é em _woody.py_: `woody` é uma instância de `Bird`,
622
+
então a chamada `alert_duck(woody)` é inválida, pois aquela função exige um `Duck.`
610
623
Todo `Duck` é um `Bird`, mas nem todo `Bird` é um `Duck`.
611
624
612
625
Durante a execução, nenhuma das duas chamadas em _woody.py_ funcionariam.
613
-
A sucessão de falhas é melhor ilustrada em uma sessão no console, através das mensagens de erro, no <<birdie_errors_ex>>.
626
+
A sucessão de falhas é melhor ilustrada em uma sessão no console,
627
+
através das mensagens de erro, no <<birdie_errors_ex>>.
614
628
615
629
[[birdie_errors_ex]]
616
630
.Erros durante a execução e como o Mypy poderia ter ajudado
@@ -636,34 +650,51 @@ AttributeError: 'Bird' object has no attribute 'quack'
636
650
----
637
651
====
638
652
<1> O Mypy não tinha como detectar esse erro, pois não há dicas de tipo em `alert`.
639
-
<2> O Mypy avisou do problema: `Argument 1 to "alert_duck" has incompatible type "Bird"; expected "Duck"` (_Argumento 1 para `alert_duck` é do tipo incompatível "Bird"; argumento esperado era "Duck"_)
640
-
<3> O Mypy está avisando desde o <<birds_module_ex>> que o corpo da função `alert_bird` está errado: `"Bird" has no attribute "quack"` (_Bird não tem um atributo "quack"_)
653
+
<2> O Mypy avisou do problema: `Argument 1 to "alert_duck" has incompatible type "Bird";
654
+
expected "Duck"` (_Argumento 1 para `alert_duck` é do tipo incompatível "Bird"; argumento esperado era "Duck"_)
655
+
<3> O Mypy está avisando desde o <<birds_module_ex>> que o corpo da função `alert_bird` está errado:
656
+
`"Bird" has no attribute "quack"` (_Bird não tem um atributo "quack"_)
641
657
642
-
Este pequeno experimento mostra que o duck typing é mais fácil para o iniciante e mais flexível, mas permite que operações não suportadas causem erros durante a execução.
643
-
A tipagem nominal detecta os erros antes da execução, mas algumas vezes rejeita código que seria executado sem erros - como a chamada a `alert_bird(daffy)` no <<daffy_module_ex>>.
658
+
Este pequeno experimento mostra que o duck typing é mais fácil para o iniciante e mais flexível,
659
+
mas permite que operações não suportadas causem erros durante a execução.
660
+
A tipagem nominal detecta os erros antes da execução,
661
+
mas algumas vezes rejeita código que seria executado sem erros—como
662
+
a chamada a `alert_bird(daffy)` no <<daffy_module_ex>>.
644
663
645
-
Mesmo que funcione algumas vezes, o nome da função `alert_bird` está incorreto: seu código exige um objeto que suporte o método `.quack()`, que não existe em `Bird`.
664
+
Mesmo que funcione algumas vezes, o nome da função `alert_bird` está incorreto:
665
+
seu código exige um objeto que suporte o método `.quack()`, que não existe em `Bird`.
646
666
647
667
Nesse exemplo bobo, as funções tem uma linha apenas.
648
-
Mas na vida real elas poderiam ser mais longas, e poderiam passar o argumento `birdie` para outras funções, e a origem daquele argumento poderia estar a muitas chamadas de função de distância, tornando difícil localizar a causa do erro durante a execução.
668
+
Mas na vida real elas poderiam ser mais longas,
669
+
e poderiam passar o argumento `birdie` para outras funções,
670
+
e a origem daquele argumento poderia estar a muitas chamadas de função de distância,
671
+
tornando difícil localizar a causa do erro durante a execução.
649
672
O checador de tipos impede que muitos erros como esse aconteçam durante a execução de um programa.
650
673
651
674
652
675
[NOTE]
653
676
====
654
677
O valor das dicas de tipo é questionável em exemplos minúsculo que cabem em um livro.
655
-
Os benefícios crescem conforme o tamanho da base de código afetada. É por essa razão que empresas com milhões de linhas de código em Python - como a Dropbox, o Google e o Facebook - investiram em equipes e ferramentas para promover a adoção global de dicas de tipo internamente, e hoje tem partes significativas e crescentes de sua base de código checadas para tipo em suas linhas (_pipeline_) de integração contínua.
678
+
Os benefícios crescem conforme o tamanho da base de código afetada.
679
+
É por essa razão que empresas com milhões de linhas de código em
680
+
Python—como Dropbox, Google e Facebook—investiram em equipes e ferramentas para
681
+
promover a adoção global de dicas de tipo internamente,
682
+
e hoje tem partes significativas e crescentes de sua base de código checadas para tipo em suas linhas (_pipeline_) de integração contínua.
656
683
====
657
684
658
-
Nessa seção exploramos as relações de tipos e operações no duck typing e na tipagem nominal, começando com a função simples `double()` — que deixamos sem dicas de tipo.
685
+
Nessa seção exploramos as relações de tipos e operações no duck typing e na tipagem nominal,
686
+
começando com a função simples `double()` — que deixamos sem dicas de tipo.
659
687
Agora vamos dar uma olhada nos tipos mais importantes ao anotar funções.
660
688
661
-
Vamos ver um bom modo de adicionar dicas de tipo a `double()` quando examinarmos <<protocols_in_fn>>. Mas antes disso, há tipos mais importantes para conhecer.((("", startref="FTHsupport08")))((("", startref="THsup08")))
689
+
Vamos ver um bom modo de adicionar dicas de tipo a `double()` quando examinarmos <<protocols_in_fn>>.
690
+
Mas antes disso, há tipos mais importantes para conhecer.((("", startref="FTHsupport08")))((("", startref="THsup08")))
662
691
663
692
664
693
=== Tipos próprios para anotações
665
694
666
-
Quase((("functions, type hints in", "types usable in annotations", id="FTHusable08")))((("type hints (type annotations)", "types usable in", id="THTusable08"))) todos os tipos em Python podem ser usados em dicas de tipo, mas há restrições e recomendações.
695
+
Quase((("functions, type hints in", "types usable in annotations",
todos os tipos em Python podem ser usados em dicas de tipo, mas há restrições e recomendações.
667
698
Além disso, o((("typing module"))) módulo `typing` introduziu constructos especiais com uma semântica às vezes surpreendente.
668
699
669
700
Essa seção trata de todos os principais tipos que você pode usar em anotações:
@@ -679,11 +710,14 @@ Essa seção trata de todos os principais tipos que você pode usar em anotaçõ
679
710
* `typing.Callable`
680
711
* `typing.NoReturn` — um bom modo de encerrar essa lista.
681
712
682
-
Vamos falar de um de cada vez, começando por um tipo que é estranho, aparentemente inútil, mas de uma importância fundamental.
713
+
Vamos falar de um de cada vez, começando por um tipo que é estranho,
714
+
aparentemente inútil, mas de uma importância fundamental.
683
715
684
716
==== O tipo Any
685
717
686
-
A((("Any type", id="anytype08")))((("dynamic type", id="dynamic08")))((("gradual type system", "Any type", id="GTSany08"))) pedra fundamental de qualquer sistema gradual de tipagem é o tipo `Any`, também conhecido como o _tipo dinâmico_.
718
+
A((("Any type", id="anytype08")))((("dynamic type", id="dynamic08")))((("gradual type system", "Any type",
719
+
id="GTSany08"))) pedra fundamental de qualquer sistema gradual de tipagem é o tipo `Any`,
720
+
também conhecido como o _tipo dinâmico_.
687
721
Quando um verificador de tipo vê um função sem tipo como esta:
688
722
689
723
[source, python]
@@ -699,7 +733,8 @@ def double(x: Any) -> Any:
699
733
return x * 2
700
734
----
701
735
702
-
Isso significa que o argumento `x` e o valor de retorno podem ser de qualquer tipo, inclusive de tipos diferentes.
736
+
Isso significa que o argumento `x` e o valor de retorno podem ser de qualquer tipo,
737
+
inclusive de tipos diferentes.
703
738
Assume-se que `Any` pode suportar qualquer operação possível.
704
739
705
740
Compare `Any` com `object`. Considere essa assinatura:
@@ -731,15 +766,21 @@ que implementa menos operações que `abc.MutableSequence`,
731
766
que por sua vez implementa menos operações que `list`.
732
767
733
768
Mas `Any` é um tipo mágico que reside tanto no topo quanto na base da hierarquia de tipos.
734
-
Ele é simultaneamente o tipo mais geral - então um argumento `n: Any` aceita valores de qualquer tipo - e o tipo mais especializado, suportando assim todas as operações possíveis.
769
+
Ele é simultaneamente o tipo mais geral - então um argumento `n: Any` aceita valores de
770
+
qualquer tipo—e o tipo mais especializado, suportando assim todas as operações possíveis.
735
771
Pelo menos é assim que o verificador de tipo entende `Any`.
736
772
737
-
Claro, nenhum tipo consegue suportar qualquer operação possível, então usar `Any` impede o verificador de tipo de cumprir sua missão primária: detectar operações potencialmente ilegais antes que seu programa falhe e levante uma exceção durante sua execução.((("", startref="GTSany08")))
773
+
Claro, nenhum tipo consegue suportar qualquer operação possível,
774
+
então usar `Any` impede o verificador de tipo de cumprir sua missão primária:
775
+
detectar operações potencialmente ilegais antes que
776
+
seu programa levante uma exceção durante a execução.((("", startref="GTSany08")))
738
777
739
778
[[consistent_with_sec]]
740
779
===== Subtipo-de versus consistente-com
741
780
742
-
Sistemas((("gradual type system", "subtype-of versus consistent-with relationships", id="GTSsub08")))((("subtype-of relationships"))) tradicionais de tipagem nominal orientados a objetos se baseiam na relação _subtipo-de_.
781
+
Sistemas((("gradual type system", "subtype-of versus consistent-with relationships",
782
+
id="GTSsub08")))((("subtype-of relationships")))
783
+
tradicionais de tipagem nominal orientados a objetos se baseiam na relação _subtipo-de_.
743
784
Dada uma classe `T1` e uma subclasse `T2`, então `T2` é _subtipo-de_ `T1`.
744
785
745
786
Observe este código:
@@ -760,11 +801,14 @@ o2 = T2()
760
801
f1(o2) # OK
761
802
----
762
803
763
-
A chamada `f1(o2)` é uma aplicação do Princípio de Substituição de Liskov (_Liskov Substitution Principle—LSP_).
804
+
A chamada `f1(o2)` é uma aplicação do Princípio de Substituição de Liskov
805
+
(_Liskov Substitution Principle—LSP_).
764
806
765
807
Barbara Liskovfootnote:[Professora do MIT, designer de linguagens de programação e homenageada com o Turing Award em 2008.
766
-
Wikipedia: https://pt.wikipedia.org/wiki/Barbara_Liskov[Barbara Liskov].] na verdade definiu _é subtipo-de_ em termos das operações suportadas.
767
-
Se um objeto do tipo `T2` substitui um objeto do tipo `T1` e o programa continua se comportando de forma correta, então `T2` é _subtipo-de_ `T1`.
na verdade definiu _é subtipo-de_ em termos das operações suportadas.
810
+
Se um objeto do tipo `T2` substitui um objeto do tipo `T1` e
811
+
o programa continua se comportando de forma correta, então `T2` é _subtipo-de_ `T1`.
768
812
769
813
Seguindo com o código visto acima, essa parte mostra uma violação do LSP:
770
814
@@ -779,17 +823,28 @@ o1 = T1()
779
823
f2(o1) # type error
780
824
----
781
825
782
-
Do ponto de vista das operações suportadas, faz todo sentido: como uma subclasse, `T2` herda e precisa suportar todas as operações suportadas por `T1`. Então uma instância de `T2` pode ser usada em qualquer lugar onde se espera uma instância de `T1`. Mas o contrário não é necessariamente verdadeiro: `T2` pode implementar métodos adicionais, então uma instância de `T1` não pode ser usada onde se espera uma instância de `T2`. Este((("behavioral subtyping"))) foco nas operações suportadas se reflete no nome https://fpy.li/8-15[_behavioral subtyping (subtipagem comportamental)] (EN), também usado para se referir ao LSP.
826
+
Do ponto de vista das operações suportadas, faz todo sentido:
827
+
como uma subclasse, `T2` herda e precisa suportar todas as operações suportadas por `T1`.
828
+
Então uma instância de `T2` pode ser usada em qualquer lugar onde se espera uma instância de `T1`.
829
+
Mas o contrário não é necessariamente verdadeiro:
830
+
`T2` pode implementar métodos adicionais, então uma instância de `T1`
831
+
não pode ser usada onde se espera uma instância de `T2`.
832
+
Este((("behavioral subtyping"))) foco nas operações suportadas se reflete no nome
Em((("consistent-with relationships"))) um sistema de tipagem gradual há outra relação, _consistente-com_ (_consistent-with_), que se aplica sempre que _subtipo-de_ puder ser aplicado, com disposições especiais para o tipo `Any`.
836
+
Em((("consistent-with relationships"))) um sistema de tipagem gradual há outra relação,
837
+
_consistente-com_ (_consistent-with_),
838
+
que se aplica sempre que _subtipo-de_ puder ser aplicado, com regras especiais para o tipo `Any`.
785
839
786
840
As regras para _consistente-com_ são:
787
841
788
842
. Dados `T1` e um subtipo `T2`, então `T2` é _consistente-com_ `T1` (substituição de Liskov).
789
843
. Todo tipo é _consistente-com_ `Any`: você pode passar objetos de qualquer tipo em um argumento declarado como de tipo `Any.
790
844
. `Any` é _consistente-com_ todos os tipos: você sempre pode passar um objeto de tipo `Any` onde um argumento de outro tipo for esperado.
791
845
792
-
Considerando as definições anteriores dos objetos `o1` e `o2`, aqui estão alguns exemplos de código válido, ilustrando as regras #2 e #3:
846
+
Considerando as definições anteriores dos objetos `o1` e `o2`,
847
+
aqui estão alguns exemplos de código válido, ilustrando as regras #2 e #3:
793
848
794
849
[source, python]
795
850
----
@@ -819,7 +874,8 @@ Todo sistema de tipagem gradual precisa de um tipo coringa como `Any`
819
874
[TIP]
820
875
====
821
876
O verbo "inferir" é um sinônimo bonito para "adivinhar", quando usado no contexto da análise de tipos.
822
-
Verificadores de tipo modernos, em Python e outras linguagens, não precisam de anotações de tipo em todo lugar porque conseguem inferir o tipo de muitas expressões.
877
+
Verificadores de tipo modernos, em Python e outras linguagens,
878
+
não precisam de anotações de tipo em todo lugar porque conseguem inferir o tipo de muitas expressões.
823
879
Por exemplo, se eu escrever `x = len(s) * 10`, o verificador não precisa de uma declaração local explícita para saber que `x` é um `int`, desde que consiga encontrar dicas de tipo para `len` em algum lugar.
0 commit comments