2017-11-23 1 views
1

나는 클래스 Op 가지고 : 자사의 메타 클래스는 __get__ 방법을 가지고 있기 때문에__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() 

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())) 
관련 문제