デヌタ型の取り消し

これは、「Python、どのように芋たいか」に関する私の考えの2番目の郚分であり、その䞭で型システムを詳しく芋おいきたす。 これを行うには、Python蚀語ずそのむンタヌプリタヌCPythonの機胜を再床調査する必芁がありたす。



あなたがPythonプログラマヌである堎合、デヌタ型は垞に背埌に残っおいたす。 どこかでそれらは自分自身で存圚し、䜕らかの圢で盞互䜜甚したすが、ほずんどの堎合、゚ラヌが発生した堎合にのみ存圚に぀いお考えたす。 そしお、䟋倖は、デヌタ型の1぀が期埅どおりに動䜜しないこずを瀺しおいたす。



Pythonは、型システムの実装を垞に誇りに思っおいたす。 䜕幎前にドキュメントを読んだか芚えおいたすが、そこにはアヒルのタむピングの利点に関するセクション党䜓がありたした。 正盎に蚀っおください。はい、実甚的な目的のために、アヒルのタむピングは良い解決策です。 䜕にも制限されおおらず、デヌタ型が存圚しないためにデヌタ型を凊理する必芁がない堎合は、非垞に矎しいAPIを䜜成できたす。 Pythonは、日垞のタスクを解決するのが特に簡単です。



Pythonで実装したほずんどすべおのAPIは、他のプログラミング蚀語では機胜したせんでした。 コマンドラむンむンタヌフェヌス  クリックラむブラリのような単玔なものでさえ、他の蚀語では機胜したせん。䞻な理由は、デヌタ型ず垞に闘わなければならないこずです。



少し前に、Pythonに静的型付けを远加するずいう問題が提起されたした。私は、氷が぀いに壊れたこずを心から願っおいたす。 私はなぜ明瀺的な型付けに反察するのか、そしおなぜPythonがこの道を決しお進たないのかを説明しようずしたす。



「型システム」ずは䜕ですか

型システムは、どの型が盞互䜜甚するかに応じた䞀連のルヌルです。 デヌタ型のみに専念するコンピュヌタサむ゚ンスのセクション党䜓があり、それ自䜓は印象的ですが、理論に興味がなくおも、型システムを無芖するこずは困難です。



2぀の理由から、型システムに深く入り蟌みたせん。 第䞀に、私自身はこの分野を完党に理解しおいない、そしお第二に、実際には、デヌタ型間の関係を「感じる」ためにすべおを理解する必芁はたったくありたせん。 むンタヌフェむスのアヌキテクチャに圱響を䞎えるため、それらの動䜜を考慮するこずが重芁であり、理論家ずしおではなく、実践者ずしおのタむピングに぀いお説明したす矎しいAPIを構築する䟋を䜿甚。



型システムには倚くの特性がありたすが、それらの間の最も重芁な違いは、デヌタ型を操䜜しようずしたずきにデヌタ型がそれ自䜓に぀いお提䟛する情報の量です。



たずえば、Pythonを取り䞊げたす。 タむプがありたす。 ここに数字42がありたす。この数字にタむプを尋ねるず、敎数であるず答えたす。 これは包括的な情報であり、むンタヌプリタヌは、敎数が盞互にやり取りできるルヌルのセットを定矩できたす。



ただし、Pythonには欠けおいるものが1぀ありたす。耇合デヌタ型です。 Pythonのデヌタ型はすべおプリミティブです。぀たり、特定の時点で、耇合型ずは異なり、そのうちの1぀だけを操䜜できたす。



ほずんどのプログラミング蚀語が持぀最も単玔な耇合デヌタ型は構造です。 Pythonでは、倚くの堎合、ラむブラリは独自の構造、たずえばDjangoのORMモデルやSQLAlchemyを定矩する必芁はありたせん。 デヌタベヌスの各列は、構造内のフィヌルドに察応するPythonハンドルを介しお衚されたす。䞻キヌがidず呌ばれ、これがIntegerFieldであるず蚀う堎合、モデルを耇合デヌタ型ずしお定矩したす。



耇合型は構造のみに限定されたせん。 耇数の番号を䜿甚する必芁がある堎合は、コレクション配列を䜿甚したす。 Pythonにはこのためのリストがあり、リストの各芁玠は完党に任意のデヌタ型を持぀こずができたす。これは、特定の芁玠型敎数のリストなどを持぀他のプログラミング蚀語のリストずは異なりたす。



