2016-09-14 2 views
7

私は何人かの人には比較的愚かなものを探していましたが、私にとってはとても興味深いものでした。 :-)OSError例外クラスのサブクラスのインスタンスを返すロジックはどこですか?

Python 3.3では入力エラーと出力エラーがOSErrorとマージされているため、例外クラス階層が変更されています。組み込みクラスOSErrorについての興味深い機能を使用すると、のサブクラスであるOSErrorリターンFileNotFoundErrorインスタンスのコンストラクタにerrnostrerrorを渡して見ることができるようにerrno

>>> OSError(2, os.strerror(2)) 
FileNotFoundError(2, 'No such file or directory') 

>>> OSError(2, os.strerror(2)).errno 
2 
>>> OSError(2, os.strerror(2)).strerror 
'No such file or directory' 

strerrorを通過したとき、それはそれのサブクラスを返す、ということですOSError

パイソンDOC:OS以下exceptionsに記載されるように

コンストラクタは、多くの場合、実際に、OSErrorのサブクラスを返します。特定のサブクラスは最終的なerrno値に依存する。 この現象は、OSErrorを直接またはエイリアス経由で構築する場合にのみ発生し、サブクラス化する際に継承されません。

このように動作するサブクラスをコーディングしたかったのです。ほとんどの場合好奇心であり、実際のコードではありません。私はまた、サブクラスオブジェクトを作成するロジックはどこですか、それは例えば__new__でコードされていることを知りたいですか?

>>> class A(OSError): pass 
>>> A(2, os.strerror(2)) 
A(2, 'No such file or directory') 

その後、型チェックがなければならない:__new__は、サブクラスのインスタンスを作成するためのロジックが含まれている場合__new__で型チェックのいくつかの並べ替えがあるかどういない限り、OSErrorから継承することは一般的に、この動作を返します

# If passed OSError, returns subclass instance 
>>> A.__new__(OSError, 2, os.strerror(2))   
FileNotFoundError(2, 'No such file or directory') 

# Not OSError? Return instance of A 
>>> A.__new__(A, 2, os.strerror(2) 
A(2, 'No such file or directory') 

私はこのコードがどこに配置されているかを知るためにCコードを掘り下げています。私はC言語の専門家ではないので、これは本当にロジックだと思っています率直でなければならない):

あなたはなぜあなたは私に言わせれば OSErroは、ユーザーが定義したクラスのインスタンスを作ることができるように、今私は、Cのコードで遊んなしのPython自体から errnomapの拡大の可能性について思ったんだけど

exceptions.c

if (myerrno && PyLong_Check(myerrno) && 
    errnomap && (PyObject *) type == PyExc_OSError) { 
    PyObject *newtype; 
    newtype = PyDict_GetItem(errnomap, myerrno); 
    if (newtype) { 
     assert(PyType_Check(newtype)); 
     type = (PyTypeObject *) newtype; 
    } 
    else if (PyErr_Occurred()) 
     goto error; 
} 
} 

それ?私は、ただ楽しむために言っています。

+0

'OSError .__ new__'をmonkeypatchする必要がありますが、組み込み/拡張型の属性は設定できません。基本的にOSErrorをサブクラス化してそこにモンキーを入れなければならないが、それはあなたが望むものではないと思う。要約すると、ユーザー定義クラスでそれをやっていますか?それほど難しくない。それを組み込みの上でやっています - そう簡単ではありません。 –

+0

また、質問を言い換えると、_ OSError例外クラスのサブクラスのインスタンスを返すロジックがありますか?それはすでに見つかりました。ビルトインでそれをすることは重要ですか?または、ユーザー定義のクラスでそれを行う一般的な概念はうまくいくでしょうか? –

+0

@ŁukaszRogalski私はおそらくタイトルを変更する必要がありますが、私は今どのように質問をしているのかは分かりません。 – direprobs

答えて

0

OSErrorの動作をPythonから変更することはできません。これはPythonで実装されていないためです。

