__init__ 외부에 새 특성 생성 금지
한 초 기 파 클 를 있 수 를 원 합 니 기 다 에 서 번 썬 만 들 화 이 된 래 스 ized 니 원 합 ▁with 다 ▁python ▁initial in ▁( ▁a 를 파 ▁i 한 ) ▁once ▁class ▁that 번 를 초 에 있 기 기 서__init__에서는 새 특성을 허용하지 않지만 기존 특성의 수정은 허용합니다.예를 들어, 제가 이것을 하기 위해 볼 수 있는 몇 가지 해킹 방법이 있습니다.__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
슬롯은 다음과 같은 이점을 제공합니다.
파이썬적인 방법은 컴퓨터를 가지고 노는 대신 슬롯을 사용하는 것입니다.__setattr__문제를 해결할 수는 있지만 성능을 향상시키지는 못합니다. 체의속에사전은성다니 "저됩장객에다"에 됩니다.__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"
=> "새" 특성을 만들지 못합니다.
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)을 가진 둘 이상의 클래스에서 상속할 수 없습니다.
만약 누군가가 장식가와 함께 그것을 하는 것에 관심이 있다면, 여기에 효과적인 해결책이 있습니다.
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__ = ('foo', 'bar', 'baz')Python은 모든 인스턴스에 foo, bar, baz만 있는지 확인합니다.그러나 설명서 목록의 주의 사항을 읽어 보십시오!
올바른 방법은 다음을 무시하는 것입니다.__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
다음은 어떻습니까?
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)
여기에 _frozen 속성이나 그 안에서 freeze()하는 방법이 필요하지 않은 접근법이 있습니다.
init 동안 모든 클래스 속성을 인스턴스에 추가합니다.
_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
저는 요헨 리첼의 "겨울왕국"을 좋아합니다.불편한 점은 클래스를 인쇄할 때 동결 변수가 나타난다는 것입니다.__dict 권한이 부여된 속성 목록(슬롯과 유사)을 만들어 이 문제를 해결했습니다.
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
그FrozenClassJochen Ritzel에 의해, 멋지지만, 전화하는 것._frozen()매번 수업을 시작하는 것이 그렇게 멋지지 않을 때 (그리고 당신은 그것을 잊어버리는 위험을 감수해야 합니다).추가했습니다.__init_slots__함수:
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
오버라이드의 성능 영향에 대해 언급한 답변이 없습니다.__setattr__이는 많은 작은 개체를 만들 때 문제가 될 수 있습니다. (그리고 성능 솔루션이 될 수 있지만 피클/상속을 제한합니다.
그래서 저는 다음과 같은 변형을 생각해냈습니다. 이 변형은 초기화 후에 느린 설정자를 설치합니다.
class FrozenClass:
def freeze(self):
def frozen_setattr(self, key, value):
if not hasattr(self, key):
raise TypeError("Cannot set {}: {} is a frozen class".format(key, self))
object.__setattr__(self, key, value)
self.__setattr__ = frozen_setattr
class Foo(FrozenClass): ...
전화하기 싫으면요.freeze에의 __init__상속이 문제이거나 상속이 포함되지 않도록 하려면vars()그것은 또한 적응할 수 있습니다: 예를 들어, 여기에 기반한 장식가 버전이 있습니다.pystrict답변:
import functools
def strict(cls):
cls._x_setter = getattr(cls, "__setattr__", object.__setattr__)
cls._x_init = cls.__init__
@functools.wraps(cls.__init__)
def wrapper(self, *args, **kwargs):
cls._x_init(self, *args, **kwargs)
def frozen_setattr(self, key, value):
if not hasattr(self, key):
raise TypeError("Class %s is frozen. Cannot set '%s'." % (cls.__name__, key))
cls._x_setter(self, key, value)
cls.__setattr__ = frozen_setattr
cls.__init__ = wrapper
return cls
@strict
class Foo: ...
저는 이 문제에 대한 해결책으로 파이스트릭트를 썼습니다.너무 커서 모든 코드를 스택 오버플로에 붙여넣을 수 없습니다.
pystrict클래스를 고정하기 위해 사용할 수 있는 피피 설치형 장식기입니다.여기에 있는 많은 솔루션은 상속을 제대로 지원하지 않습니다.
한다면__slots__상속 문제로 인해 귀하에게 효과가 없습니다. 이것은 좋은 대안입니다.
README에는 프로젝트에서 mypy와 pylint를 실행하고 있는 경우에도 이러한 데코레이터가 필요한 이유를 보여주는 예가 있습니다.
pip install pystrict
그런 다음 @strict decorator를 사용합니다.
from pystrict import strict
@strict
class Blah
def __init__(self):
self.attr = 1
@dataclass(slots=True) 3 바나파 (썬이너 3.10)
나는 이것을 사랑에 빠졌어요
main.py
from dataclasses import dataclass
@dataclass(slots=True)
class C:
n: int
s: str
c = C(n=1, s='one')
assert c.n == 1
assert c.s == 'one'
c.n == 2
c.s == 'two'
c.asdf = 2
결과:
Traceback (most recent call last):
File "/home/ciro/main.py", line 15, in <module>
c.asdf = 2
AttributeError: 'C' object has no attribute 'asdf'
방법에 주목@dataclass유형 주석을 사용하여 속성을 한 번만 정의하면 됩니다.
n: int
s: str
그런 다음 반복 없이 무료로 얻을 수 있습니다.
-
def __init__(n, s): self.n = n self.s = s __slots__ = ['n', 's']
이 예에 표시되지 않은 기타 무료 항목:
__str____eq__개체 인스턴스의 특성을 기준으로 동일성 비교__hash__또한 사용하는 경우frozen=True사전 키로 사용자 지정 유형의 개체
Python 3.10.7, Ubuntu 22.10에서 테스트되었습니다.
언급URL : https://stackoverflow.com/questions/3603502/prevent-creating-new-attributes-outside-init
'programing' 카테고리의 다른 글
| jQuery에서 버튼의 텍스트를 변경하는 방법은 무엇입니까? (0) | 2023.05.28 |
|---|---|
| 셀을 편집할 때 키를 눌렀을 때 발생하는 이벤트가 있습니까? (0) | 2023.05.28 |
| Git: 로컬 지점만 나열하려면 어떻게 해야 합니까? (0) | 2023.05.28 |
| 스위프트에서 오브젝티브-C 코드를 어떻게 부르나요? (0) | 2023.05.28 |
| http 핸들러 인터페이스에서 boolIsReuseable의 중요성 (0) | 2023.05.23 |