2017-02-24 14 views
2

免責事項:属性のアクセスタイプ(クラスまたはインスタンスを使用している)に応じて、関数間で「切り替え」を行う方法はありますか?

この記事では、質問よりもレシピですが、私はウェブでほとんど言及して、被写体が非常に興味深い発見しました。

StackOverflowでこの種の記事を公開する場所があれば教えてください。

件名:

がどのように(クラスまたはインスタンスを使用して)属性アクセスの種類に応じて異なる機能を呼び出すためのPythonを強制することができ - 例えばPythonにMyClass.my_method()MyClass().my_method()の異なるメソッドを呼び出させますか?

ユースケース:

我々は(Python36列挙型ではなく、いくつかのカスタマイズに基づく)カスタム列挙型の実装を持っている、のは、言ってみましょう。このEnumのユーザーとして、Enumだけでなくstr:class MyEnum(str, Enum)から継承するCustomEnumを作成したいと考えています。エンコードとデコードの機能も追加したいと考えています。私たちの考えはMyEnum.encodeを使用して、enumメンバーを含むオブジェクトをエンコードしますが、元のstr.encodeはenumクラスのインスタンスのままにしておきます。 要約:MyEnum.encodeは、私たちのカスタムエンコーディング関数を呼び出し、この観点から完全に感覚的です。 MyEnum()は文字列なので、MyEnum().encodeはstrクラスから継承したencode関数を呼び出す必要があります。

ソリューション:

はスイッチとして動作する記述子を、書きます。 私の最初の投稿に完全な答え。 __get__(self, instance, instance_type)

+0

答えとしてソリューションの一部を投稿してください。 – Gabriel

+1

SOのドキュメントセクション(http://stackoverflow.com/documentation)に投稿できます。あるいは、あなたの質問を問題のセクションのみに限定して、ソリューションセクション –

+0

で自分自身で答えることができます。@utsav_deep - 私の答えを問題だけに制限する方法?そのためのオプションはありますか?申し訳ありませんが、質問のために、これらは私の最初のステップStackOverflowです。 –

答えて

3

ソリューション:彼らは理由__get__関数のシグネチャの、クラスまたはインスタンスの起動している場合

は、私の知る限りでは、記述子は、区別することができる唯一のオブジェクトです。このプロパティを使用すると、その上にスイッチを作成できます。

class boundmethod(object):  
    def __init__(self, cls_method=None, instance_method=None, doc=None): 
     self._cls_method = cls_method 
     self._instance_method = instance_method 
     if cls_method: 
      self._method_name = cls_method.__name__ 
     elif instance_method: 
      self._method_name = instance_method.__name__ 

     if doc is None and cls_method is not None: 
      doc = cls_method.__doc__ 
     self.__doc__ = doc 

     self._method = None 
     self._object = None 

    def _find_method(self, instance, instance_type, method_name): 
     for base in instance_type.mro()[1:]: 
      method = getattr(base, method_name, None) 
      if _is_descriptor(method): 
       method = method.__get__(instance, base) 
      if method and method is not self: 
       try: 
        return method.__func__ 
       except AttributeError: 
        return method 

    def __get__(self, instance, instance_type): 
     if instance is None: 
      self._method = self._cls_method or self._find_method(instance, instance_type, self._method_name) 
      self._object = instance_type 
     else: 
      self._method = self._instance_method or self._find_method(instance, instance_type, self._method_name) 
      self._object = instance 
     return self 

    @staticmethod 
    def cls_method(obj=None): 
     def constructor(cls_method): 
      if obj is None: 
       return boundmethod(cls_method, None, cls_method.__doc__) 
      else: 
       return type(obj)(cls_method, obj._instance_method, obj.__doc__) 

     if isinstance(obj, FunctionType): 
      return boundmethod(obj, None, obj.__doc__) 
     else: 
      return constructor 

    @staticmethod 
    def instance_method(obj=None): 
     def constructor(instance_method): 
      if obj is None: 
       return boundmethod(None, instance_method, instance_method.__doc__) 
      else: 
       return type(obj)(obj._cls_method, instance_method, obj.__doc__) 

     if isinstance(obj, FunctionType): 
      return boundmethod(None, obj, obj.__doc__) 
     else: 
      return constructor 

    def __call__(self, *args, **kwargs): 
     if self._method: 
      try: 
       return self._method(self._object, *args, **kwargs) 
      except TypeError: 
       return self._method(*args, **kwargs) 
     return None 

例:

>>> class Walkmen(object): 
...  @boundmethod.cls_method 
...  def start(self): 
...   return 'Walkmen start class bound method' 
...  @boundmethod.instance_method(start) 
...  def start(self): 
...   return 'Walkmen start instance bound method' 
>>> print Walkmen.start() 
Walkmen start class bound method 
>>> print Walkmen().start() 
Walkmen start instance bound method 

私はそれが君たちoをいくつかに役立つことを願っています。

ベスト。

0

私は実際にこの質問をしました(Python descriptors and inheritance私はこの質問を見ていませんでした)。 My solutionは、継承のための記述子とメタクラスを使用します。 my answerから

:中

class dynamicmethod: 
    ''' 
     Descriptor to allow dynamic dispatch on calls to class.Method vs obj.Method 

     fragile when used with inheritence, to inherit and then overwrite or extend 
     a dynamicmethod class must have dynamicmethod_meta as its metaclass 
    ''' 
    def __init__(self, f=None, m=None): 
     self.f = f 
     self.m = m 

    def __get__(self, obj, objtype=None): 
     if obj is not None and self.f is not None: 
      return types.MethodType(self.f, obj) 
     elif objtype is not None and self.m is not None: 
      return types.MethodType(self.m, objtype) 
     else: 
      raise AttributeError('No associated method') 

    def method(self, f): 
     return type(self)(f, self.m) 

    def classmethod(self, m): 
     return type(self)(self.f, m) 

def make_dynamicmethod_meta(meta): 
    class _dynamicmethod_meta(meta): 
     def __prepare__(name, bases, **kwargs): 
      d = meta.__prepare__(name, bases, **kwargs) 
      for base in bases: 
       for k,v in base.__dict__.items(): 
        if isinstance(v, dynamicmethod): 
         if k in d: 
          raise ValueError('Multiple base classes define the same dynamicmethod') 
         d[k] = v 
      return d 

    return _dynamicmethod_meta 

dynamicmethod_meta=make_dynamicmethod_meta(type) 

class A(metaclass=dynamicmethod_meta): 
    @dynamicmethod 
    def a(self): 
     print('Called from obj {} defined in A'.format(self)) 

    @a.classmethod 
    def a(cls) 
     print('Called from class {} defined in A'.format(cls)) 

class B(A): 
    @a.method 
    def a(self): 
     print('Called from obj {} defined in B'.format(self)) 

A.a() 
A().a() 
B.a() 
B().a() 

結果:

Called from class <class 'A'> defined in A 
Called from obj <A object at ...> defined in A 
Called from class <class 'B'> defined in A 
Called from obj <B object at ...> defined in B