web-dev-qa-db-ja.com

Pythonでモジュールとバニラ関数のみを使用する場合に継承を実現するにはどうすればよいですか?

Pythonはパラダイムを強制しません。それはあなたに自由を与えます。 Pythonは、モジュールレベルでのカプセル化を提供します。

モジュールAがあり、同じインターフェースのモジュールBがある場合。 BをAから継承し、Bが提供する一部の機能をオーバーライドするにはどうすればよいですか?

1

Yモジュールをインポートすると、モジュールはXから_from X import *_を使用して関数をインポートします。Xのすべての関数は、Yにあった場合と同じように使用できます(Xの内容をYに貼り付けるなど)。

また、複数のdefがある場合、Pythonは最後のdefを取得します。そのため、関数をオーバーライドするには、Xをインポートした後で、これらの関数を再定義する必要があります。

また、XYの元の関数を使用する場合は、追加の_import X_を追加して、X.functionName()でアクセスできます。


例:

module1.py

_def foo():
   print "this will be overridden"
def bar():
   print "this will be preserved"
_

module2.py

_from module1 import *
import module1

def foo():
   print "This was foo:"
   module1.foo()
   print "But it was overridden."
_

run.py

_import module2

module2.foo()
module2.bar()
_

例の結果:

_-> % python run.py 
This was foo:
this will be overridden
But it was overridden.
this will be preserved
_
5
Marqin

モジュールのポイントは何ですか?

モジュールは、アプリケーションを個別の再利用可能な部分に構造化するメカニズムです。例えば。コマンドラインユーティリティは、引数を解析するコードを別のモジュールに分解し、他のコマンドラインツールでも使用できるようにします。モジュールには明確なパブリックインターフェイスがあり、モジュール内の実装の詳細を隠すことができるため、アプリケーション全体の結合も弱くなります。

同じインターフェイスを持つ別のモジュールにモジュールを交換できますか?

必ずしもそうとは限らず、それがモジュールのポイントではありません。ただし、モジュールを実装するさまざまな手法があります。 1つは、モジュール全体の型を定義する関数ポインターの構造を定義することです。モジュールがロードされると、このモジュールのインスタンスが提供されます。この手法は、JavaScriptでモジュールを模倣するために頻繁に使用されます。

_// a mathModule may be anything that supplies "add" and "sub" functions
var mathModule = (function () {
  var privateVar = "secret";
  function add(a, b) { return a + b; }
  function sub(a, b) { return a - b; }
  function privateFunction() { return "very" + privateVar; }

  // return an “object” (Python: dict) that defines the public interface
  return {
    add: add,
    sub: sub,
  };
})();
_

この手法は、Cに適用して実際のモジュールを提供することもでき、Pythonに簡単に変換できます。ただし、どちらの言語にもラムダがなく、Cにはクロージャーがないため、翻訳された例はより制限されます。

ここでは関数ポインタを扱っているため、重要なレベルの間接参照があります。原則として、同じモジュールインターフェースの複数のインスタンスを一度に持つことができ、同じインターフェースを提供する場合は、別の実装に簡単に切り替えることができます。

これはオブジェクト指向プログラミングとよく似ていますが、いくつかの重要な詳細が欠落しています。OOPの場合、インスタンス構造にはメソッドに加えてデータも含まれている必要があり、インスタンス構造を引数として、呼び出されたメソッドに渡す必要があります。 クロージャーからJavaScriptオブジェクトシステムを作成する については、ブログの投稿で詳しく説明しています。

Pythonのモジュールは次のように機能しますか?

やや。実際、Pythonのモジュールは、self引数がないことを除いて、通常のオブジェクトと非常によく似ています。ただし、Pythonには、モジュールの明確なインターフェイスを実際に定義する部分がありません。上記のJavaScriptの例で、パブリックインターフェイスを介して関数を公開するかどうかを明示的に選択できる場合、Pythonは、モジュールをimportingするときにファイルスコープ内のすべてのシンボルにアクセスできるようにします。この問題を制限するトリック(ヘルパーモジュールで関数を定義し、パブリックインターフェイスをモジュールの___init__.py_に選択的にインポートするなど)がありますが、Pythonでは、ドキュメントを通じてインターフェイスを定義することになります、コードではなく。

