6

Matlab から Python に移行しようとしています。魔法の間?Matlab の非常に優れた機能の 1 つは、(; を省略して) 問題のオブジェクトのインスタンス変数 (Matlab ではプロパティと呼ばれる) をコマンド ラインで確認できることです。これはpythonで可能ですか(IPython経由だと思います)?

理想的には、次のようなクラスです。

class MyClass(object):
    _x = 5

    @property
    def x(self):
        return self._x + 100

    @x.setter
    def x(self, value):
        self._x = value + 1

    def myFunction(self, y):
        return self.x ** 2 + y

次のように表示されます。

mc = Myclass()
mc
<package.MyClass> <superclass1> <superclass2>

Attributes:
_x: 5
 x: 105

Method Attributes:
myFunction(self, y)

クラスの print メソッド (そのようなものが存在する場合) をオーバーライドすることで可能ですか? またはipythonの魔法の方法を介して?

4

3 に答える 3

8

簡単に言えば、属性は動的に生成される可能性があるため、Python でオブジェクトのすべての属性のリストを取得する方法はありません。極端な例として、次のクラスを考えてみます。

>>> class Spam(object):
...     def __getattr__(self, attr):
...         if attr.startswith('x'):
...             return attr[1:]
>>> spam = Spam()
>>> spam.xeggs
'eggs'

インタープリターがすべての属性のリストを誰かが把握できたとしても、そのリストは無限になります。

単純なクラスの場合spam.__dict__は、多くの場合十分です。__slots__動的属性、 に基づく属性、クラス属性、C 拡張クラス、上記のほとんどから継承された属性、およびその他のあらゆる種類のものを処理しません。しかし、それは少なくとも何かであり、時にはそれはあなたが望むものです. 最初の概算では、それは正確に で明示的に割り当てたもの__init__であり、それ以外のものではありません。

人間の可読性を目的とした「すべて」を目的としたベスト エフォートの場合は、 を使用しますdir(spam)

プログラムで使用するための「すべて」を目的としたベスト エフォートの場合は、 を使用しますinspect.getmembers(spam)。(実際、この実装はdirCPython 2.x のラッパーにすぎませんが、もっと多くのことができます — 実際、CPython 3.2+ ではそうです。)

これらは両方とも、処理できない幅広いものを処理し、にあるが見たくない__dict__ものをスキップする可能性があります。__dict__しかし、それらはまだ本質的に不完全です。

何を使用しても、キーだけでなく値も簡単に取得できます。__dict__またはを使用している場合getmembers、それは簡単です。__dict__通常、これは、または目的dictのために十分に近い動作をするものであり、キーと値のペアを明示的に返します。を使用している場合は、非常に簡単に取得できます。dictgetmembersdirdict

{key: getattr(spam, key) for key in dir(spam)}

最後にもう 1 つ: 「オブジェクト」という用語は少しあいまいです。「から派生したクラスのobject任意のインスタンス」、「クラスの任意のインスタンス」、「新しいスタイルのクラスの任意のインスタンス」、または「任意の型の任意の値」(モジュール、クラス、関数など) を意味します。 .)。dirand getmemberson はほぼ何でも使用できます。それが何を意味するかの正確な詳細は、ドキュメントに記載されています。

