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()
가능합니까? 미쳤어? 약을 먹어야하나요?
당신이 그것을 설명하는 방식은 절대 불가능합니다. 이름 할당은 Python의 기본 기능이며 동작을 변경하기위한 후크가 제공되지 않았습니다.
그러나 클래스 인스턴스의 멤버에 대한 할당 은를 재정 의하여 원하는대로 제어 할 수 있습니다 .__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
할당이 허용되는지 여부를 제어 하는 멤버 변수 가 있습니다. 잠금을 해제하여 값을 업데이트 할 수 있습니다.
아니요, 할당은 수정 후크가없는 언어 내장 입니다.
나는 그것이 가능하다고 생각하지 않는다. 내가보기에 변수에 대한 할당은 이전에 언급 한 객체에 아무 작업도 수행하지 않습니다. 단지 변수가 다른 객체를 "가리키는"것입니다.
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__()
메서드를 사용하여 래퍼 개체를 만들고 "변경할 수 없는"항목을 내부에 유지하여 원하는 동작을 모방 할 수 있습니다 .
최상위 네임 스페이스를 사용하면 불가능합니다. 당신이 달릴 때
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)
. 이것은 악성 코드로부터 보호하지 않습니다.
아니요
예를 들어 var라는 이름을 새 값으로 리 바인딩하고 있습니다. 실제로 Protect 인스턴스를 건 드리는 것이 아닙니다.
리 바인딩하려는 이름이 실제로 다른 엔티티 (예 : myobj.var)의 특성 인 경우 엔티티의 특성 / 속성에 값을 할당하는 것을 방지 할 수 있습니다. 그러나 나는 그것이 당신이 당신의 예에서 원하는 것이 아니라고 생각합니다.
전역 네임 스페이스에서는 이것이 불가능하지만, Protect
객체 의 여러 인스턴스 가 생성되는 것을 방지하기 위해보다 고급 Python 메타 프로그래밍을 활용할 수 있습니다 . 싱글 톤 패턴은 이것의 좋은 예입니다.
Singleton의 경우 인스턴스를 참조하는 원래 변수가 다시 할당 되더라도 인스턴스화되면 개체가 지속되도록 할 수 있습니다. 모든 후속 인스턴스는 동일한 객체에 대한 참조를 반환합니다.
이 패턴에도 불구하고 전역 변수 이름 자체가 재 할당되는 것을 막을 수는 없습니다.
일반적으로 내가 찾은 가장 좋은 접근 방식 은 속성 데코레이터에 의해 복제되는 __ilshift__
setter 및 __rlshift__
getter로 재정의 하는 것 입니다. 해결되는 거의 마지막 연산자이며 (| & ^) 논리 값은 더 낮습니다. 거의 사용되지 않습니다 ( __lrshift__
적지 만 고려할 수 있음).
PyPi 할당 패키지를 사용하는 동안에는 정방향 할당 만 제어 할 수 있으므로 운영자의 실제 '강도'가 낮습니다. PyPi 할당 패키지 예 :
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
추악한 해결책은 소멸자에 재 할당하는 것입니다. 그러나 실제 과부하 할당은 아닙니다.
import copy
global a
class MyClass():
def __init__(self):
a = 1000
# ...
def __del__(self):
a = copy.copy(self)
a = MyClass()
a = 1
예, 가능 __assign__
합니다 ast
. modify를 통해 처리 할 수 있습니다 .
pip install assign
Test with:
class T():
def __assign__(self, v):
print('called with %s' % v)
b = T()
c = b
You will get
>>> import magic
>>> import test
called with c
The project is at https://github.com/RyanKung/assign
And the simpler gist: https://gist.github.com/RyanKung/4830d6c8474e6bcefa4edd13f122b4df
Inside a module, this is absolutely possible, via a bit of dark magic.
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
Note that even from within the module, you cannot write to the protected variable once the class change happens. The above example assumes the code resides in a module named tst
. You can do this in the repl
by changing tst
to __main__
.
참고URL : https://stackoverflow.com/questions/11024646/is-it-possible-to-overload-python-assignment
'Nice programing' 카테고리의 다른 글
어떤 크로스 브라우저 문제에 직면 했습니까? (0) | 2020.11.21 |
---|---|
SourceTree에서 git-blame은 어디에 있습니까? (0) | 2020.11.21 |
녹아웃에서 관찰 가능한 배열 정렬 (0) | 2020.11.21 |
컴퓨터 과학을위한 수학 (0) | 2020.11.21 |
'killed'는 무엇을 의미합니까? (0) | 2020.11.21 |