__init__ 외부에서 새 속성 생성 방지
일단으로 초기화되고 __init__
새 속성을 허용하지 않지만 기존 속성의 수정을 허용 하는 클래스 (Python에서)를 만들 수 있기를 원합니다 . 예를 들어 다음 __setattr__
과 같은 방법을 사용하는 것과 같이 몇 가지 해킹 방법이 있습니다.
def __setattr__(self, attribute, value):
if not attribute in self.__dict__:
print "Cannot set %s" % attribute
else:
self.__dict__[attribute] = value
그런 다음 __dict__
내부 __init__
에서 직접 편집 하지만 이것을 수행하는 '적절한'방법이 있는지 궁금합니다.
__dict__
직접 사용 하지는 않지만 인스턴스를 명시 적으로 "고정"하는 함수를 추가 할 수 있습니다.
class FrozenClass(object):
__isfrozen = False
def __setattr__(self, key, value):
if self.__isfrozen and not hasattr(self, key):
raise TypeError( "%r is a frozen class" % self )
object.__setattr__(self, key, value)
def _freeze(self):
self.__isfrozen = True
class Test(FrozenClass):
def __init__(self):
self.x = 42#
self.y = 2**3
self._freeze() # no new attributes after this point.
a,b = Test(), Test()
a.x = 10
b.z = 10 # fails
누군가 데코레이터로 작업하는 데 관심이 있다면 다음과 같은 작업 솔루션이 있습니다.
from functools import wraps
def froze_it(cls):
cls.__frozen = False
def frozensetattr(self, key, value):
if self.__frozen and not hasattr(self, key):
print("Class {} is frozen. Cannot set {} = {}"
.format(cls.__name__, key, value))
else:
object.__setattr__(self, key, value)
def init_decorator(func):
@wraps(func)
def wrapper(self, *args, **kwargs):
func(self, *args, **kwargs)
self.__frozen = True
return wrapper
cls.__setattr__ = frozensetattr
cls.__init__ = init_decorator(cls.__init__)
return cls
사용하기 매우 간단합니다.
@froze_it
class Foo(object):
def __init__(self):
self.bar = 10
foo = Foo()
foo.bar = 42
foo.foobar = "no way"
결과:
>>> Class Foo is frozen. Cannot set foobar = no way
사실, 당신이 원하지 않는 __setattr__
당신이 원하는, __slots__
. __slots__ = ('foo', 'bar', 'baz')
클래스 본문에 추가 하면 Python은 모든 인스턴스에 foo, bar 및 baz 만 있는지 확인합니다. 그러나 설명서 목록의주의 사항을 읽으십시오!
슬롯은 이동 방법입니다.
비단뱀적인 방법은 __setter__
. 문제를 해결할 수 있지만 성능이 향상되지는 않습니다. 객체의 속성은 사전 " __dict__
"에 저장됩니다. 이것이 바로 지금까지 만든 클래스의 객체에 속성을 동적으로 추가 할 수있는 이유입니다. 속성 저장에 딕셔너리를 사용하는 것은 매우 편리하지만 인스턴스 변수가 적은 객체를위한 공간 낭비를 의미 할 수 있습니다.
슬롯 은이 공간 소비 문제를 해결하는 좋은 방법입니다. 객체에 속성을 동적으로 추가 할 수있는 동적 사전을 갖는 대신 슬롯은 인스턴스 생성 후 추가를 금지하는 정적 구조를 제공합니다.
클래스를 디자인 할 때 슬롯을 사용하여 속성의 동적 생성을 방지 할 수 있습니다. 슬롯을 정의하려면 이름으로 목록을 정의해야합니다 __slots__
. 목록에는 사용하려는 모든 속성이 포함되어야합니다. 다음 클래스에서이를 설명합니다. 슬롯 목록에는 속성 "val"의 이름 만 포함됩니다.
class S(object):
__slots__ = ['val']
def __init__(self, v):
self.val = v
x = S(42)
print(x.val)
x.new = "not possible"
=> "new"속성 생성 실패 :
42
Traceback (most recent call last):
File "slots_ex.py", line 12, in <module>
x.new = "not possible"
AttributeError: 'S' object has no attribute 'new'
주의 :
Python 3.3 이후로 공간 소비를 최적화하는 이점은 더 이상 인상적이지 않습니다. Python 3.3에서는 키 공유 사전이 객체 저장에 사용됩니다. 인스턴스의 속성은 내부 저장소의 일부를 서로 공유 할 수 있습니다. 즉, 키와 해당 해시를 저장하는 부분입니다. 이것은 비 내장 유형의 많은 인스턴스를 생성하는 프로그램의 메모리 소비를 줄이는 데 도움이됩니다. 그러나 여전히 동적으로 생성 된 속성을 피하는 방법입니다.
슬롯을 사용하면 자체 비용이 발생합니다. 직렬화 (예 : 피클)가 중단됩니다. 또한 다중 상속을 중단합니다. 클래스는 슬롯을 정의하거나 C 코드에 정의 된 인스턴스 레이아웃 (예 : list, tuple 또는 int)을 포함하는 둘 이상의 클래스에서 상속 할 수 없습니다.
올바른 방법은 __setattr__
. 그것이 거기에있는 이유입니다.
저는 데코레이터를 사용하는 솔루션을 매우 좋아합니다. 각 클래스에 대해 최소한의 추가로 프로젝트 전체의 많은 클래스에서 사용하기 쉽기 때문입니다. 그러나 상속과는 잘 작동하지 않습니다. 그래서 여기에 내 버전이 있습니다. __setattr__ 함수 만 재정의합니다. 속성이 존재하지 않고 호출자 함수가 __init__가 아니면 오류 메시지를 출력합니다.
import inspect
def froze_it(cls):
def frozensetattr(self, key, value):
if not hasattr(self, key) and inspect.stack()[1][3] != "__init__":
print("Class {} is frozen. Cannot set {} = {}"
.format(cls.__name__, key, value))
else:
self.__dict__[key] = value
cls.__setattr__ = frozensetattr
return cls
@froze_it
class A:
def __init__(self):
self._a = 0
a = A()
a._a = 1
a._b = 2 # error
다음은 init에서 freeze ()하는 _frozen 속성이나 메서드가 필요하지 않은 접근 방식입니다.
초기화 하는 동안 모든 클래스 속성을 인스턴스에 추가합니다.
_frozen, freeze ()가없고 _frozen도 vars (instance) 출력에 표시되지 않기 때문에 이것을 좋아합니다.
class MetaModel(type):
def __setattr__(self, name, value):
raise AttributeError("Model classes do not accept arbitrary attributes")
class Model(object):
__metaclass__ = MetaModel
# init will take all CLASS attributes, and add them as SELF/INSTANCE attributes
def __init__(self):
for k, v in self.__class__.__dict__.iteritems():
if not k.startswith("_"):
self.__setattr__(k, v)
# setattr, won't allow any attributes to be set on the SELF/INSTANCE that don't already exist
def __setattr__(self, name, value):
if not hasattr(self, name):
raise AttributeError("Model instances do not accept arbitrary attributes")
else:
object.__setattr__(self, name, value)
# Example using
class Dog(Model):
name = ''
kind = 'canine'
d, e = Dog(), Dog()
print vars(d)
print vars(e)
e.junk = 'stuff' # fails
이것에 대해 :
class A():
__allowed_attr=('_x', '_y')
def __init__(self,x=0,y=0):
self._x=x
self._y=y
def __setattr__(self,attribute,value):
if not attribute in self.__class__.__allowed_attr:
raise AttributeError
else:
super().__setattr__(attribute,value)
I like the "Frozen" of Jochen Ritzel. The inconvenient is that the isfrozen variable then appears when printing a Class.__dict I went around this problem this way by creating a list of authorized attributes (similar to slots):
class Frozen(object):
__List = []
def __setattr__(self, key, value):
setIsOK = False
for item in self.__List:
if key == item:
setIsOK = True
if setIsOK == True:
object.__setattr__(self, key, value)
else:
raise TypeError( "%r has no attributes %r" % (self, key) )
class Test(Frozen):
_Frozen__List = ["attr1","attr2"]
def __init__(self):
self.attr1 = 1
self.attr2 = 1
The FrozenClass
by Jochen Ritzel is cool, but calling _frozen()
when initialing a class every time is not so cool (and you need to take the risk of forgetting it). I added a __init_slots__
function:
class FrozenClass(object):
__isfrozen = False
def _freeze(self):
self.__isfrozen = True
def __init_slots__(self, slots):
for key in slots:
object.__setattr__(self, key, None)
self._freeze()
def __setattr__(self, key, value):
if self.__isfrozen and not hasattr(self, key):
raise TypeError( "%r is a frozen class" % self )
object.__setattr__(self, key, value)
class Test(FrozenClass):
def __init__(self):
self.__init_slots__(["x", "y"])
self.x = 42#
self.y = 2**3
a,b = Test(), Test()
a.x = 10
b.z = 10 # fails
pystrict
is a pypi installable decorator inspired by this stackoverflow question that can be used with classes to freeze them. There is an example to the README that shows why a decorator like this is needed even if you have mypy and pylint running on your project:
pip install pystrict
Then just use the @strict decorator:
from pystrict import strict
@strict
class Blah
def __init__(self):
self.attr = 1
참고URL : https://stackoverflow.com/questions/3603502/prevent-creating-new-attributes-outside-init
'program tip' 카테고리의 다른 글
열려있는 SQL 연결 닫기를 사용하여 종료합니까? (0) | 2020.11.20 |
---|---|
수평 UL 메뉴의 수직 분할기 (0) | 2020.11.20 |
ASP.NET MVC에서 업로드 된 파일의 유효성을 검사하는 방법은 무엇입니까? (0) | 2020.11.20 |
Rails 앱에서 force_ssl로 무한 리디렉션 루프가 발생하는 이유는 무엇입니까? (0) | 2020.11.20 |
IE에서 로컬 저장소 콘텐츠보기 (0) | 2020.11.20 |