求めているものを正確に取得することは、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'
(呼び出されたメソッドにさらにパラメーターを渡す場合は、さらにコードが必要ですが、アイデアを得るにはこれで十分だと思います)。