17

Python がいかに動的であるかを考えると、これが何らかの方法で不可能であるとしたら、私はショックを受けるでしょう。

の実装を変更したいと思いsys.stdout.writeます。

私の別の質問に対するこの回答からアイデアを得ました: https://stackoverflow.com/a/24492990/901641

私はこれを簡単に書いてみました:

original_stdoutWrite = sys.stdout.write

def new_stdoutWrite(*a, **kw):
    original_stdoutWrite("The new one was called! ")
    original_stdoutWrite(*a, **kw)

sys.stdout.write = new_stdoutWrite

しかし、それは私に言いますAttributeError: 'file' object attribute 'write' is read-only

これは、潜在的に (おそらく) ばかげたことをしないようにするための良い試みですが、とにかくやりたいと思っています。インタープリターには、私が変更できる何らかのルックアップテーブルがあると思われますが、Google でそのようなものを見つけることができませんでした。__setattr__どちらも機能しませんでした-属性が読み取り専用であるというまったく同じエラーが返されました。

私は特にPython 2.7のソリューションを探しています.

4

2 に答える 2

27

その動的性にもかかわらず、Python ではfile. __dict__そのようなタイプの を変更することで、これを防ぐことさえできます—__dict__プロパティは、読み取り専用プロキシでラップされた dict を返すため、への割り当てfile.writeとへの割り当ての両方がfile.__dict__['write']失敗します。そして、少なくとも 2 つの正当な理由があります。

  1. C コードは、file組み込み型がPyFile型構造と内部で使用される関数file.writeに対応していることを期待しています。PyFile_Write()

  2. Python は、型の属性アクセスのキャッシュを実装して、メソッドの検索とインスタンス メソッドの作成を高速化します。このキャッシュは、タイプ dict に直接割り当てることが許可されている場合、破損します。

モンキー パッチはもちろん、動的な変更を適切に処理できる Python で実装されたクラスに対して許可されています。

ただし...自分が何をしているのかを本当に知っている場合はctypes、実装にフックしてタイプdictに到達するなど、低レベルのAPIを使用できます。例えば:

# WARNING: do NOT attempt this in production code!

import ctypes

def magic_get_dict(o):
    # find address of dict whose offset is stored in the type
    dict_addr = id(o) + type(o).__dictoffset__

    # retrieve the dict object itself
    dict_ptr = ctypes.cast(dict_addr, ctypes.POINTER(ctypes.py_object))
    return dict_ptr.contents.value

def magic_flush_mro_cache():
    ctypes.PyDLL(None).PyType_Modified(ctypes.py_object(object))

# monkey-patch file.write
dct = magic_get_dict(file)
dct['write'] = lambda f, s, orig_write=file.write: orig_write(f, '42')

# flush the method cache for the monkey-patch to take effect
magic_flush_mro_cache()

# magic!
import sys
sys.stdout.write('hello world\n')
于 2014-06-30T20:36:30.607 に答える
3

Python はほとんどが動的言語ですがstr、 、file( を含むstdout) 、dict、などのネイティブ オブジェクト型がありlist、実際には低レベル C で実装されており、完全に静的です。

>>> a = []
>>> a.append = 'something else'
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object attribute 'append' is read-only

>>> a.hello = 3
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute 'hello'

>>> a.__dict__  # normal python classes would have this
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
AttributeError: 'list' object has no attribute '__dict__'

オブジェクトがネイティブ C コードの場合、唯一の希望は実際の通常のクラスを使用することです。あなたの場合、すでに述べたように、次のようなことができます:

class NewOut(type(sys.stdout)):
    def write(self, *args, **kwargs):
        super(NewOut, self).write('The new one was called! ')
        super(NewOut, self).write(*args, **kwargs)
sys.stdout = NewOut()

または、元のコードと同様のことを行うには:

original_stdoutWrite = sys.stdout.write
class MyClass(object):
    pass
sys.stdout = MyClass()
def new_stdoutWrite(*a, **kw):
    original_stdoutWrite("The new one was called! ")
    original_stdoutWrite(*a, **kw)
sys.stdout.write = new_stdoutWrite
于 2014-06-30T20:31:13.237 に答える