9


2

requireとvsの長所と短所は何ですか? コードを読み込む方法をインポートしますか?

Rubyは「require」を使用し、Pythonは「import」を使用します。 それらは実質的に異なるモデルであり、「require」モデルには慣れていますが、「import」がより好きだと思ういくつかの場所を見ることができます。 これらの各モデルで、人々が特に簡単だと思うもの、またはもっと興味深いことに、本来あるべきものよりも難しいものを見つけたいと思っています。

特に、新しいプログラミング言語を書いている場合、コード読み込みメカニズムをどのように設計しますか? デザインの選択で最も重要な「長所」と「短所」はどれですか?

4 Answer


14


Pythonの `import`には、_importを見つける方法と_what名前空間の下に含める_という2つのことを結び付けるという大きな特徴があります。

これにより、非常に明示的なコードが作成されます。

「xml.saxをインポートする」

Python検索パスのルールにより、使用するコードの場所を指定します。

同時に、アクセスするすべてのオブジェクトは、この正確なネームスペース、たとえば `xml.sax.ContentHandler`の下でライブになります。

私はこれをRubyの要求に対する利点と考えています。 require 'xml'`は、実際には、名前空間 XML`またはモジュール内で利用可能な他の名前空間内にオブジェクトを作成しますが、これはrequire行から直接明らかではありません。

`xml.sax.ContentHandler`が長すぎる場合、インポート時に別の名前を指定できます。

import xml.sax as X

そして、今では `X.ContentHandler`の下で利用可能です。

この方法では、Pythonでは各モジュールのネームスペースを明示的に構築する必要があります。 したがって、Python名前空間は非常に「物理的」であり、私が意味することを説明します。

  • デフォルトでは、モジュールで直接定義された名前のみが その名前空間:関数、クラスなど。

  • モジュールの名前空間に追加するには、明示的に名前をインポートします 追加し、それらを(参照により)現在のモジュールに「物理的に」配置します。

たとえば、内部サブモジュール machine`と interface`を持つ小さなPythonパッケージ "process"があり、これをパッケージ名の直下にある便利な名前空間として提示したい場合、これは記述可能な例です。 「パッケージ定義」ファイル process / init 。py

from process.interface import *
from process.machine import Machine, HelperMachine

したがって、通常は「process.machine.Machine」としてアクセス可能なものを「process.Machine」まで持ち上げます。 そして、非常に明示的な方法で、すべての名前を「process.interface」から「process」名前空間に追加します。

私が書いたPythonのインポートの利点は2つだけでした。

  • 「インポート」を使用するときに含めるものを*クリア*

  • *明示的*独自のモジュールの名前空間を変更する方法(プログラム用) または他の人がインポートするため)


2


require`の素晴らしい特性は、実際に Kernel`で定義されたメソッドであることです。 したがって、それをオーバーライドして、Ruby用の独自のパッケージングシステムを実装できます。 Rubygemsはそうです!

PS:_私はここでモンキーパッチを販売していませんが、Rubyのパッケージシステムをユーザーが書き換えることができるという事実(たとえPythonのシステムのように動作する場合でも)。 新しいプログラミング言語を書くとき、すべてを正しくすることはできません。 したがって、インポートメカニズムが言語内から完全に(完全にすべての方向に)拡張可能な場合、将来のユーザーに最高のサービスを提供します。 それ自体から完全に拡張可能でない言語は、進化の行き止まりです。 これはMatzがRubyで正しかったことの1つだと思います。


1


免責事項、私は決してPythonの専門家ではありません。

「require」が「import」よりも優れている最大の利点は、名前空間とファイルパス間のマッピングを理解する必要がないことです。 明らかです。これは単なる標準のファイルパスです。

私は「インポート」が持つ名前空間に重点を置いていることを本当に気に入っていますが、この特定のアプローチが柔軟性に欠けないのではないかと思わずにはいられません。 私が知る限り、Pythonでモジュールの命名を制御する唯一の手段は、インポートされるモジュールのファイル名を変更するか、「as」の名前変更を使用することです。 さらに、明示的な名前空間では、完全修飾識別子で何かを参照する手段がありますが、暗黙的な名前空間では、モジュール自体の内部でこれを行う手段がなく、潜在的なあいまいさを引き起こす可能性があります名前を変更せずに解決することは困難です。

つまり、 `foo.py`で:

class Bar:
  def myself(self):
    return foo.Bar

これは失敗します:

Traceback (most recent call last):
  File "", line 1, in ?
  File "foo.py", line 3, in myself
    return foo.Bar
NameError: global name 'foo' is not defined

どちらの実装も検索する場所のリストを使用します。これは、選択したモデルに関係なく、非常に重要なコンポーネントであると思います。

「require」のようなコード読み込みメカニズムが使用されていたが、言語にグローバルな名前空間がなかった場合はどうなりますか? つまり、あらゆる場所で名前空間を指定する必要がありますが、開発者はクラスが定義されている名前空間を完全に制御でき、その名前空間宣言はファイル名ではなくコード内で明示的に行われます。 または、グローバル名前空間で何かを定義すると、警告が生成されます。 それは両方の世界で最高のアプローチですか、それとも私が見逃している明らかな欠点がありますか?


1


Pythonのインポートは非​​常に明示的な種類の名前空間を提供します。名前空間はパスであり、定義を行う名前空間を知るためにファイルを調べる必要はありません。また、ファイルは名前空間定義で散らかっていません。 これにより、アプリケーションの名前空間スキームを簡単かつ迅速に理解できるようになり(ソースツリーを見るだけ)、名前空間宣言の入力ミスなどの単純なミスを回避できます。

良い副作用は、すべてのファイルに独自のプライベート名前空間があるため、名前を付けるときに競合を心配する必要はありません。

名前空間も煩わしくなり、 some.module.far.far.away.TheClass()`のようなものがあると、コードが非常に長くなり、入力するのが面倒になります。 これらの場合、「インポート…​ from …​ `現在の名前空間に別の名前空間のビットを挿入します。 インジェクションがインポートするモジュールと競合する場合、インポートしたものの名前を変更できます: `from some.other.module import Bar as BarFromOtherModule

Pythonは、循環インポートのような問題に対して依然として脆弱ですが、これらのケースで非難されるべき言語よりもアプリケーション設計です。

そのため、PythonはC ++の「名前空間」と「#include」を取り、その上で大きく拡張しました。 一方、rubyの module`と require`がこれらに新しいものを追加する方法はわかりませんが、グローバルな名前空間の乱雑さのようなまったく同じ恐ろしい問題があります。