新しいスタイル クラスは、古いスタイル クラスよりも高速かつ正確に動作します。したがって、安価な理由で疑わしい順序で高価な__getattr__
, __getattribute__
, を呼び出すことはありません。__coerce__
古いスタイル__coerce__
には、特別な目的のために演算子メソッドをすでにオーバーロードしている場合でも呼び出されるという問題もありました。また、一般的な型にキャストする必要があり、特定のバイナリ操作に限定されます。int / float / string の他のすべてのメソッドとプロパティ、および pow() について考えてみてください。これらすべての制限によりcoerce
、PY3 では欠落しています。質問の例は、かなり広い仮想化を目指しています。
新しいスタイルのクラスでは、多くの「類似した」メソッドをほとんどコードで提供したり、それらの呼び出しを仮想ハンドラーにルーティングしたりしてから、高速で正確に定義され、正確かつきめ細かい方法でサブクラス化できるループにすぎません。それは「Python 言語の回帰」ではありません。
ただし、そのようなループや単純な基本クラスの種類の動作を提供するためだけに、他の回答に示されているようにメタクラスを使用しません。それはハンマーでナッツを割ることです。
「バリアント」の仮想化のヘルパーの例を次に示します。
def Virtual(*methods):
"""Build a (new style) base or mixin class, which routes method or
operator calls to one __virtualmeth__ and attribute lookups to
__virtualget__ and __virtualset__ optionally.
*methods (strings, classes): Providing method names to be routed
"""
class VirtualBase(object):
def __virtualmeth__(self, methname, *args, **kw):
raise NotImplementedError
def _mkmeth(methname, thing):
if not callable(thing):
prop = property(lambda self:self.__virtualget__(methname),
lambda self, v:self.__virtualset__(methname, v))
return prop
def _meth(self, *args, **kw):
return self.__virtualmeth__(methname, *args, **kw)
_meth.__name__ = methname
return _meth
for m in methods:
for name, thing in (isinstance(m, str) and
{m:lambda:None} or m.__dict__).items():
if name not in ('__new__', '__init__', '__setattr__', ##'__cmp__',
'__getattribute__', '__doc__', ): ##'__getattr__',
setattr(VirtualBase, name, _mkmeth(name, thing))
return VirtualBase
そして、ここにユースケースの例があります: An Anaphor! (PY2 および PY3) :
import operator
class Anaphor(Virtual(int, float, str)):
"""remember a sub-expression comfortably:
A = Anaphor() # at least per thread / TLS
if re.search(...) >> A:
print(A.groups(), +A)
if A(x % 7) != 0:
print(A, 1 + A, A < 3.0, A.real, '%.2f' % A, +A)
"""
value = 0
def __virtualmeth__(self, methname, *args, **kw):
try: r = getattr(self.value, methname)(*args, **kw)
except AttributeError:
return getattr(operator, methname)(self.value, *args, **kw)
if r is NotImplemented: # simple type -> coerce
try: tcommon = type(self.value + args[0]) # PY2 coerce
except: return NotImplemented
return getattr(tcommon(self.value), methname)(*args, **kw)
return r
def __call__(self, value):
self.value = value
return value
__lshift__ = __rrshift__ = __call__ # A << x; x >> A
def __pos__(self): # real = +A
return self.value
def __getattr__(self, name):
return getattr(self.value, name)
def __repr__(self):
return '<Anaphor:%r>' % self.value
3 引数の演算子もシームレスに処理しますpow()
:-) :
>>> A = Anaphor()
>>> x = 1
>>> if x + 11 >> A:
... print repr(A), A, +A, 'y' * A, 3.0 < A, pow(A, 2, 100)
...
<Anaphor:12> 12 12 yyyyyyyyyyyy True 44