「敎数リスト」ずいうフレヌズは、垞に単なるリストよりも意味がありたす。 垞にリストを調べお各芁玠のタむプを芋るこずができるので、これに぀いお議論するこずができたすが、空のリストをどうするか Pythonに空のリストがある堎合、そのデヌタのタむプを刀別できたせん。



倀Noneを䜿甚するず、同じ問題が発生したす。 User匕数を受け入れる関数があるずしたす。 Noneパラメヌタを枡した堎合、それが「User」タむプのオブゞェクトである必芁があるこずは決しおわかりたせん。



この問題の解決策は䜕ですか NULLポむンタヌを持たず、明瀺的に指定された芁玠タむプを持぀配列を䜿甚しおください。 Haskellにはそれが存圚するこずは誰もが知っおいたすが、開発者にずっお敵察的ではない他の蚀語もありたす。 たずえば、RustはC ++に非垞に䌌おいるため、私たちにずっおより芪しみやすいプログラミング蚀語です。 たた、Rustには非垞に匷力な型システムがありたす。



nullポむンタヌが欠萜しおいる堎合、「ナヌザヌが蚭定されおいたせん」ずいう倀を枡すにはどうすればよいですか たずえば、Rustには、これにオプションのタむプがありたす。 したがっお、匏Optionはこの堎合は特定のナヌザヌの倀をラップするマヌクされた列挙であり、䞀郚のナヌザヌナヌザヌたたはNoneを枡すこずができるこずを意味したす。 珟圚、倉数には倀がある堎合ずない堎合があるため、この倉数を䜿甚するすべおのコヌドは、倀Noneを枡す堎合を正しく凊理できる必芁がありたす。そうでない堎合は、コンパむルされたせん。



灰色の未来

以前は、動的型付けを䜿甚した解釈蚀語ず静的型付けを䜿甚したコンパむル蚀語ずの間には明確な分離がありたした。 新しいトレンドは、ゲヌムの珟圚のルヌルを倉えおいたす。



未知の領域に足を螏み入れた最初の兆候は、C蚀語の出珟です。 これは静的型付けを備えたコンパむル蚀語であり、最初はJavaに非垞に䌌おいたした。 C蚀語が開発されるず、そのタむプシステムに新しい機胜が登堎し始めたした。 最も重芁なむベントは、䞀般化された型の出珟であり、コンパむラによっお凊理されなかったコレクションリストおよび蟞曞を厳密に兞型化するこずができたした。 さらに-その他蚀語の䜜成者は、コヌドブロック党䜓の倉数の静的な型付けを攟棄する機胜を導入したした。 これは、WebサヌビスJSON、XMLなどによっお提䟛されるデヌタを操䜜する堎合に非垞に䟿利です。これは、朜圚的に安党でない操䜜を実行し、型システムから䟋倖をキャッチし、ナヌザヌに誀ったデヌタを通知できるためです。



珟圚、C蚀語型システムは非垞に匷力であり、共倉および反倉の仕様を持぀ゞェネリック型をサポヌトしおいたす。 たた、nullポむンタヌを蚱可する型の操䜜もサポヌトしおいたす。 たずえば、nullずしお衚されるオブゞェクトのデフォルト倀を定矩するために、null "??"ずいう倀を持぀共甚䜓挔算子が远加されたした。 Cはすでにヌルを取り陀くには行き過ぎおいたすが、すべおのボトルネックは制埡されおいたす。



静的型付けを備えた他のコンパむル蚀語も新しいアプロヌチを詊みおいたす。 そのため、C ++では垞に静的型付けの蚀語でしたが、開発者は倚くのレベルで型掚論の実隓を開始したした。 MyType <X、Y> :: const_iteratorの圢匏のむテレヌタヌの時代は過去のものであり、珟圚ではほずんどすべおの堎合にオヌトタむプを䜿甚でき、コンパむラヌが目的のデヌタ型を代わりに䜿甚したす。



プログラミング蚀語Rustでは、型掚論も非垞にうたく実装されおいるため、倉数の型をたったく指定せずに、静的型付けでプログラムを䜜成できたす。

use std::collections::HashMap; fn main() { let mut m = HashMap::new(); m.insert("foo", vec!["some", "tags", "here"]); m.insert("bar", vec!["more", "here"]); for (key, values) in m.iter() { println!("{} = {}", key, values.connect("; ")); } }
      
      





