2017-11-23 6 views
1

私はクラスOpを得た:そのメタクラスは__get__メソッドを持っているので、私は、記述子として機能するようにOpクラス自体を期待__get__メタクラスのメソッドが呼び出されないのはなぜですか?

class Pipeable(type): 
    def __get__(self, instance, owner): 
     def pipe_within(*args, **kwargs): 
      return self(*args, op=instance, **kwargs) 
     print('piping...') 
     return pipe_within 

class Op(metaclass=Pipeable): 
    def __init__(self, op=None): 
     if op is not None: 
      print('piped!') 
     self.op = op 
     self.__dict__[type(self).__name__] = type(self) 

が、コード

op = Op().Op() 

Op.__get__を起動しません。どうして?

+1

記述子はインスタンスではなくクラスになければなりません。 – user2357112

答えて

0

作業に入るためには、記述子はインスタンス属性ではなくクラス属性でなければなりません。 このコードは、必要なことを行います。

class Pipeable(type): 
    _instances = {} 

    def __new__(cls, name, bases, namespace, **kwds): 
     namespace.update(cls._instances) 
     instance = type.__new__(cls, name, bases, namespace) 

     cls._instances[name] = instance 
     for inst in cls._instances: 
      setattr(inst, name, instance) 
     return instance 

    def __get__(self, instance, owner): 
     def pipe_within(*args, **kwargs): 
      return self(*args, op=instance, **kwargs) 
     print('piping...') 
     return pipe_within 


class Op(metaclass=Pipeable): 
    def __init__(self, op=None): 
     if op is not None: 
      print('piped!') 
     self.op = op 

Op().Op() 
1

あなたが本当に欲しいものを言うのは難しいです。しかし、新しいクラスが登場するたびにプロパティを追加するメタクラスは、あなたが必要とするものであればよりうまくいくかもしれません。

あなたのコードを理解できれば、新しいインスタンスを作成すると(新しいインスタンスを作成し、他のインスタンスの参照を取得する)古いクラスには新しいインスタンスへの参照が移入されません。第二に

しかし、dinamically __new__ inisdeプロパティを作成することがハックようだ - しかし、あなたはただそれほど複雑なコードのメタクラス__getattr____dir__メソッドを実装することができます

シンプルなバージョンは、クラスのためではなく、そのインスタンスのために働きます - インスタンスは、メタクラスの__getattr__をトリガしませんので:

class Pipeable(type): 
    _classes = {} 

    def __new__(metacls, name, bases, namespace, **kwds): 
     cls = type.__new__(metacls, name, bases, namespace) 
     metacls._classes[name] = cls 
     return cls 


    def __getattr__(cls, attr): 
     classes = cls.__class__._classes 
     if attr not in classes: 
      raise AttributeError 
     def pipe_within(*args, **kwargs): 
      return cls(*args, op=classes[attr], **kwargs) 
     print('piping...') 
     return pipe_within 

    def __dir__(cls): 
     regular = super().__dir__() 
     return sorted(regular + list(cls.__class__._classes.keys())) 


class Op(metaclass=Pipeable): 
    def __init__(self, op=None): 
     if op is not None: 
      print('piped!') 
     self.op = op 

Op.Op() 

(だけでなく、時間をかけて私はメタクラスで使用する命名規則は、このパラメータを選んだことに注意してください - ほとんどの彼らの方法はで作成したクラスを取るよう彼らは普通のクラスの "自己"の代わりに、私はこの命名法に従うのが簡単だと分かります。しかし、必ずしも「正しい」とは限りません)

しかし、作成されたクラスに直接__dir____getattr__を作成することで、インスタンスに対して機能させることができます。あなたが作成しているクラスはすでにスーパークラスであっても、__getattr__またはカスタム__dir__を持っているため、ラップする必要があります。確保することにより、それが「正しいことを行う」 -

