3

私はそのようなものを作ろうとします:

class oObject(object):
    def __init__(self, x = 0, y = 0, z = 0):
        self.x = x
        self.y = y
        self.z = z

def asString (self, value):
    return str(value)

vector = oObject(5,5,5)

# So i can do
asString(vector.x)

# But I want this kind of syntax
vector.x.asString()

これは単なる例です。積分器を文字列に変換したくありません。それは、クラスからクラスへのクラスについてです。

4

3 に答える 3

3

oObject指定された の文字列を返すクラスのカスタム メソッドを作成するか、カスタムクラスを作成して値をラップするkeyことができます。Variant

class oObject(object):
    def __init__(self, x = 0, y = 0, z = 0):
        self.x = Variant(x)
        self.y = Variant(y)
        self.z = Variant(z)

class Variant(object):
    def __init__(self, obj):
        self._obj = obj

    def __repr__(self):
        return '<%s: %s>' % (self.__class__.__name__, self.asString())

    def __str__(self):
        return self.asString()

    def asString(self):
        return str(self._obj)

    def value(self):
        return self._obj

PyQt4が実際に Qt からのQVariant クラスを使用してどのようにそれを行うかについては、この参照を確認してください。通常、Python ではこの型は必要ありませんが、C++ では複数の型を表す必要がありました。

于 2012-07-08T18:38:43.437 に答える
2

Python でこの種のことを行うべきではありません

ただし、できることは、クラスに標準__str__メソッドを実装することです。これは、 を使用してインスタンスを文字列に変換するときに使用されるコードですstr(instance)

技術的には、構文を使い慣れたものに曲げようとして、Python で多くのトリックを実行できますが、Python をより読みやすくするために多くの努力が払われており、基本的にその作業を破壊しているため、これは悪い考えです。

Python では、文字列への変換はstr(x)、 という名前のメソッドを呼び出すのではなく、 によって行われasStringます。使用すると、返さ__str__れるものをすでにカスタマイズできますがstr、なぜメソッドを追加するのですか? カスタム文字列変換を行う方法が必要な場合は、既存のクラスに新しいメソッドを注入しようとする代わりに、オブジェクト型でディスパッチする関数を定義するだけです:

converters = dict()

def add_converter(klass, f):
    converters[klass] = f

def default_converter(x):
    return "<%s: %s>" % (x.__class__.__name__, str(x))

def mystr(x):
    return converters.get(x.__class__, default_converter)(x)

このアプローチでは、「魔法の」(つまり驚くべき) 動作はなく、ラップすることもありません (コードを読む人を驚かせる可能性のある別のアプローチ)。

上記の例では、コンバーターの継承を処理していませんが、必要に応じて、より洗練されたルックアップを使用してそれを行うことができます (文字列関数への変換を継承する意味がわからない場合は、静かに失われます)。情報)。

また、メタクラスの目的を理解していない場合は、その概念をそのままにしておくと、ほとんどの場合、実際には必要ありません。メタクラスは強力ですが、それほど頻繁には必要とされないやや複雑なツールです...

この記事は、メタクラスとは何か、メタクラスで何ができるかについての良い一般的な説明だと思います。一部の悲惨な詳細が欠落しているため、公式ドキュメントを使用してそれらを掘り下げる必要があることに注意してください。

于 2012-07-08T18:27:45.350 に答える
0

求めているものを正確に取得することは、Python ではトリッキーです。つまり、"instance.x.method" を実行すると、Python は最初に "instance" から属性 "x" を取得し、"method" を見つけようとします。 "「x」オブジェクト自体の属性として(「メソッド」内から取得できる可能性のある「x」への参照を元々持っていた「インスタンス」への参照はありませんが、フレームのイントロスペクション用です)。

私はそれが「できる」と言いました-そして、ほとんどのタイプのxで機能するように作られていますが、属性「x」のタイプに応じて、最終的に失敗するか、付随的な影響を与える可能性があります __setattr__:インスタンスに設定された各属性に対して、実際にはその属性の動的サブクラスが作成されます。これにより、新しいオブジェクトで目的のメソッドが有効になります。欠点は、すべてのタイプのオブジェクトをサブクラス化できるわけではなく、サブクラス化されたすべてのオブジェクトが親とまったく同じように動作するわけではないということです。(たとえば、「x」が関数の場合)。ただし、ほとんどの場合に機能します。

class Base(object):
    def __setattr__(self, name, attr):
        type_ = type(attr)
        new_dict = {}
        for meth_name in dir(self.__class__):
            function = getattr(self.__class__, meth_name)
            # Assume any methods on the class have the desired behavior and would
            # accept the attribute as it's second parameter (the first being self).

            # This could be made more robust by making a simple method-decorator
            # which would mark the methods that one wishes to be appliable
            # to attributes, instead of picking all non "_" starting methods like here:

            if not callable(function) or meth_name in new_dict or meth_name.startswith("_"):
                continue
            def pinner(f):
                def auto_meth(se, *args, **kw):
                    return f(se._container, se, *args, **kw)
                return auto_meth
            new_dict[meth_name] = pinner(function)
        # This could be improved in order to have a class-based cache of derived types
        # so that each attribute setting would only create a new_type for
        # each different type that is being set
        new_type = type(type_.__name__, (type_,), new_dict)
        try:
            attr.__class__ = new_type
        except TypeError:
            # here is the main problem withthis approach: 
            # if the type being stored can't have it's `__class__`dynamically
            # changed, we have to build a new instance of it.
            # And if the constructor can't take just the base type
            # as its building parameter, it won't work. Worse if having another instance
            # does have side-effects in the code, we are subject to those.

            attr = new_type(attr)
        attr._container = self
        super(Base, self).__setattr__(name, attr)



class oObject(Base):
    def __init__(self, x = 0, y = 0, z = 0):
        self.x = x
        self.y = y
        self.z = z

    def asString(self, attr):
        return str(attr)

そして、これらをインタラクティブなセクションにロードした後:

>>> v = oObject(1,2,3)
>>> v.x.asString()
'1'
>>> v.w = [1,2,3]
>>> v.w.append(3)
>>> v.w.asString()
'[1, 2, 3, 4]'
>>> 

ご覧のとおり、これはメタクラスを必要としない通常のクラス継承で実行できます。

任意の Parameter タイプに対する別のより信頼性の高いアプローチは、属性名とメソッドに別のセパレーターを使用することです。これらを使用すると、基本クラスではるかに単純な__getattribute__メソッドを記述でき、リクエスト メソッドを動的にチェックし、それを呼び出します。属性。このアプローチは、動的なサブクラス化を必要とせず、約 2 桁単純です。vector.x__asString 価格は、ドット区切りの代わりに次のようなものを書くことです。これは、実際に Python 用の試行済みの SQLALchemy ORM で採用されているアプローチです。

# Second approach:

class Base(object):
    separator = "__"
    def __getattr__(self, attr_name):
        if self.__class__.separator in attr_name:
            attr_name, method_name = attr_name.split(self.__class__.separator, 1)
            method = getattr(self, method_name)
            return method(getattr(self, attr_name))
        raise AttributeError

そしていま:

>>> class oObject(Base):
...     def __init__(self, x = 0, y = 0, z = 0):
...         self.x = x
...         self.y = y
...         self.z = z
...         
...     def asString(self, attr):
...         return str(attr)
... 
>>> 
>>> 
>>> v = oObject(1,2,3)
>>> v.x__asString
'1'

(呼び出されたメソッドにさらにパラメーターを渡す場合は、さらにコードが必要ですが、アイデアを得るにはこれで十分だと思います)。

于 2012-07-08T20:49:49.667 に答える