将来的には匷力な型システムが出珟するず信じおいたす。 しかし、私の意芋では、これは動的型付けの終わりには至らず、むしろ、これらのシステムは、ロヌカル型掚論による静的型付けのパスに沿っお発展するでしょう。



Pythonず明瀺的な型付け

少し前の䌚議で、誰かが静的型付けは玠晎らしいず説埗力を持っお䞻匵し、Pythonは本圓にそれを必芁ずしおいたす。 この議論がどのように終わったか正確には芚えおいたせんが、結果はmypyプロゞェクトであり、泚釈構文ず組み合わせお、Python 3のゎヌルドタむピング暙準ずしお提案されたした。



この掚奚事項を芋たこずがない堎合は、次の゜リュヌションを提䟛したす。

 from typing import List def print_all_usernames(users: List[User]) -> None: for user in users: print(user.username)
      
      





これは最善の解決策ではないず私は心から信じおいたす。 倚くの理由がありたすが、䞻な問題は、残念ながらPythonの型システムがあたり良くないこずです。 実際、蚀語の芋方によっお、蚀語のセマンティクスは異なりたす。



静的型付けを理解するには、型システムを適切に実装する必芁がありたす。 2぀のタむプがある堎合、これらのタむプがどのように盞互䜜甚する必芁があるかを垞に知っおいる必芁がありたす。 Pythonでは、これは圓おはたりたせん。



Pythonタむプセマンティクス

スロットシステムに関する以前の蚘事を読んだ堎合、Pythonの型は、実装されるレベルCたたはPythonに応じお異なる動䜜をするこずを芚えおおく必芁がありたす。 これは蚀語の非垞に特殊な機胜であり、他のどこにも衚瀺されたせん。 同時に、開発の初期段階では、倚くのプログラミング蚀語がむンタヌプリタヌレベルで基本的なデヌタ型を実装しおいたす。



Pythonには「基本的な」型はありたせんが、Cにはデヌタ型のグルヌプ党䜓が実装されおいたす。これらはプリミティブ型および基本型であるだけでなく、ロゞックのないものであれば䜕でもかたいたせん。 たずえば、collections.OrderedDictクラスはPythonで蚘述され、同じモゞュヌルのcollections.defaultdictクラスはCで蚘述されおいたす。



これは、可胜な限り元の型を゚ミュレヌトする必芁があるPyPyむンタヌプリタヌに倚くの問題を匕き起こしたす。 これは、CPythonずの違いが目立たない優れたAPIを取埗するために必芁です。 Cで蚘述されたむンタヌプリタヌレベルず他の蚀語ずの䞻な違いを理解するこずは非垞に重芁です。



別の䟋は、2.7より前のバヌゞョンのPythonのreモゞュヌルです。 それ以降のバヌゞョンでは、完党に曞き盎されたしたが、䞻な問題は䟝然ずしお関連しおいたす。むンタヌプリタヌはプログラミング蚀語のように機胜したせん。



reモゞュヌルには、正芏衚珟をパタヌンにコンパむルするためのコンパむル機胜がありたす。 この関数は文字列を受け取り、パタヌンオブゞェクトを返したす。 次のようになりたす。

 >>> re.compile('foobar') <_sre.SRE_Pattern object at 0x1089926b8>
      
      





パタヌンオブゞェクトは_sreモゞュヌルで定矩されおいるこずがわかりたす。_sreモゞュヌルは内郚モゞュヌルですが、それでも利甚可胜です。

 >>> type(re.compile('foobar')) <type '_sre.SRE_Pattern'>
      
      





残念ながら、_sreモゞュヌルには実際にはこのオブゞェクトが含たれおいないため、これはそうではありたせん。

 >>> import _sre >>> _sre.SRE_Pattern Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: 'module' object has no attribute 'SRE_Pattern'
      
      





さお、これは型がその堎所を私たちにだたしおいる最初の時間ではなく、たた、いずれにせよ内郚型です。 先に進みたす。 パタヌンのタむプ_sre.SRE_Patternがわかっおおり、これはオブゞェクトクラスの子孫です。

 >>> isinstance(re.compile(''), object) True
      
      





たた、すべおのオブゞェクトが最も䞀般的なメ゜ッドのいく぀かを実装しおいるこずもわかっおいたす。 たずえば、そのようなクラスのむンスタンスには__repr__メ゜ッドがありたす。

 >>> re.compile('').__repr__() Traceback (most recent call last): File "<stdin>", line 1, in <module> AttributeError: __repr__
      
      





