복붙노트

[PYTHON] 파이썬 할당을 오버로드 할 수 있습니까?

PYTHON

파이썬 할당을 오버로드 할 수 있습니까?

__assign __ (self, new_value)처럼 대입 연산자를 오버로드 할 수있는 마법 메서드가 있습니까?

인스턴스에 대한 재 바인드를 금지하고 싶습니다.

class Protect():
  def __assign__(self, value):
    raise Exception("This is an ex-parrot")

var = Protect()  # once assigned...
var = 1          # this should raise Exception()

가능한가? 미친 짓이야? 나는 약을 먹어야 하나?

해결법

  1. ==============================

    1.당신이 그것을 묘사하는 방법은 절대 불가능합니다. 이름에 대한 할당은 파이썬의 기본 기능이며, 동작을 변경하기위한 후크가 제공되지 않았습니다.

    당신이 그것을 묘사하는 방법은 절대 불가능합니다. 이름에 대한 할당은 파이썬의 기본 기능이며, 동작을 변경하기위한 후크가 제공되지 않았습니다.

    그러나 클래스 인스턴스의 멤버에 대한 할당은 .__ setattr __ ()을 재정 의하여 원하는대로 제어 할 수 있습니다.

    class MyClass(object):
        def __init__(self, x):
            self.x = x
            self._locked = True
        def __setattr__(self, name, value):
            if self.__dict__.get("_locked", False) and name == "x":
                raise AttributeError("MyClass does not allow assignment to .x member")
            self.__dict__[name] = value
    
    >>> m = MyClass(3)
    >>> m.x
    3
    >>> m.x = 4
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "<stdin>", line 7, in __setattr__
    AttributeError: MyClass does not allow assignment to .x member
    

    할당이 허용되는지 여부를 제어하는 ​​멤버 변수 _locked가 있습니다. 잠금을 해제하여 값을 업데이트 할 수 있습니다.

  2. ==============================

    2.할당은 변경 후크가없는 언어 내장입니다.

    할당은 변경 후크가없는 언어 내장입니다.

  3. ==============================

    3.나는 그것이 가능하다고 생각하지 않는다. 내가 본 방식대로, 변수에 대한 할당은 이전에 참조한 객체에 아무 것도하지 않습니다. 변수가 "다른 객체"를 가리키는 것입니다.

    나는 그것이 가능하다고 생각하지 않는다. 내가 본 방식대로, 변수에 대한 할당은 이전에 참조한 객체에 아무 것도하지 않습니다. 변수가 "다른 객체"를 가리키는 것입니다.

    In [3]: class My():
       ...:     def __init__(self, id):
       ...:         self.id=id
       ...: 
    
    In [4]: a = My(1)
    
    In [5]: b = a
    
    In [6]: a = 1
    
    In [7]: b
    Out[7]: <__main__.My instance at 0xb689d14c>
    
    In [8]: b.id
    Out[8]: 1 # the object is unchanged!
    

    그러나 __setitem __ () 또는 __setattr __ () 메서드를 사용하여 예외를 발생시키는 래퍼 객체를 만들고 "변경 불가능한"객체를 내부에 유지함으로써 원하는 동작을 모방 할 수 있습니다.

  4. ==============================

    4.아니야.

    아니야.

    생각해 보면, 당신의 예제에서 var라는 이름을 새로운 값으로 리 바인딩하고 있습니다. 실제로 Protect의 인스턴스를 만지는 것은 아닙니다.

    리바 인딩하려는 이름이 실제로 다른 일부 엔티티의 속성 인 경우 myobj.var 그러면 엔티티의 특성 / 속성에 값을 할당하는 것을 방지 할 수 있습니다. 그러나 나는 당신이 당신의 본보기에서 원하는 것이 아니라고 생각합니다.

  5. ==============================

    5.전역 네임 스페이스에서는 이것이 가능하지 않지만 더 많은 고급 Python 메타 프로그래밍을 활용하여 Protect 객체의 여러 인스턴스가 생성되는 것을 방지 할 수 있습니다. 싱글 톤 패턴이 좋은 예입니다.

    전역 네임 스페이스에서는 이것이 가능하지 않지만 더 많은 고급 Python 메타 프로그래밍을 활용하여 Protect 객체의 여러 인스턴스가 생성되는 것을 방지 할 수 있습니다. 싱글 톤 패턴이 좋은 예입니다.

    싱글 톤의 경우 인스턴스를 참조한 원래 변수가 다시 할당 되더라도 인스턴스화 된 후에도 객체가 유지된다는 것을 보장합니다. 후속 인스턴스는 동일한 객체에 대한 참조를 반환합니다.

    이 패턴에도 불구하고 전역 변수 이름 자체가 다시 할당되는 것을 방지 할 수는 없습니다.

  6. ==============================

    6.최상위 네임 스페이스를 사용하는 것은 불가능합니다. 너가 달릴 때

    최상위 네임 스페이스를 사용하는 것은 불가능합니다. 너가 달릴 때

    var = 1
    

    키 var 및 값 1을 전역 사전에 저장합니다. globals () .__ setitem __ ( 'var', 1)을 호출하는 것과 대략 같습니다. 문제는 실행중인 스크립트에서 전역 사전을 바꿀 수 없다는 것입니다 (스택을 망칠 수는 있지만 좋은 생각은 아닙니다). 그러나 보조 네임 스페이스에서 코드를 실행하고 해당 전역에 대한 사용자 지정 사전을 제공 할 수 있습니다.

    class myglobals(dict):
        def __setitem__(self, key, value):
            if key=='val':
                raise TypeError()
            dict.__setitem__(self, key, value)
    
    myg = myglobals()
    dict.__setitem__(myg, 'val', 'protected')
    
    import code
    code.InteractiveConsole(locals=myg).interact()
    

    그러면 거의 정상적으로 작동하는 REPL이 실행되지만 변수 val을 설정하려는 시도는 거부됩니다. execfile (filename, myg)을 사용할 수도 있습니다. 악의적 인 코드로부터 보호되지는 않습니다.

  7. ==============================

    7.추악한 솔루션은 소멸자에 재 할당하는 것입니다. 그러나 실제 오버로드 할당은 아닙니다.

    추악한 솔루션은 소멸자에 재 할당하는 것입니다. 그러나 실제 오버로드 할당은 아닙니다.

    import copy
    global a
    
    class MyClass():
        def __init__(self):
                a = 1000
                # ...
    
        def __del__(self):
                a = copy.copy(self)
    
    
    a = MyClass()
    a = 1
    
  8. ==============================

    8.예, 가능합니다. 수정을 통해 __assign__을 처리 할 수 ​​있습니다.

    예, 가능합니다. 수정을 통해 __assign__을 처리 할 수 ​​있습니다.

    pip install assign

    class T():
        def __assign__(self, v):
            print('called with %s' % v)
    b = T()
    c = b
    
    >>> import magic
    >>> import test
    called with c
    

    프로젝트는 https://github.com/RyanKung/assign에 있습니다. 그리고 더 간단한 요지 : https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df

  9. ==============================

    9.일반적으로, 내가 찾은 최선의 접근 방식은 setter로 __ilshift__를 대체하고 getter로 __rlshift__를 재정의하여 속성 데코레이터에 의해 복제됩니다. 그것은 거의 마지막 연산자가 해결되고 (| & ^) 논리가 더 낮습니다. 거의 사용하지 않습니다 (__lrshift__는 적지 만 고려해 볼 수 있음).

    일반적으로, 내가 찾은 최선의 접근 방식은 setter로 __ilshift__를 대체하고 getter로 __rlshift__를 재정의하여 속성 데코레이터에 의해 복제됩니다. 그것은 거의 마지막 연산자가 해결되고 (| & ^) 논리가 더 낮습니다. 거의 사용하지 않습니다 (__lrshift__는 적지 만 고려해 볼 수 있음).

    PyPi 지정 패키지를 사용하면 전달 할당 만 제어 할 수 있으므로 운영자의 실제 '강도'는 낮습니다. PyPi assign package 예제 :

    class Test:
    
        def __init__(self, val, name):
            self._val = val
            self._name = name
            self.named = False
    
        def __assign__(self, other):
            if hasattr(other, 'val'):
                other = other.val
            self.set(other)
            return self
    
        def __rassign__(self, other):
            return self.get()
    
        def set(self, val):
            self._val = val
    
        def get(self):
            if self.named:
                return self._name
            return self._val
    
        @property
        def val(self):
            return self._val
    
    x = Test(1, 'x')
    y = Test(2, 'y')
    
    print('x.val =', x.val)
    print('y.val =', y.val)
    
    x = y
    print('x.val =', x.val)
    z: int = None
    z = x
    print('z =', z)
    x = 3
    y = x
    print('y.val =', y.val)
    y.val = 4
    

    산출:

    x.val = 1
    y.val = 2
    x.val = 2
    z = <__main__.Test object at 0x0000029209DFD978>
    Traceback (most recent call last):
      File "E:\packages\pyksp\pyksp\compiler2\simple_test2.py", line 44, in <module>
        print('y.val =', y.val)
    AttributeError: 'int' object has no attribute 'val'
    

    교대와 동일 :

    class Test:
    
        def __init__(self, val, name):
            self._val = val
            self._name = name
            self.named = False
    
        def __ilshift__(self, other):
            if hasattr(other, 'val'):
                other = other.val
            self.set(other)
            return self
    
        def __rlshift__(self, other):
            return self.get()
    
        def set(self, val):
            self._val = val
    
        def get(self):
            if self.named:
                return self._name
            return self._val
    
        @property
        def val(self):
            return self._val
    
    
    x = Test(1, 'x')
    y = Test(2, 'y')
    
    print('x.val =', x.val)
    print('y.val =', y.val)
    
    x <<= y
    print('x.val =', x.val)
    z: int = None
    z <<= x
    print('z =', z)
    x <<= 3
    y <<= x
    print('y.val =', y.val)
    y.val = 4
    

    산출:

    x.val = 1
    y.val = 2
    x.val = 2
    z = 2
    y.val = 3
    Traceback (most recent call last):
      File "E:\packages\pyksp\pyksp\compiler2\simple_test.py", line 45, in <module>
        y.val = 4
    AttributeError: can't set attribute
    

    따라서 << = 연산자는 속성에서 값을 얻는 것보다 훨씬 시각적으로 깨끗한 솔루션이며 사용자가 다음과 같은 실수를 시도하지는 않습니다.

    var1.val = 1
    var2.val = 2
    
    # if we have to check type of input
    var1.val = var2
    
    # but it could be accendently typed worse,
    # skipping the type-check:
    var1.val = var2.val
    
    # or much more worse:
    somevar = var1 + var2
    var1 += var2
    # sic!
    var1 = var2
    
  10. ==============================

    10.모듈 내부에서는 어두운 마술을 통해 절대적으로 가능합니다.

    모듈 내부에서는 어두운 마술을 통해 절대적으로 가능합니다.

    import sys
    tst = sys.modules['tst']
    
    class Protect():
      def __assign__(self, value):
        raise Exception("This is an ex-parrot")
    
    var = Protect()  # once assigned...
    
    Module = type(tst)
    class ProtectedModule(Module):
      def __setattr__(self, attr, val):
        exists = getattr(self, attr, None)
        if exists is not None and hasattr(exists, '__assign__'):
          exists.__assign__(val)
        super().__setattr__(attr, val)
    
    tst.__class__ = ProtectedModule
    

    클래스 변경이 발생하면 모듈 내에서도 보호 된 변수에 쓸 수 없습니다. 위의 예에서는 코드가 tst라는 모듈에 상주한다고 가정합니다. repl에서 tst를 __main__으로 변경하면됩니다.

  11. from https://stackoverflow.com/questions/11024646/is-it-possible-to-overload-python-assignment by cc-by-sa and MIT license