getmembers最後にもう 1 つ: が ) のようなものを返すことに気付くかもしれませんが、これにはおそらく関心がありません。結果は単なる名前と値のペアであるため、メソッドや変数など('__str__', <method-wrapper '__str__' of Spam object at 0x1066be790>を削除したいだけの場合は、簡単。しかし、「メンバーの種類」でフィルタリングしたいことがよくあります。この関数はフィルター パラメーターを受け取りますが、ドキュメントはその使用方法をうまく説明していません (さらに、記述子がどのように機能するかを理解している必要があります)。基本的に、フィルターが必要な場合は、通常、 、、または関数の組み合わせで構成されます。__dunder___privategetmemberscallablelambda x: not callable(x)lambdainspect.isfoo

したがって、これは関数として書きたいと思うかもしれません。

def get_public_variables(obj):
    return [(name, value) for name, value 
            in inspect.getmembers(obj, lambda x: not callable(x))
            if not name.startswith('_')]

これをカスタム IPython %magic 関数に変換するか、単に %macro を作成するか、通常の関数のままにして明示的に呼び出すことができます。


__repr__コメントで、%magic 関数などを作成しようとする代わりに、これを関数にパッケージ化できるかどうかを尋ねました。

すべてのクラスが単一のルート クラスから継承されている場合、これは素晴らしいアイデアです。__repr__すべてのクラスで機能する単一のものを作成できます (または、それらの 99% で機能する場合__repr__は、残りの 1% でそれをオーバーライドできます)。その後、インタープリターまたは印刷でオブジェクトを評価するたびにそれらを出して、あなたが望むものを手に入れましょう。

ただし、次の点に注意してください。

Python には、理由から (オブジェクトの__str__場合に得られるものprint) と(対話型プロンプトでオブジェクトを評価しただけの場合に得られるもの) の両方があります。__repr__通常、前者は人間が判読できる優れた表現ですが、後者は、入力可能eval(または対話型プロンプトに入力可能) であるか、タイプを区別するのに十分な簡潔な山かっこ形式です。オブジェクトのアイデンティティ。

これはルールではなく単なる慣例なので、自由に破ることができます。ただし、それを壊す場合でもstr、 /reprの区別を利用したい場合があります。たとえば、makereprはすべての内部構造の完全なダンプを提供しstr、便利な public 値のみを表示します。

reprもっと真剣に、値がどのように構成されているかを考慮する必要があります。たとえば、あなたprintまたはreprの場合list、実質的に を取得します'[' + ', '.join(map(repr, item))) + ']'。これは、複数行でかなり奇妙に見えるでしょうrepr。また、IPython に組み込まれているような、ネストされたコレクションをインデントしようとする任意の種類のプリティ プリンターを使用すると、さらに悪化します。結果はおそらく判読不能になることはなく、pretty-printer が提供するはずの利点が台無しになるだけです。

表示したい特定のものについては、すべて非常に簡単です。このようなもの:

def __repr__(self):
    lines = []

    classes = inspect.getmro(type(self))
    lines.append(' '.join(repr(cls) for cls in classes))

    lines.append('')
    lines.append('Attributes:')
    attributes = inspect.getmembers(self, callable)
    longest = max(len(name) for name, value in attributes)
    fmt = '{:>%s}: {}' % (longest, )
    for name, value in attributes:
        if not name.startswith('__'):
            lines.append(fmt.format(name, value))

    lines.append('')
    lines.append('Methods:')
    methods = inspect.getmembers(self, negate(callable))
    for name, value in methods:
        if not name.startswith('__'):
            lines.append(name)

    return '\n'.join(lines)

ここで最も難しいのは、属性名を右揃えにすることです。(そして、これはテストされていないコードなので、おそらく間違っていると思います…) 他のすべては簡単であるか、楽しいものです (さまざまなフィルターで遊んで、getmembersそれらが何をするかを確認します)。

于 2013-03-18T22:55:44.263 に答える
1

を実装することで、IPythonで(少なくとも大まかに)望んでいたことを達成できました_repr_pretty_

def get_public_variables(obj):
    from inspect import getmembers
    return [(name, value) for name, value in
            getmembers(obj, lambda x: not callable(x)) if
            not name.startswith('__')]


class MySuperClass(object):
    def _repr_pretty_(self, p, cycle):
        for (name, value) in get_public_variables(self):
            f = '{:>12}{} {:<} \n'
            line = f.format(str(name), ':', str(value))
            # p.text(str(name) + ': ' + str(value) + '\n')
            p.text(line)

class MyClass(MySuperClass):
    _x = 5

    @property
    def x(self):
        return self._x + 100

から私を与えます:

mc = MyClass()
mc
Out[15]: 
          _x: 5 
           x: 105 

明らかに、空白などに関して微調整を行う必要があります。しかし、これは大まかに私が達成しようとしていたことでした

于 2013-03-20T18:53:32.073 に答える
0

次の例を使用して、オブジェクトのインスタンス変数にアクセスできますobj.__dict__

class Dog:
    def __init__(self, name, age):
        self.name = name
        self.age = age


d1 = Dog("Fido", 7)

for key, val in d1.__dict__.items():
    print(key, ": ", val)

出力:

age :  7
name :  Fido

ただし、これは、多数のインスタンス変数とメソッドを持つ可能性のある実際のオブジェクトではうまく機能しない場合があります。

于 2013-03-18T22:42:40.197 に答える