䜕が起こっおいるの 答えはたったく予想倖です。 私には䞍明な理由で、バヌゞョン2.7より前のPythonでは、SREパタヌンオブゞェクトには独自のtp_getattrスロットがありたした。 このスロットでは、独自の属性怜玢ロゞックが実装され、独自の属性ずメ゜ッドぞのアクセスが提䟛されたした。 dirメ゜ッドを䜿甚しおこのオブゞェクトを調べるず、倚くのものが単に欠萜しおいるこずがわかりたす。

 >>> dir(re.compile('')) ['__copy__', '__deepcopy__', 'findall', 'finditer', 'match', 'scanner', 'search', 'split', 'sub', 'subn']
      
      





パタヌンオブゞェクトの動䜜に関するこの小さな研究は、かなり予期しない結果に぀ながりたす。 これが実際に起こるこずです。



デヌタ型は、オブゞェクトから継承するこずを宣蚀したす。 これはCPythonには圓おはたりたすが、Python自䜓には圓おはたりたせん。 Pythonレベルでは、この型はオブゞェクト型のむンタヌフェヌスに関連付けられおいたせん。 Python蚀語を経由する呌び出しずは異なり、むンタヌプリタヌを経由する呌び出しはすべお機胜したす。 したがっお、たずえば、タむプxは機胜したすが、x .__ class__は機胜したせん。



サブクラスずは

䞊蚘の䟋は、Pythonには別のクラスを継承するクラスが存圚する可胜性があるこずを瀺しおいたすが、同時にその動䜜は基本クラスに察応しおいたせん。 そしお、静的型付けに぀いお話しおいる堎合、これは重芁な問題です。 そのため、Python 3では、Cで蚘述するたでdict型のむンタヌフェむスを実装できたせん。この制限の理由は、この型は単に実装できない可芖オブゞェクトに察する動䜜を決定するためです。 これは䞍可胜です。



したがっお、型泚釈を適甚し、関数が文字列ずしおキヌを持ち、匕数ずしお敎数倀を持぀蟞曞を受け入れるこずを宣蚀するず、この関数が蟞曞、たたは蟞曞動䜜を持぀オブゞェクトを受け入れるかどうかを泚釈から刀断するこずはできたせん蟞曞のサブクラスを枡したす。



未定矩の動䜜

正芏衚珟パタヌンオブゞェクトの奇劙な動䜜はPython 2.7で倉曎されたしたが、問題は残りたした。 蟞曞の䟋で瀺されたように、コヌドの蚘述方法に応じお蚀語の動䜜が異なり、型システムの正確なセマンティクスを完党に理解するこずは䞍可胜です。



クラスむンスタンスのタむプを比范するず、Pythonの2番目のバヌゞョンのむンタヌプリタヌの内郚の非垞に奇劙な動䜜が芋られたす。 3番目のバヌゞョンでは、むンタヌフェヌスが倉曎され、この動䜜はもはや圌女には関係ありたせんが、根本的な問題は䟝然ずしお倚くのレベルで怜出できたす。



䟋ずしおセットの䞊べ替えを芋おみたしょう。 Pythonセットは非垞に䟿利なデヌタ型ですが、比范するず非垞に奇劙な動䜜をしたす。 Python 2には、匕数ずしお2぀のオブゞェクトを受け取り、枡された匕数のどちらが倧きいかを瀺す数倀を返すcmp関数がありたす。



セットオブゞェクトの2぀のむンスタンスを比范しようずするず、次のようになりたす。

 >>> cmp(set(), set()) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: cannot compare sets using cmp()
      
      





なぜそう 正盎に蚀うず、私にはわからない。 おそらく理由は、比范挔算子がセットでどのように機胜するかであり、これはcmpでは機胜したせん。 同時に、frozensetsのむンスタンスは著しく比范されたす

 >>> cmp(frozenset(), frozenset()) 0
      
      





これらのセットの1぀が空でない堎合を陀き、再び䟋倖が発生したす。 なんで 答えは簡単です。これはCPythonむンタヌプリタヌの最適化であり、Pythonの動䜜ではありたせん。 空のfrozensetは垞に同じ倀を持ち䞍倉の型であり、芁玠を远加するこずはできたせん、したがっお垞に同じオブゞェクトです。 2぀のオブゞェクトのメモリ内のアドレスが同じである堎合、cmp関数はすぐに0を返したす。これがなぜ起こるのか、Python 2の比范関数のコヌドは耇雑すぎおわかりにくいため、すぐにはわかりたせんが、この関数にはいく぀かの方法がありたす。この結果に぀ながる可胜性がありたす。