class Pipeable(type): 
    _classes = {} 

    def __new__(metacls, name, bases, namespace, **kwds): 
     cls = type.__new__(metacls, name, bases, namespace) 
     metacls._classes[name] = cls 
     original__getattr__ = getattr(cls, "__getattr__", None) 
     if hasattr(original__getattr__, "_metapipping"): 
      # Do not wrap our own (metaclass) implementation of __getattr__ 
      original__getattr__ = None 
     original__dir__ = getattr(cls, "__dir__") # Exists in "object", so it is always found. 

     # these two functions have to be nested so they can get the 
     # values for the originals "__getattr__" and "__dir__" from 
     # the closure. These values could be set on the class created, alternatively. 
     def __getattr__(self, attr): 
      if original__getattr__: 
       # If it is desired that normal attribute lookup have 
       # less precedence than these injected operators 
       # move this "if" block down. 
       try: 
        value = original__getattr__(self, attr) 
       except AttributeError: 
        pass 
       else: 
        return value 
      classes = self.__class__.__class__._classes 
      if attr not in classes: 
       raise AttributeError 
      def pipe_within(*args, **kwargs): 
       return cls(*args, op=classes[attr], **kwargs) 
      print('piping...') 
      return pipe_within 
     __getattr__._pipping = True 

     def __dir__(self): 
      regular = original__dir__(self) 
      return sorted(regular + list(self.__class__.__class__._classes.keys())) 
     __dir__.pipping = True 

     if not original__getattr__ or not hasattr(original__getattr__, "_pipping"): 
      cls.__getattr__ = __getattr__ 
     if not hasattr(original__dir__, "_pipping"): 
      cls.__dir__ = __dir__ 
     return cls 


    def __getattr__(cls, attr): 
     classes = cls.__class__._classes 
     if attr not in classes: 
      raise AttributeError 
     def pipe_within(*args, **kwargs): 
      return cls(*args, op=classes[attr], **kwargs) 
     print('piping...') 
     return pipe_within 
    __getattr__._metapipping = True 

    def __dir__(cls): 
     regular = super().__dir__() 
     return sorted(regular + list(cls.__class__._classes.keys())) 


class Op(metaclass=Pipeable): 
    def __init__(self, op=None): 
     if op is not None: 
      print('piped!') 

Op().Op() 

だから、これは長いことになった。そして、私たちは__dir__自身と__getattr__ので、いくつかの余分なケアを再ラップする必要はありません階層内のすべてのクラスとインスタンスは、作成順序に関係なく、互いに見ることができます。

また、どのような複雑さを補うことは正しくクラス階層に__getattr____dir__の他の可能なカスタマイズをラップしている - あなたはそれらのいずれかのカスタマイズを取得しない場合、これは単純な大きさの順になります

class Pipeable(type): 
    _classes = {} 

    def __new__(metacls, name, bases, namespace, **kwds): 
     cls = type.__new__(metacls, name, bases, namespace) 
     metacls._classes[name] = cls 

     def __getattr__(self, attr): 
      classes = self.__class__.__class__._classes 
      if attr not in classes: 
       raise AttributeError 
      def pipe_within(*args, **kwargs): 
       return cls(*args, op=classes[attr], **kwargs) 
      print('piping...') 
      return pipe_within 

     def __dir__(self): 
      regular = original__dir__(self) 
      return sorted(regular + list(self.__class__.__class__._classes.keys())) 

     cls.__getattr__ = __getattr__ 
     cls.__dir__ = __dir__ 

     return cls 

    def __getattr__(cls, attr): 
     classes = cls.__class__._classes 
     if attr not in classes: 
      raise AttributeError 
     def pipe_within(*args, **kwargs): 
      return cls(*args, op=classes[attr], **kwargs) 
     print('piping...') 
     return pipe_within 

    def __dir__(cls): 
     regular = super().__dir__() 
     return sorted(regular + list(cls.__class__._classes.keys())) 
関連する問題