要するに、Pythonには「モジュール」と呼ばれる名前空間がありますが、カプセル化は提供されていません。

では、どのようにしてモジュールを継承できますか?

「継承」の概念は、OOPの用語に近いため、モジュールについて説明する場合は意味がありません。ここで、「SubclassBaseから継承」は、(a)SubclassBaseのサブタイプであること、および(b)Subclassであることを意味しますインスタンスはBaseで定義された実装を何らかの方法で使用できます。ご存知かもしれませんが、実装の再利用は、実装の継承(Subclassのメソッド検索メカニズムがBaseのメソッドで初期化される)と構成(ここでSubclassBaseインスタンスにデリゲートします)。

Module-as-dictエンコーディングを使用すると、次のように説明できます。

_# The original module.
# Can be instantiated like `mathModule = mathModule_load()`
def mathModule_load():
  private_var = "secret"
  def add(a, b): return a + b
  def sub(a, b): return a - b
  def private_function(): return "very" + private_var

  return {
    'add': add,
    'sub': sub,
  }

# Inherit the mathModule via composition
# Overrides "add" to type-check for int arguments
def checkedMathModule_load():
  base = mathModule_load() # the module instance we inherit

  def add(a, b):
    if type(a) is not int or type(b) is not int:
      raise TypeError("give me ints!")
    return base['add'](a, b)

  def sub(a, b):
    return base['sub'](a, b)

  # return our own module instance that meets the expected interface
  return {
    'add': add,
    'sub': sub,
  }

# Inherit the mathModule by changing the module definition
# Overrides "add" to emit debug info
def chattyMathModule_load():
  module = mathModule_load() # the module instance we inherit

  original_add = module['add']
  def my_add(a, b):
    print("DEBUG: add(%d, %d)" % (a, b))
    return original_add(a, b)
  module['add'] = my_add

  return module
_

OK、組み込みモジュールでこれを実行できますか?

* grumble * Pythonは正式にはマルチパラダイムになる可能性がありますが、このような問題に対するOOPソリューションが非常に明確に支持されます。上記の例を書き直して、Pythonの組み込みモジュールを使用することもできますが、これはきれいではありません。モジュールはグローバルであるため、Pythonのモジュールには「インスタンス」という同等の概念がないことに注意してください。

モジュールの関数を格納する明示的なdictの代わりに、通常のPython構文を使用して、ファイルスコープにシンボルを追加します。

モジュールは、次のファイル階層で構成されています。

_mathModule/
  __init__.py
  detail.py
checkedMathModule/
  __init__.py
  detail.py
chattyMathModule/
  __init__.py
  detail.py
_

_mathModule/__init__.py_:

_from .detail import add, sub
del detail
_

_mathModule/detail.py_:

_private_var = "secret"

def add(a, b):
  return a + b

def sub(a, b):
  return a - b

def private_function():
  return "very" + private_var
_

_checkedMathModule/__init__.py_:

_# this module offers the same interface as mathModule
from .detail import add, sub
del detail
_

_checkedMathModule/detail.py_:

_import mathModule as base

def add(a, b):
  if type(a) is not int or type(b) is not int:
    raise TypeError("give me ints!")
  return base.add(a, b)

def sub(a, b):
  return base.sub(a, b)
_

_chattyMathModule/__init__.py_:

_from mathModule import *

# now import the function we want to override
from .detail import my_add as add
del detail
_

_chattyMathModule/detail.py_:

_from mathModule import add as original_add

def my_add(a, b):
  print("DEBUG: add(%d, %d)" % (a, b))
  return original_add(a, b)
_

_del detail_が何も壊さないかどうかはわかりませんが、それがないと、ユーザーはmathModule.detail.private_function()を実行して_'verysecret'_を取得でき、カプセル化が完全に破られます。

これらのテクニックは一種の仕事ですが、通常のオブジェクトではるかに簡単に行うことができる何かのための多くの不必要な努力です。また、Pythonは特別です。これは、モジュールがas valuesで渡すことができるモジュールオブジェクトとして表されるのに対し、ほとんどの言語のモジュールシステムははるかに静的です。これらの手法は、モジュール式プログラミングを表すものではなく、「_を理解する以外に建設的ではないOOP言語の一部を使用せずにOOPを実行したい」という考え方の代表的なものですOOPより良い。

3
amon