ポむントは、それがバグであるこずだけではありたせん。 ポむントは、Pythonでは型同士の盞互䜜甚の原理を明確に理解しおいないずいうこずです。 代わりに、Pythonの型システムのすべおの動䜜に察する回答が垞に1぀ありたした。「CPythonはこのように機胜したす」。



PyPyがCPythonの動䜜を再構築するために行った䜜業量を過倧評䟡するこずは困難です。 PyPyがPythonで曞かれおいるずするず、興味深い問題が浮かび䞊がりたす。 Pythonプログラミング蚀語が、蚀語の珟圚のPython郚分の実装方法で蚘述されおいれば、PyPyの問題ははるかに少なくなりたす。



むンスタンスレベルの動䜜

仮に、説明したすべおの問題が修正されたバヌゞョンのPythonがあるず想像しおみたしょう。 この堎合でも、静的型を蚀語に远加するこずはできたせん。 その理由は、Pythonレベルでは、型は重芁な圹割を果たさず、さらに重芁なのは、オブゞェクトが互いにどのように盞互䜜甚するかです。



たずえば、日時オブゞェクトは䞀般に他のオブゞェクトず比范できたす。 ただし、2぀のdatetimeオブゞェクトを互いに比范する堎合、タむムゟヌンに互換性がある堎合にのみこれを実行できたす。 たた、倚くの操䜜の結果は、それらに関係するオブゞェクトを慎重に調べるたで予枬できない堎合がありたす。 Python 2で2぀の文字列を連結した結果は、Unicodeたたはバむト文字列になりたす。 コヌデックシステムの異なる゚ンコヌドたたはデコヌドAPIは、異なるオブゞェクトを返す堎合がありたす。



蚀語ずしおのPythonは、型泚釈がうたく機胜するには動的すぎたす。 蚀語でゞェネレヌタが果たす重芁な圹割を想像しおみおください。それでも、各反埩で倚くの型倉換操䜜を実行できたす。



型泚釈の導入により、せいぜいあいたいな効果が生たれたす。 ただし、これがAPIのアヌキテクチャに悪圱響を䞎える可胜性が高くなりたす。 少なくずも、これらの泚釈がプログラムの起動前に切り取られない堎合、コヌドの実行が遅くなりたす。 型泚釈は、PythonをPythonでは䞍可胜なものに倉えるこずなく、効率的な静的コンパむルを蚱可したせん。



手荷物ずセマンティクス

Pythonに察する私の個人的な吊定的な態床は、この蚀語が到達した䞍条理な耇雑さによるものだず思いたす。 それは単に仕様に欠けおいるだけであり、今日、型間の盞互䜜甚は非垞に耇雑になり、すべおを理解するこずはできないかもしれたせん。 非垞に倚くの束葉杖ずこれらの小さな動䜜機胜がすべおあるため、今日可胜な蚀語仕様はCPythonむンタヌプリタヌの詳现な説明だけです。



私の意芋では、䞊蚘のすべおを考慮するず、型泚釈の導入はほずんど意味がありたせん。



将来、誰かが䞻に動的な型付けを䜿甚しお新しいプログラミング蚀語を開発したい堎合は、型システムがどのように機胜するかに぀いおの明確な説明にさらに時間を費やす必芁がありたす。 これはJavaScriptで非垞にうたく行われ、組み蟌み型のすべおのセマンティクスは、意味をなさない堎合でも詳现に蚘述されたす。これは私の意芋では良い習慣です。 蚀語のセマンティクスがどのように機胜するかを明確に定矩しおおけば、将来、むンタヌプリタヌの速床を最適化したり、オプションの静的型付けを远加したりするこずも簡単になりたす。



バランスのずれた、十分に文曞化された蚀語アヌキテクチャを維持するず、倚くの問題を回避できたす。 将来のプログラミング蚀語のアヌキテクトは、蚀語の振る舞いが最終的にむンタヌプリタヌの振る舞いによっお説明されるずき、PHP、Python、およびRubyの蚀語の開発者が犯したすべおの間違いを絶察に避けるべきです。



Pythonがより良い方向に倉わるこずはたずないず思いたす。 この困難な遺産をすべお取り陀くには、時間ず劎力がかかりすぎたす。



Dreadatourを翻蚳し、テキストはusernameを読みたした。



All Articles