클래스에 동적으로 속성을 추가하려면 어떻게 해야 합니까?
목표는 DB 결과 집합처럼 작동하는 모의 클래스를 만드는 것입니다.
하여 반환된 경우 dict 표현은 "dict " 입니다.{'ab':100, 'cd':200}
보고 것은 '보다'입니다.
>>> dummy.ab
100
처음에는 이렇게 할 수 있을 거라고 생각했어요.
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
def __init__(self, ks, vs):
for i, k in enumerate(ks):
self[k] = vs[i]
setattr(self, k, property(lambda x: vs[i], self.fn_readyonly))
def fn_readonly(self, v)
raise "It is ready only"
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
c.ab
대신 속성 개체를 반환합니다.
의 교환setattr
을 하다k = property(lambda x: vs[i])
전혀 쓸모가 없다.
그러면 런타임에 인스턴스 속성을 생성하는 올바른 방법은 무엇일까요?
추신: "어떻게 사용하는가?"에 제시된 대안을 알고 있습니다.
이제 나이가 들고 현명해져서 무슨 일이 일어나고 있는지 알게 되었으니 이 대답을 확대해야 할 것 같아요.늦더라도 안 하느니보다는 낫다.
클래스에 동적으로 속성을 추가할 수 있습니다.하지만 그게 함정입니다. 수업에 추가해야 합니다.
>>> class Foo(object):
... pass
...
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4
A property
기술자라고 불리는 것을 간단하게 구현한 것입니다.이 오브젝트는 특정 클래스의 특정 속성에 대한 커스텀 처리를 제공하는 오브젝트입니다.일종의 거대한 인수인수처럼if
에서 뽑다__getattribute__
.
가 부탁할 때foo.b
은 "Python"이 "Python"이라는 을 알 수 있습니다.b
클래스에 정의되어 있는 디스크립터 프로토콜을 구현합니다.즉, 이 프로토콜은 이 프로토콜이 다음 명령어를 가진 객체임을 의미합니다.__get__
,__set__
, 「」__delete__
가 그하기 때문에 은 Python을 호출합니다.Foo.b.__get__(foo, Foo)
아트리뷰트 property
각 는, 「」, 「」, 「」fget
,fset
, 「」fdel
하셨습니다.property
컨스트럭터
기술자는 실제로 파이썬이 전체 OO 구현의 배관을 노출하는 방식입니다.사실, 설명자의 다른 타입이 있습니다.property
.
>>> class Foo(object):
... def bar(self):
... pass
...
>>> Foo().bar
<bound method Foo.bar of <__main__.Foo object at 0x7f2a439d5dd0>>
>>> Foo().bar.__get__
<method-wrapper '__get__' of instancemethod object at 0x7f2a43a8a5a0>
겸손한 방법은 또 다른 종류의 설명자에 불과합니다.__get__
이 과 같습니다 tacks 」츠키다
def __get__(self, instance, owner):
return functools.partial(self.function, instance)
어쨌든, 이것이 기술자가 수업에만 종사하는 이유라고 생각합니다.그것은, 애초에 수업의 원동력이 되는 것을 정식화한 것입니다. 규칙은클래스에 를 할당하는 자체가 '클래스'의 입니다.클래스에 디스크립터를 할당할 수 있습니다.클래스 자체는type
, 중!Foo.bar
콜이 있다.property.__get__
; 클래스 속성으로 접근하면 디스크립터가 자신을 반환하는 것은 관용적인 표현입니다.
Python의 모든 OO 시스템이 Python으로 표현된다는 것은 꽤 멋진 일이라고 생각합니다.:)
아, 그리고 얼마 전에 설명자에 관한 블로그 글을 썼는데 관심 있으시다면요.
목표는 DB 결과 집합처럼 작동하는 모의 클래스를 만드는 것입니다.
그래서 네가 원하는 건 a['b']를 a.b로 철자할 수 있는 사전이야?
간단합니다.
class atdict(dict):
__getattr__= dict.__getitem__
__setattr__= dict.__setitem__
__delattr__= dict.__delitem__
당신은 그것을 위해 부동산을 사용할 필요가 없습니다.【 】를 만 하면 .__setattr__
읽기 전용입니다.
class C(object):
def __init__(self, keys, values):
for (key, value) in zip(keys, values):
self.__dict__[key] = value
def __setattr__(self, name, value):
raise Exception("It is read only!")
짠!
>>> c = C('abc', [1,2,3])
>>> c.a
1
>>> c.b
2
>>> c.c
3
>>> c.d
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
AttributeError: 'C' object has no attribute 'd'
>>> c.d = 42
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
Exception: It is read only!
>>> c.a = 'blah'
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 6, in __setattr__
Exception: It is read only!
필드 전체 목록을 미리 알고 있기 때문에 를 사용하면 이 문제를 훨씬 쉽게 해결할 수 있을 것 같습니다.
from collections import namedtuple
Foo = namedtuple('Foo', ['bar', 'quux'])
foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux
foo2 = Foo() # error
직접 property()
인스턴스에서는 동작하지 않습니다.
python 클래스에 동적으로 속성을 추가하려면 어떻게 해야 합니까?
속성을 추가할 개체가 있다고 가정합니다.일반적으로 다운스트림 사용을 가진 코드의 속성에 대한 액세스 관리를 시작해야 할 때 속성을 사용하여 일관된 API를 유지할 수 있습니다.일반적으로 오브젝트가 정의되어 있는 소스 코드에 추가합니다만, 액세스 권한이 없거나, 실제로 동적으로 함수를 프로그래밍 방식으로 선택할 필요가 있다고 가정해 보겠습니다.
클래스 만들기
의 매뉴얼에 근거한 예를 사용하여 "숨김" 속성을 가진 오브젝트 클래스를 만들고 그 인스턴스를 만듭니다.
class C(object):
'''basic class'''
_x = None
o = C()
Python에서는 확실한 방법이 하나 있을 것으로 기대하고 있습니다.하지만 이 경우에는 두 가지 방법을 보여 드리겠습니다. 데코레이터 표기법과 사용하지 않는 방법입니다.일단 데코레이터 표기 없이.이는 getter, setter 또는 deleter의 동적 할당에 더 유용할 수 있습니다.
동적(일명.k.a)몽키 패치 적용)
클래스용으로 작성합시다.
def getx(self):
return self._x
def setx(self, value):
self._x = value
def delx(self):
del self._x
그리고 이제 우리는 이것들을 사유지에 할당합니다.여기서는 동적 질문에 대한 답을 프로그래밍 방식으로 함수를 선택할 수 있습니다.
C.x = property(getx, setx, delx, "I'm the 'x' property.")
용도:
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
>>> help(C.x)
Help on property:
I'm the 'x' property.
데코레이터
위의 데코레이터 표기법과 동일하게 할 수 있지만, 이 경우 메서드에 모두 같은 이름을 붙여야 합니다(속성과 동일하게 하는 것이 좋습니다).따라서 프로그램 할당은 위의 메서드를 사용하는 것만큼 간단하지 않습니다.
@property
def x(self):
'''I'm the 'x' property.'''
return self._x
@x.setter
def x(self, value):
self._x = value
@x.deleter
def x(self):
del self._x
또한 프로비저닝된 설정자 및 삭제자를 포함하는 속성 개체를 클래스에 할당합니다.
C.x = x
용도:
>>> help(C.x)
Help on property:
I'm the 'x' property.
>>> o.x
>>> o.x = 'foo'
>>> o.x
'foo'
>>> del o.x
>>> print(o.x)
None
다음과 같은 솔루션이 있습니다.
- 속성 이름을 문자열로 지정할 수 있으므로 프로그램에 모두 나열되지 않고 일부 외부 데이터 원본에서 가져올 수 있습니다.
- 개체를 만들 때마다가 아니라 클래스가 정의될 때 속성을 추가합니다.
클래스를 정의한 후 다음 작업을 수행하여 동적으로 속성을 추가합니다.
setattr(SomeClass, 'propertyName', property(getter, setter))
다음은 Python 3에서 테스트한 완전한 예입니다.
#!/usr/bin/env python3
class Foo():
pass
def get_x(self):
return 3
def set_x(self, value):
print("set x on %s to %d" % (self, value))
setattr(Foo, 'x', property(get_x, set_x))
foo1 = Foo()
foo1.x = 12
print(foo1.x)
검색 엔진에서 나오는 경우 동적 속성에 대해 이야기할 때 다음 두 가지를 살펴봅니다.
class Foo:
def __init__(self):
# we can dynamically have access to the properties dict using __dict__
self.__dict__['foo'] = 'bar'
assert Foo().foo == 'bar'
# or we can use __getattr__ and __setattr__ to execute code on set/get
class Bar:
def __init__(self):
self._data = {}
def __getattr__(self, key):
return self._data[key]
def __setattr__(self, key, value):
self._data[key] = value
bar = Bar()
bar.foo = 'bar'
assert bar.foo == 'bar'
__dict__
동적으로 생성된 속성을 넣는 경우 좋습니다. __getattr__
데이터베이스 쿼리 등 값이 필요한 경우에만 작업을 수행하는 것이 좋습니다.set/get 콤보는 위의 예시와 같이 클래스에 저장된 데이터에 대한 액세스를 단순화하는 데 유용합니다.
하나의 동적 속성만 원하는 경우 property() 내장 함수를 확인하십시오.
다음 코드를 사용하여 사전 개체를 사용하여 클래스 속성을 업데이트할 수 있습니다.
class ExampleClass():
def __init__(self, argv):
for key, val in argv.items():
self.__dict__[key] = val
if __name__ == '__main__':
argv = {'intro': 'Hello World!'}
instance = ExampleClass(argv)
print instance.intro
할 수 , 할 수 .이러한 속성은, 내장된 「」를 사용해 변경할 수 있습니다.__dict__
★★★★★★★★★★★★★★★★★★:
class C(object):
def __init__(self, ks, vs):
self.__dict__ = dict(zip(ks, vs))
if __name__ == "__main__":
ks = ['ab', 'cd']
vs = [12, 34]
c = C(ks, vs)
print(c.ab) # 12
저는 이 Stack Overflow 게시물에 간단한 유형을 만드는 클래스 팩토리를 만들기 위해 비슷한 질문을 했습니다.그 결과, 클래스 팩토리의 작업 버전이 있는 이 답변이 나왔습니다.다음은 답변의 일부입니다.
def Struct(*args, **kwargs):
def init(self, *iargs, **ikwargs):
for k,v in kwargs.items():
setattr(self, k, v)
for i in range(len(iargs)):
setattr(self, args[i], iargs[i])
for k,v in ikwargs.items():
setattr(self, k, v)
name = kwargs.pop("name", "MyStruct")
kwargs.update(dict((k, None) for k in args))
return type(name, (object,), {'__init__': init, '__slots__': kwargs.keys()})
>>> Person = Struct('fname', 'age')
>>> person1 = Person('Kevin', 25)
>>> person2 = Person(age=42, fname='Terry')
>>> person1.age += 10
>>> person2.age -= 10
>>> person1.fname, person1.age, person2.fname, person2.age
('Kevin', 35, 'Terry', 32)
>>>
이를 사용하여 목표인 기본값을 생성할 수 있습니다(이 문제에 대한 답변도 있습니다).
할 수 .property()
속성이 데이터 기술자이기 때문에 런타임에 인스턴스로 이동합니다.새 오버로드해야 합니다.__getattribute__
인스턴스의 데이터 기술자를 처리하기 위해 사용합니다.
이것은 OP가 원했던 것과는 조금 다르지만, 저는 제대로 된 솔루션이 나올 때까지 머리를 싸매고 있었기 때문에, 다음 사람을 위해 여기에 두겠습니다.
다이내믹 세터와 게터를 지정할 방법이 필요했습니다.
class X:
def __init__(self, a=0, b=0, c=0):
self.a = a
self.b = b
self.c = c
@classmethod
def _make_properties(cls, field_name, inc):
_inc = inc
def _get_properties(self):
if not hasattr(self, '_%s_inc' % field_name):
setattr(self, '_%s_inc' % field_name, _inc)
inc = _inc
else:
inc = getattr(self, '_%s_inc' % field_name)
return getattr(self, field_name) + inc
def _set_properties(self, value):
setattr(self, '_%s_inc' % field_name, value)
return property(_get_properties, _set_properties)
나는 내 분야를 미리 알고 있기 때문에 내 자산을 만들 것이다.메모: 이 PER 인스턴스는 실행할 수 없습니다.이러한 속성은 클래스에 존재합니다!!!
for inc, field in enumerate(['a', 'b', 'c']):
setattr(X, '%s_summed' % field, X._make_properties(field, inc))
이제 모든 것을 테스트해 봅시다.
x = X()
assert x.a == 0
assert x.b == 0
assert x.c == 0
assert x.a_summed == 0 # enumerate() set inc to 0 + 0 = 0
assert x.b_summed == 1 # enumerate() set inc to 1 + 0 = 1
assert x.c_summed == 2 # enumerate() set inc to 2 + 0 = 2
# we set the variables to something
x.a = 1
x.b = 2
x.c = 3
assert x.a_summed == 1 # enumerate() set inc to 0 + 1 = 1
assert x.b_summed == 3 # enumerate() set inc to 1 + 2 = 3
assert x.c_summed == 5 # enumerate() set inc to 2 + 3 = 5
# we're changing the inc now
x.a_summed = 1
x.b_summed = 3
x.c_summed = 5
assert x.a_summed == 2 # we set inc to 1 + the property was 1 = 2
assert x.b_summed == 5 # we set inc to 3 + the property was 2 = 5
assert x.c_summed == 8 # we set inc to 5 + the property was 3 = 8
헷갈리나요?네, 의미 있는 실제 사례를 떠올리지 못해 죄송합니다.또한, 이것은 가벼운 마음을 위한 것이 아닙니다.
하기 위한 의 방법은 ""를 정의하는 입니다.__slots__
이렇게 하면 인스턴스가 새로운 속성을 가질 수 없습니다.
ks = ['ab', 'cd']
vs = [12, 34]
class C(dict):
__slots__ = []
def __init__(self, ks, vs): self.update(zip(ks, vs))
def __getattr__(self, key): return self[key]
if __name__ == "__main__":
c = C(ks, vs)
print c.ab
은 that prints that12
c.ab = 33
다음과 같습니다.AttributeError: 'C' object has no attribute 'ab'
원하는 효과를 얻는 또 다른 예에 불과합니다.
class Foo(object):
_bar = None
@property
def bar(self):
return self._bar
@bar.setter
def bar(self, value):
self._bar = value
def __init__(self, dyn_property_name):
setattr(Foo, dyn_property_name, Foo.bar)
이제 다음과 같은 작업을 수행할 수 있습니다.
>>> foo = Foo('baz')
>>> foo.baz = 5
>>> foo.bar
5
>>> foo.baz
5
많은 답변이 있지만 마음에 드는 답변을 찾을 수 없었습니다.난 내 해결책을 찾아냈어property
하다번째 질문에 :
#!/usr/local/bin/python3
INITS = { 'ab': 100, 'cd': 200 }
class DP(dict):
def __init__(self):
super().__init__()
for k,v in INITS.items():
self[k] = v
def _dict_set(dp, key, value):
dp[key] = value
for item in INITS.keys():
setattr(
DP,
item,
lambda key: property(
lambda self: self[key], lambda self, value: _dict_set(self, key, value)
)(item)
)
a = DP()
print(a) # {'ab': 100, 'cd': 200}
a.ab = 'ab100'
a.cd = False
print(a.ab, a.cd) # ab100 False
이것은 동작하고 있는 것 같습니다(단, 이하를 참조).
class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
self.__dict__.update(self)
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
좀 더 복잡한 행동이 필요한 경우 언제든지 답변을 편집하십시오.
편집하다
대규모 데이터셋의 경우 다음과 같은 방법이 메모리 효율이 높을 수 있습니다.
class data(dict,object):
def __init__(self,*args,**argd):
dict.__init__(self,*args,**argd)
def __getattr__(self,name):
return self[name]
def __setattr__(self,name,value):
raise AttributeError,"Attribute '%s' of '%s' object cannot be set"%(name,self.__class__.__name__)
def __delattr__(self,name):
raise AttributeError,"Attribute '%s' of '%s' object cannot be deleted"%(name,self.__class__.__name__)
질문의 주요 요점에 답하려면 dict의 읽기 전용 속성을 불변의 데이터 소스로 사용합니다.
목표는 DB 결과 집합처럼 작동하는 모의 클래스를 만드는 것입니다.
하여 반환된 경우 dict 표현은 "dict " 입니다.
{'ab':100, 'cd':200}
저는 '이렇게 하다'라고 볼 수 있겠죠>>> dummy.ab 100
namedtuple
collections
모듈을 사용하여 다음 작업을 수행합니다.
import collections
data = {'ab':100, 'cd':200}
def maketuple(d):
'''given a dict, return a namedtuple'''
Tup = collections.namedtuple('TupName', d.keys()) # iterkeys in Python2
return Tup(**d)
dummy = maketuple(data)
dummy.ab
100
class atdict(dict):
def __init__(self, value, **kwargs):
super().__init__(**kwargs)
self.__dict = value
def __getattr__(self, name):
for key in self.__dict:
if type(self.__dict[key]) is list:
for idx, item in enumerate(self.__dict[key]):
if type(item) is dict:
self.__dict[key][idx] = atdict(item)
if type(self.__dict[key]) is dict:
self.__dict[key] = atdict(self.__dict[key])
return self.__dict[name]
d1 = atdict({'a' : {'b': [{'c': 1}, 2]}})
print(d1.a.b[0].c)
출력은 다음과 같습니다.
>> 1
kjfletch에서 아이디어 확장
# This is my humble contribution, extending the idea to serialize
# data from and to tuples, comparison operations and allowing functions
# as default values.
def Struct(*args, **kwargs):
FUNCTIONS = (types.BuiltinFunctionType, types.BuiltinMethodType, \
types.FunctionType, types.MethodType)
def init(self, *iargs, **ikwargs):
"""Asume that unamed args are placed in the same order than
astuple() yields (currently alphabetic order)
"""
kw = list(self.__slots__)
# set the unnamed args
for i in range(len(iargs)):
k = kw.pop(0)
setattr(self, k, iargs[i])
# set the named args
for k, v in ikwargs.items():
setattr(self, k, v)
kw.remove(k)
# set default values
for k in kw:
v = kwargs[k]
if isinstance(v, FUNCTIONS):
v = v()
setattr(self, k, v)
def astuple(self):
return tuple([getattr(self, k) for k in self.__slots__])
def __str__(self):
data = ['{}={}'.format(k, getattr(self, k)) for k in self.__slots__]
return '<{}: {}>'.format(self.__class__.__name__, ', '.join(data))
def __repr__(self):
return str(self)
def __eq__(self, other):
return self.astuple() == other.astuple()
name = kwargs.pop("__name__", "MyStruct")
slots = list(args)
slots.extend(kwargs.keys())
# set non-specific default values to None
kwargs.update(dict((k, None) for k in args))
return type(name, (object,), {
'__init__': init,
'__slots__': tuple(slots),
'astuple': astuple,
'__str__': __str__,
'__repr__': __repr__,
'__eq__': __eq__,
})
Event = Struct('user', 'cmd', \
'arg1', 'arg2', \
date=time.time, \
__name__='Event')
aa = Event('pepe', 77)
print(aa)
raw = aa.astuple()
bb = Event(*raw)
print(bb)
if aa == bb:
print('Are equals')
cc = Event(cmd='foo')
print(cc)
출력:
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
<Event: user=pepe, cmd=77, arg1=None, arg2=None, date=1550051398.3651814>
Are equals
<Event: user=None, cmd=foo, arg1=None, arg2=None, date=1550051403.7938335>
나에게 효과가 있는 것은, 다음과 같습니다.
class C:
def __init__(self):
self._x=None
def g(self):
return self._x
def s(self, x):
self._x = x
def d(self):
del self._x
def s2(self,x):
self._x=x+x
x=property(g,s,d)
c = C()
c.x="a"
print(c.x)
C.x=property(C.g, C.s2)
C.x=C.x.deleter(C.d)
c2 = C()
c2.x="a"
print(c2.x)
산출량
a
aa
일부 인스턴스 속성을 기반으로 동적으로 속성을 생성해야 하는 경우 다음 코드를 사용할 수 있습니다.
import random
class Foo:
def __init__(self, prop_names: List[str], should_property_be_zero: bool = False) -> None:
self.prop_names = prop_names
self.should_property_be_zero = should_property_be_zero
def create_properties(self):
for name in self.prop_names:
setattr(self.__class__, name, property(fget=lambda self: 0 if self.should_property_be_zero else random.randint(1, 100)))
은 '먹다'를 사용한다는 입니다.setattr(self.__class__, name, ...)
andsetattr(self, name, ...)
사용 예:
In [261]: prop_names = ['a', 'b']
In [262]: ff = Foo(prop_names=prop_names, should_property_be_zero=False)
In [263]: ff.create_properties()
In [264]: ff.a
Out[264]: 10
In [265]: ff.b
Out[265]: 37
In [266]: ft = Foo(prop_names=prop_names, should_property_be_zero=True)
In [267]: ft.create_properties()
In [268]: ft.a
Out[268]: 0
In [269]: ft.b
Out[269]: 0
을 설정하면 이 올라갑니다.AttributeError: can't set attribute
★★★★★★★★★★★★★★★★★★:
In [270]: ff.a = 5
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-270-5f9cad5b617d> in <module>
----> 1 ff.a = 5
AttributeError: can't set attribute
In [271]: ft.a = 5
---------------------------------------------------------------------------
AttributeError Traceback (most recent call last)
<ipython-input-271-65e7b8e25b67> in <module>
----> 1 ft.a = 5
AttributeError: can't set attribute
에 , 은 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★__getattr__
★★★★★★★★★★★★★★★★★」__setattr__
하다
class C(object):
def __init__(self, properties):
self.existing = "Still Here"
self.properties = properties
def __getattr__(self, name):
if "properties" in self.__dict__ and name in self.properties:
return self.properties[name] # Or call a function, etc
return self.__dict__[name]
def __setattr__(self, name, value):
if "properties" in self.__dict__ and name in self.properties:
self.properties[name] = value
else:
self.__dict__[name] = value
if __name__ == "__main__":
my_properties = {'a':1, 'b':2, 'c':3}
c = C(my_properties)
assert c.a == 1
assert c.existing == "Still Here"
c.b = 10
assert c.properties['b'] == 10
다음은 프로그래밍 방식으로 속성 개체를 만드는 간단한 예입니다.
#!/usr/bin/python3
class Counter:
def __init__(self):
cls = self.__class__
self._count = 0
cls.count = self.count_ref()
def count_get(self):
print(f'count_get: {self._count}')
return self._count
def count_set(self, value):
self._count = value
print(f'count_set: {self._count}')
def count_del(self):
print(f'count_del: {self._count}')
def count_ref(self):
cls = self.__class__
return property(fget=cls.count_get, fset=cls.count_set, fdel=cls.count_del)
counter = Counter()
counter.count
for i in range(5):
counter.count = i
del counter.count
'''
output
======
count_get: 0
count_set: 0
count_set: 1
count_set: 2
count_set: 3
count_set: 4
count_del: 4
'''
속성을 동적으로 연결하는 유일한 방법은 새 클래스와 새 속성을 사용하여 해당 인스턴스를 만드는 것입니다.
class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)
제공된 답변의 대부분은 속성별로 매우 많은 행이 필요합니다. 즉, 여러 속성에 필요한 반복성 등으로 인해 추악하거나 지루한 구현이라고 생각합니다.저는 더 이상 단순화할 수 없거나 그렇게 하는 데 큰 도움이 되지 않을 때까지 끓이는 것을 줄이고 단순화하는 것을 선호합니다.
요컨대, 완성된 작품에서는, 2행의 코드를 반복하면, 통상, 1행의 헬퍼 함수로 변환하는 등...(start_x, start_y, end_x, end_y 등)에서 (x, y, w, h) ie x, y, x + w, y + h (때로는 min / max가 필요하거나 w/h가 음수이고 구현이 마음에 들지 않으면 x/y 및 abs w/h 등에서 뺄게요.).
내부 getters/setters를 덮어쓰는 것은 괜찮은 방법이지만, 문제는 모든 클래스에 대해 덮어쓰거나 해당 기반으로 클래스를 부모로 만들어야 한다는 것입니다.상속 대상 자녀/부모, 자녀 노드 등을 자유롭게 선택하고 싶기 때문에 이 방법은 사용할 수 없습니다.
데이터 입력 등이 귀찮기 때문에, 데이터 입력에 필요한 데이터 타입을 사용하지 않고 질문에 대답할 수 있는 솔루션을 작성했습니다.
이 솔루션에서는 클래스 위에 2줄을 추가하여 속성을 추가할 클래스의 기본 클래스를 만듭니다.다음으로 1줄마다 1줄씩 콜백을 추가하여 데이터를 제어하거나 데이터가 변경되었을 때 통지하거나 값 및/또는 데이터 유형에 따라 설정할 수 있는 데이터를 제한할 수 있습니다.
_object.x, _object.x = 값, _object를 사용할 수도 있습니다.GetX(), _object.SetX(value)와 이러한 값은 동등하게 처리됩니다.
또한 값은 클래스 인스턴스에 할당되어 있는 유일한 비정적 데이터이지만 실제 속성은 클래스에 할당되어 반복하고 싶지 않은 것, 반복할 필요가 없는 것을 의미합니다.기본값을 덮어쓰는 옵션이 있지만 매번 getter에 필요하지 않도록 기본값을 할당할 수 있습니다.또한 기본값을 덮어쓰고 저장된 원시값을 getter가 반환하는 다른 옵션이 있습니다(주의: 이 메서드는 값이 할당될 때만 원시값이 할당됨을 의미하며 그렇지 않으면 None - whe가 됩니다).n 값이 Reset이면 None 등이 할당됩니다.)
도우미 함수도 많이 있습니다.처음 추가된 속성은 인스턴스 값을 참조하기 위해 클래스에 도우미를 2개 정도 추가합니다.ResetAccessors ( _key , .. ) varargs repeated ( 첫 번째 이름 있는args )및 SetAccessors ( _key , _value )입니다.효율성을 높이기 위해 메인클래스에 추가되는 옵션이 있습니다.이러한 옵션에는 접근자를 그룹화하는 방법이 있습니다.따라서 매번 리셋할 수 있습니다.그룹화 및 리셋은 매번 지정된 키를 반복하지 않고 이루어집니다.
instance / raw stored 값은 class., __class에 저장됩니다.는 속성의 정적 변수/값/함수를 유지하는 액세스자 클래스를 참조합니다._class.설정/가져오기 등 중에 인스턴스 클래스를 통해 액세스할 때 호출되는 속성입니다.
접근자 _class.__는 클래스를 가리키지만 내부이므로 클래스에서 할당해야 합니다.따라서 __Name = AccessorFunc(...)를 사용하여 속성별로 한 줄씩 할당하고 다수의 선택적 인수를 사용합니다(키 변수를 사용하면 식별 및 유지보수가 더 쉽고 효율적이기 때문에).
또, 전술한 바와 같이, 액세스 기능 정보를 사용하고 있기 때문에, 호출할 필요가 없는 기능도 다수 준비하고 있습니다(현재는 조금 불편하기 때문에, _class를 사용할 필요가 있습니다.FunctionName ( _class_instance , args ) : 이 비트마라톤을 실행하는 함수를 추가하거나 오브젝트에 접근자를 추가하고 self(인스턴스용임을 지적하기 위해 이 이름을 붙임)를 사용하여 값을 취득하기 위해 스택/트레이스를 사용했습니다.Func 클래스 참조 및 함수 정의 내의 기타 정보.
아직 완성되지는 않았지만 환상적인 발판이다.주의: __Name = AccessorFunc(...)를 사용하여 속성을 생성하지 않으면 init 함수 내에서 키를 정의해도 __ 키에 액세스할 수 없습니다.그렇게 하면 문제 없습니다.
또한 이름과 키가 다르다는 점에 유의하십시오.이름은 Function Name Creation에서 사용되는 'formal'로, 키는 데이터 저장 및 액세스용입니다.즉 _class.x는 소문자x가 키이고 이름은 대문자X이므로 GetX()가 함수가 됩니다.이 함수는 약간 이상하게 보입니다.이것에 의해, self.x 가 동작해 적절한 것처럼 보이는 것과 동시에, GetX() 가 동작해 적절한 것처럼 보이는 것도 가능하게 됩니다.
동일한 키/이름과 다른 이름으로 설정된 샘플 클래스가 있습니다.데이터를 출력하기 위해 작성된 도우미 함수가 많습니다(주의:이 모든 것이 완전한 것은 아니기 때문에 무슨 일이 일어나고 있는지 알 수 있습니다.
키: x, name: X를 사용한 현재 함수 목록은 다음과 같이 출력됩니다.
이것은 결코 포괄적인 리스트가 아닙니다.게시 시점에서는 이 리스트에 기재되지 않은 것이 몇 가지 있습니다.
_instance.SetAccessors( _key, _value [ , _key, _value ] .. ) Instance Class Helper Function: Allows assigning many keys / values on a single line - useful for initial setup, or to minimize lines. In short: Calls this.Set<Name>( _value ) for each _key / _value pairing.
_instance.ResetAccessors( _key [ , _key ] .. ) Instance Class Helper Function: Allows resetting many key stored values to None on a single line. In short: Calls this.Reset<Name>() for each name provided.
Note: Functions below may list self.Get / Set / Name( _args ) - self is meant as the class instance reference in the cases below - coded as this in AccessorFuncBase Class.
this.GetX( _default_override = None, _ignore_defaults = False ) GET: Returns IF ISSET: STORED_VALUE .. IF IGNORE_DEFAULTS: None .. IF PROVIDED: DEFAULT_OVERRIDE ELSE: DEFAULT_VALUE 100
this.GetXRaw( ) RAW: Returns STORED_VALUE 100
this.IsXSet( ) ISSET: Returns ( STORED_VALUE != None ) True
this.GetXToString( ) GETSTR: Returns str( GET ) 100
this.GetXLen( _default_override = None, _ignore_defaults = False ) LEN: Returns len( GET ) 3
this.GetXLenToString( _default_override = None, _ignore_defaults = False ) LENSTR: Returns str( len( GET ) ) 3
this.GetXDefaultValue( ) DEFAULT: Returns DEFAULT_VALUE 1111
this.GetXAccessor( ) ACCESSOR: Returns ACCESSOR_REF ( self.__<key> ) [ AccessorFuncBase ] Key: x : Class ID: 2231452344344 : self ID: 2231448283848 Default: 1111 Allowed Types: {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"} Allowed Values: None
this.GetXAllowedTypes( ) ALLOWED_TYPES: Returns Allowed Data-Types {"<class 'int'>": "<class 'type'>", "<class 'float'>": "<class 'type'>"}
this.GetXAllowedValues( ) ALLOWED_VALUES: Returns Allowed Values None
this.GetXHelpers( ) HELPERS: Returns Helper Functions String List - ie what you're reading now... THESE ROWS OF TEXT
this.GetXKeyOutput( ) Returns information about this Name / Key ROWS OF TEXT
this.GetXGetterOutput( ) Returns information about this Name / Key ROWS OF TEXT
this.SetX( _value ) SET: STORED_VALUE Setter - ie Redirect to __<Key>.Set N / A
this.ResetX( ) RESET: Resets STORED_VALUE to None N / A
this.HasXGetterPrefix( ) Returns Whether or Not this key has a Getter Prefix... True
this.GetXGetterPrefix( ) Returns Getter Prefix... Get
this.GetXName( ) Returns Accessor Name - Typically Formal / Title-Case X
this.GetXKey( ) Returns Accessor Property Key - Typically Lower-Case x
this.GetXAccessorKey( ) Returns Accessor Key - This is to access internal functions, and static data... __x
this.GetXDataKey( ) Returns Accessor Data-Storage Key - This is the location where the class instance value is stored.. _x
출력되는 데이터의 일부는 다음과 같습니다.
이것은 Demo 클래스를 사용하여 만든 완전히 새로운 클래스로, 이름(출력 가능) 이외의 데이터는 할당되지 않았습니다.이것은 제가 사용한 변수 이름인 _foo입니다.
_foo --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016
Key Getter Value | Raw Key Raw / Stored Value | Get Default Value Default Value | Get Allowed Types Allowed Types | Get Allowed Values Allowed Values |
Name: _foo | _Name: _foo | __Name.DefaultValue( ): AccessorFuncDemoClass | __Name.GetAllowedTypes( ) <class 'str'> | __Name.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
x: 1111 | _x: None | __x.DefaultValue( ): 1111 | __x.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __x.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
y: 2222 | _y: None | __y.DefaultValue( ): 2222 | __y.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __y.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
z: 3333 | _z: None | __z.DefaultValue( ): 3333 | __z.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __z.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Blah: <class 'int'> | _Blah: None | __Blah.DefaultValue( ): <class 'int'> | __Blah.GetAllowedTypes( ) <class 'str'> | __Blah.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Width: 1 | _Width: None | __Width.DefaultValue( ): 1 | __Width.GetAllowedTypes( ) (<class 'int'>, <class 'bool'>) | __Width.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Height: 0 | _Height: None | __Height.DefaultValue( ): 0 | __Height.GetAllowedTypes( ) <class 'int'> | __Height.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
Depth: 2 | _Depth: None | __Depth.DefaultValue( ): 2 | __Depth.GetAllowedTypes( ) Saved Value Restricted to Authorized Values ONLY | __Depth.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
this.IsNameSet( ): True this.GetName( ): _foo this.GetNameRaw( ): _foo this.GetNameDefaultValue( ): AccessorFuncDemoClass this.GetNameLen( ): 4 this.HasNameGetterPrefix( ): <class 'str'> this.GetNameGetterPrefix( ): None
this.IsXSet( ): False this.GetX( ): 1111 this.GetXRaw( ): None this.GetXDefaultValue( ): 1111 this.GetXLen( ): 4 this.HasXGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetXGetterPrefix( ): None
this.IsYSet( ): False this.GetY( ): 2222 this.GetYRaw( ): None this.GetYDefaultValue( ): 2222 this.GetYLen( ): 4 this.HasYGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetYGetterPrefix( ): None
this.IsZSet( ): False this.GetZ( ): 3333 this.GetZRaw( ): None this.GetZDefaultValue( ): 3333 this.GetZLen( ): 4 this.HasZGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetZGetterPrefix( ): None
this.IsBlahSet( ): False this.GetBlah( ): <class 'int'> this.GetBlahRaw( ): None this.GetBlahDefaultValue( ): <class 'int'> this.GetBlahLen( ): 13 this.HasBlahGetterPrefix( ): <class 'str'> this.GetBlahGetterPrefix( ): None
this.IsWidthSet( ): False this.GetWidth( ): 1 this.GetWidthRaw( ): None this.GetWidthDefaultValue( ): 1 this.GetWidthLen( ): 1 this.HasWidthGetterPrefix( ): (<class 'int'>, <class 'bool'>) this.GetWidthGetterPrefix( ): None
this.IsDepthSet( ): False this.GetDepth( ): 2 this.GetDepthRaw( ): None this.GetDepthDefaultValue( ): 2 this.GetDepthLen( ): 1 this.HasDepthGetterPrefix( ): None this.GetDepthGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ): False this.GetHeight( ): 0 this.GetHeightRaw( ): None this.GetHeightDefaultValue( ): 0 this.GetHeightLen( ): 1 this.HasHeightGetterPrefix( ): <class 'int'> this.GetHeightGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
이것은 모든 _foo 속성(이름 제외)에 같은 순서로 'string', 1.0, True, 9, 10, False 값을 할당한 후입니다.
this.IsNameSet( ): True this.GetName( ): _foo this.GetNameRaw( ): _foo this.GetNameDefaultValue( ): AccessorFuncDemoClass this.GetNameLen( ): 4 this.HasNameGetterPrefix( ): <class 'str'> this.GetNameGetterPrefix( ): None
this.IsXSet( ): True this.GetX( ): 10 this.GetXRaw( ): 10 this.GetXDefaultValue( ): 1111 this.GetXLen( ): 2 this.HasXGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetXGetterPrefix( ): None
this.IsYSet( ): True this.GetY( ): 10 this.GetYRaw( ): 10 this.GetYDefaultValue( ): 2222 this.GetYLen( ): 2 this.HasYGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetYGetterPrefix( ): None
this.IsZSet( ): True this.GetZ( ): 10 this.GetZRaw( ): 10 this.GetZDefaultValue( ): 3333 this.GetZLen( ): 2 this.HasZGetterPrefix( ): (<class 'int'>, <class 'float'>) this.GetZGetterPrefix( ): None
this.IsBlahSet( ): True this.GetBlah( ): string Blah this.GetBlahRaw( ): string Blah this.GetBlahDefaultValue( ): <class 'int'> this.GetBlahLen( ): 11 this.HasBlahGetterPrefix( ): <class 'str'> this.GetBlahGetterPrefix( ): None
this.IsWidthSet( ): True this.GetWidth( ): False this.GetWidthRaw( ): False this.GetWidthDefaultValue( ): 1 this.GetWidthLen( ): 5 this.HasWidthGetterPrefix( ): (<class 'int'>, <class 'bool'>) this.GetWidthGetterPrefix( ): None
this.IsDepthSet( ): True this.GetDepth( ): 9 this.GetDepthRaw( ): 9 this.GetDepthDefaultValue( ): 2 this.GetDepthLen( ): 1 this.HasDepthGetterPrefix( ): None this.GetDepthGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
this.IsHeightSet( ): True this.GetHeight( ): 9 this.GetHeightRaw( ): 9 this.GetHeightDefaultValue( ): 0 this.GetHeightLen( ): 1 this.HasHeightGetterPrefix( ): <class 'int'> this.GetHeightGetterPrefix( ): (0, 1, 2, 3, 4, 5, 6, 7, 8, 9)
_foo --- MyClass: ---- id( this.__class__ ): 2231452349064 :::: id( this ): 2231448475016
Key Getter Value | Raw Key Raw / Stored Value | Get Default Value Default Value | Get Allowed Types Allowed Types | Get Allowed Values Allowed Values |
Name: _foo | _Name: _foo | __Name.DefaultValue( ): AccessorFuncDemoClass | __Name.GetAllowedTypes( ) <class 'str'> | __Name.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
x: 10 | _x: 10 | __x.DefaultValue( ): 1111 | __x.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __x.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
y: 10 | _y: 10 | __y.DefaultValue( ): 2222 | __y.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __y.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
z: 10 | _z: 10 | __z.DefaultValue( ): 3333 | __z.GetAllowedTypes( ) (<class 'int'>, <class 'float'>) | __z.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Blah: string Blah | _Blah: string Blah | __Blah.DefaultValue( ): <class 'int'> | __Blah.GetAllowedTypes( ) <class 'str'> | __Blah.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Width: False | _Width: False | __Width.DefaultValue( ): 1 | __Width.GetAllowedTypes( ) (<class 'int'>, <class 'bool'>) | __Width.GetAllowedValues( ) Saved Value Restrictions Levied by Data-Type |
Height: 9 | _Height: 9 | __Height.DefaultValue( ): 0 | __Height.GetAllowedTypes( ) <class 'int'> | __Height.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
Depth: 9 | _Depth: 9 | __Depth.DefaultValue( ): 2 | __Depth.GetAllowedTypes( ) Saved Value Restricted to Authorized Values ONLY | __Depth.GetAllowedValues( ) (0, 1, 2, 3, 4, 5, 6, 7, 8, 9) |
제한된 데이터 유형 또는 값 제한으로 인해 일부 데이터가 할당되지 않았습니다. 이는 설계상입니다.setter는 잘못된 데이터 유형 또는 값을 할당하는 것을 금지합니다(기본값 보호 동작을 덮어쓰지 않는 한).
예시와 설명 후에 공간이 없어서 여기에 코드가 게시되지 않았습니다.또한 바뀔 것이기 때문이다.
주의: 이 투고 시점에서는 파일이 지저분합니다.변경됩니다.그러나 Sublime Text에서 실행해 컴파일하거나 Python에서 실행하면 컴파일되어 대량의 정보가 출력됩니다.- AccessorDB 부분이 완료되지 않았습니다(Print Getters 및 GetKeyOutput 도우미 기능을 업데이트하고 인스턴스 함수로 변경되며 단일 함수로 전환되고 이름이 변경될 수 있음). 검색하십시오.)
다음: 실행하는데 모든 것이 필요한 것은 아닙니다.아래쪽에 있는 코멘트의 대부분은 디버깅에 사용되는 상세 정보에 대한 것입니다.다운로드 시 표시되지 않을 수 있습니다.이 경우 추가 정보를 얻기 위해 코멘트를 해제하고 다시 컴파일할 수 있습니다.
MyClassBase: pass, MyClass(MyClassBase): ...솔루션을 알고 있다면 투고해 주세요.
클래스에서 필요한 것은 __ 행뿐입니다.str은 init과 마찬가지로 디버깅을 위한 것입니다.이것들은 데모 클래스에서 삭제할 수 있지만, 아래의 행 중 일부를 코멘트 아웃 또는 삭제해야 합니다(_foo / 2 / 3 ).
맨 위에 있는 String, Dict 및 Util 클래스는 내 Python 라이브러리의 일부이지만 완전하지는 않습니다.도서관에서 필요한 것을 몇 개 복사해서 몇 개 새로 만들었어요.완전한 코드가 라이브러리 전체에 링크되어 갱신된 콜 제공 및 코드 삭제와 함께 포함됩니다(실제로 남은 코드는 데모 클래스와 인쇄문뿐입니다-액세서).Func 시스템이 라이브러리로 이동합니다.)...
파일의 일부:
##
## MyClass Test AccessorFunc Implementation for Dynamic 1-line Parameters
##
class AccessorFuncDemoClassBase( ):
pass
class AccessorFuncDemoClass( AccessorFuncDemoClassBase ):
__Name = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Name', default = 'AccessorFuncDemoClass', allowed_types = ( TYPE_STRING ), allowed_values = VALUE_ANY, documentation = 'Name Docs', getter_prefix = 'Get', key = 'Name', allow_erroneous_default = False, options = { } )
__x = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'X', default = 1111, allowed_types = ( TYPE_INTEGER, TYPE_FLOAT ), allowed_values = VALUE_ANY, documentation = 'X Docs', getter_prefix = 'Get', key = 'x', allow_erroneous_default = False, options = { } )
__Height = AccessorFuncBase( parent = AccessorFuncDemoClassBase, name = 'Height', default = 0, allowed_types = TYPE_INTEGER, allowed_values = VALUE_SINGLE_DIGITS, documentation = 'Height Docs', getter_prefix = 'Get', key = 'Height', allow_erroneous_default = False, options = { } )
이 아름다움에 의해 Accessor를 사용하여 동적으로 추가된 속성을 사용하여 새로운 클래스를 쉽게 만들 수 있습니다.펑크 / 콜백 / 데이터 유형 / 가치 적용 등
현시점에서는 이 링크는 (이 링크는 문서의 변경을 반영합니다.): https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0
또한 Sublime Text를 사용하지 않는 경우 Notepad+, Atom, Visual Code 등의 적절한 스레드 구현을 통해 훨씬 더 빠르게 사용할 수 있으므로 추천합니다.IDE와 같은 코드 매핑 시스템도 준비하고 있습니다.https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/ (패키지 매니저에서 먼저 Repo를 추가하고 플러그인 설치 - 버전 1.0.0이 준비되면 메인 플러그인 목록에 추가합니다.))
이 솔루션이 도움이 되었으면 합니다.그리고 항상 그렇듯이:
단지 효과가 있다고 해서 그것이 올바르게 되는 것은 아니다 - Josh 'Acecool' Moser
언급URL : https://stackoverflow.com/questions/1325673/how-to-add-property-to-a-class-dynamically
'programing' 카테고리의 다른 글
어레이가 비어 있는 경우 Forech를 건너뛸 수 있는 가장 깨끗한 방법 (0) | 2022.12.07 |
---|---|
CodeIgniter 인증 라이브러리를 선택하려면 어떻게 해야 합니까? (0) | 2022.12.07 |
서블릿 필터를 사용하여 요청 매개 변수 수정 (0) | 2022.12.07 |
MySQL과 Maria의 JSON_SEARCH 차이DB (0) | 2022.12.07 |
php 랜덤x 자리수 (0) | 2022.12.07 |