Pythonで実装されたクラスでは、__new__と書くことができるので、ベースクラスで呼び出されている場合にのみサブクラスを返します。その後、その動作は継承されません。

class MyClass(object): 
    def __new__(cls, sub=0, _subtypes={}): 
     if cls is MyClass: 
      if sub not in _subtypes: 
       _subtypes[sub] = type("MyClass(%s)" % sub, (MyClass,), {}) 
      return _subtypes[sub](sub) 
     return object.__new__(cls, sub) 
    def __init__(self, sub): 
     assert type(self).__name__ == "MyClass(%s)" % sub 

class SubClass(MyClass): 
    def __init__(self, sub=None): 
     assert type(self).__name__ == "SubClass" 

print(MyClass(1)) # <__main__.MyClass(1) object at 0x01EB1EB0> 
print(SubClass()) # <__main__.SubClass object at 0x01EB1CD0> 
+0

Pythonからうまく拡張することはできますが、それは良い考えではないかもしれません。あなたは(ほとんど)それを変更することはできません。 – user2357112

+0

申し訳ありませんが、それは私が意味するものです。 OPは実際にOSErrorの動作を変更したいと考えています。もちろん、サブクラス化でそれを拡張することができます。編集されました。 – kindall

6

あなたはerrnomapOSErrorサブクラスにerrno値のマッピングを保持する変数であるが、残念ながらそれはexceptions.cソースファイルの外にエクスポートしていないので、それを修正するために何のポータブルな方法はありませんことを正しいです。それ


が可能非常に非ポータブルハックを使用してアクセスすることであり、私は楽しみの精神で、純粋に下回る(デバッガを使用して)そうするための1つの可能な方法を提示します。これは任意のx86-64 Linuxシステムで動作するはずです。これがどのように動作するかの

>>> import os, sys 
>>> os.system("""gdb -p %d \ 
-ex 'b PyDict_GetItem if (PyLong_AsLongLong($rsi) == -1 ? \ 
(PyErr_Clear(), 0) : PyLong_AsLongLong($rsi)) == 0xbaadf00d' \ 
-ex c \ 
-ex 'call PySys_SetObject("errnomap", $rdi)' --batch >/dev/null 2>&1 &""" % os.getpid()) 
0 
>>> OSError(0xbaadf00d, '') 
OSError(3131961357, '') 
>>> sys.errnomap 
{32: <class 'BrokenPipeError'>, 1: <class 'PermissionError'> [...]} 
>>> class ImATeapotError(OSError): 
    pass 
>>> sys.errnomap[99] = ImATeapotError 
>>> OSError(99, "I'm a teapot") 
ImATeapotError(99, "I'm a teapot") 

クイック説明:

gdb -p %d [...] --batch >/dev/null 2>&1 &

、無人モード(--batch)で、現在のPythonプロセス(os.getpid())にデバッガをアタッチ出力(>/dev/null 2>&1)を破棄し、中背景(&)、Pythonの実行を継続できます。

b PyDict_GetItem if (PyLong_AsLongLong($rsi) == -1 ? (PyErr_Clear(), 0) : PyLong_AsLongLong($rsi)) == 0xbaadf00d

ときPythonプログラムaccesses any dictionary、(後でOSError(0xbaadf00d, '')として使用)マジック値を持つキーis an int場合破ります。 intでない場合は、TypeErrorを呼び出すだけです。したがって、suppress itです。

call PySys_SetObject("errnomap", $rdi)

これが起こるとき、私たちは中に検索される辞書はerrnomap知っています。 store it as an attribute on the sys module

+0

これは家庭でこれを試してはいけません_警告。良いですね。 –

+1

Yeesh、それは 'gc.get_referrers'で試したくそよりも厄介です(これは、恐らく追跡されていないオブジェクトの問題のために動作しませんでした)。 – user2357112

+1

@ user2357112ええ、これは私が最初に試したものです:) - 残念ながら、明示的に追跡されない限り、Cモジュールで作成され所有されているオブジェクトはGCには見えません。 – ecatmur

関連する問題