자식 개체에 대한 프록시 속성 선언

9652 단어 python
자식 개체에 의해 구현되는 동안 파사드 개체에 속성을 노출하려고 합니다. Python에서는 읽기/쓰기 속성을 선언하여 이를 수행할 수 있습니다.

class Foo:

    def __init__(self):
        self.var1 = "hello"

class Bar:

    def __init__(self):
        self.foo = Foo()

    @property
    def var1(self):
        return self.foo.var1

    @var1.setter
    def var1(self, value):
        self.foo.var1 = value


이 코드에서 var1Bar 속성은 var1 인스턴스의 foo 속성에 대한 프록시입니다.

이 구현을 사용하면 Foo에 속성을 추가하고 Bar에 속성을 노출할 때마다 6줄의 코드를 선언해야 합니다. 이 상용구는 커져 클래스를 읽고 유지하기가 더 어려워집니다.
descriptor proxy_property를 구현하여 이 상용구를 한 줄로 줄입니다. 다음은 재구현된Bar 클래스입니다.

class Bar:
    var1 = property_proxy("foo", "var1")

    def __init__(self):
        self.foo = Foo()


파이썬에서 Adescriptor는 던더 메서드__get__ , __set____delete__ 를 구현하는 클래스입니다.

Descriptors are a powerful, general purpose protocol. They are the mechanism behind properties, methods, static methods, class methods, and super().

Python Doc - Descriptor HowTo Guide



다음은 property_proxy 설명자의 구현입니다.

class property_proxy(object):
    """
    A descriptor based recipe that makes it possible to write shorthands
    that forward attribute access from one object onto another.

    >>> class B:
    >>>     def __init__(self):
    >>>         self.foo = 12
    >>>         self.bar = 12
    >>>
    >>> class A:
    >>>     foo: int = property_proxy("b", "foo")
    >>>     bar: int = property_proxy("b", "bar")
    >>>
    >>>     def __init__(self):
    >>>         self.b = B()
    >>>
    >>> a = A()
    >>> print(a.foo)

    This descriptor avoids writing the code below to establish a proxy
     with a child instance

    >>> class B:
    >>>     def __init__(self):
    >>>         self.foo = 12
    >>>
    >>> class A:
    >>>
    >>>     def __init__(self):
    >>>         self.b = B()
    >>>
    >>>     @property
    >>>     def foo(self):
    >>>         return self.b.foo
    >>>
    >>>     @foo.setter
    >>>     def foo(self, value):
    >>>         self.b.foo = value
    >>>

    """

    def __init__(self, objectName, attrName):
        self.objectName = objectName
        self.attrName = attrName

    def __get__(self, instance, owner=None):
        proxy = getattr(instance, self.objectName)
        if proxy is None:
            raise ValueError(f"{self.objectName} does not exists on {instance}")

        return getattr(proxy, self.attrName)

    def __set__(self, instance, value):
        proxy = getattr(instance, self.objectName)
        if proxy is None:
            raise ValueError(f"{self.objectName} does not exists on {instance}")

        setattr(proxy, self.attrName, value)


결론적으로



이 게시물에서 우리는 파사드에 자식 객체의 속성을 노출하는 것이 큰 상용구를 가져오고 파사드를 읽고 유지하는 데 방해가 된다는 것을 발견했습니다.

이 선언을 한 줄로 만들고 코드 일관성을 개선하기 위해 property_proxy 설명자를 구현했습니다. 가독성의 향상은 상당합니다.

class Bar:
    var1 = property_proxy("foo", "var1")

    def __init__(self):
        self.foo = Foo()


한계


property_proxy 선언에는 코드의 유지 관리 가능성에 영향을 미치는 2가지 결함이 있습니다. 이 선언은 mypy와 같은 linter가 속성의 존재 및 유형을 확인하는 것을 허용하지 않습니다. IDE의 속성 이름 리팩터링은 property_proxy 선언의 속성을 일반 텍스트로 간주하기 때문에 이 선언을 놓칠 수 있습니다.

더 읽어보기


  • Python Doc - Descriptor HowTo Guide
  • 좋은 웹페이지 즐겨찾기