Programing

클래스에 동적으로 속성을 추가하는 방법은 무엇입니까?

lottogame 2020. 5. 14. 07:55
반응형

클래스에 동적으로 속성을 추가하는 방법은 무엇입니까?


목표는 DB 결과 집합처럼 동작하는 모의 클래스를 만드는 것입니다.

예를 들어 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])것은 전혀 쓸모 가 없습니다.

런타임에 인스턴스 속성을 만드는 올바른 방법은 무엇입니까?

추신 : 나는 방법이 어떻게 사용됩니까?에 제시된 대안을 알고 있습니다 .__getattribute__


나는이 대답을 확장해야한다고 생각합니다. 이제 나이가 많고 현명하고 진행 상황을 알고 있습니다. 안하는 것보다 늦게하는 것이 낫다.

당신은 할 수 클래스 동적에 속성을 추가 할 수 있습니다. 그러나 그것은 캐치입니다. 클래스 에 추가해야합니다 .

>>> class Foo(object):
...     pass
... 
>>> foo = Foo()
>>> foo.a = 3
>>> Foo.b = property(lambda self: self.a + 1)
>>> foo.b
4

A property는 실제로 descriptor 라고하는 것의 간단한 구현입니다 . 주어진 클래스에서 주어진 속성에 대한 사용자 정의 처리를 제공하는 객체입니다 . if나무를 제거 하는 방법을 좋아합니다 __getattribute__.

내가 부탁하면 foo.b위의 예에서, 파이썬은 있다고 본다 b클래스 구현에 정의 된 기술자 프로토콜 단지가와 객체의 의미 - 어떤 __get__, __set__또는 __delete__방법을. 설명자는 해당 속성을 처리 할 책임이 있다고 주장하므로 Python이 호출 Foo.b.__get__(foo, Foo)하고 반환 값이 속성 값으로 다시 전달됩니다. 의 경우 property, 이러한 각각의 방법은 바로 호출 fget, fset또는 fdel당신은 전달 property생성자입니다.

설명자는 실제로 전체 OO 구현의 배관을 노출시키는 Python의 방법입니다. 사실,보다 일반적인 다른 유형의 설명자가 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__첫 번째 인수로 호출 인스턴스 침; 사실상, 이렇게합니다 :

def __get__(self, instance, owner):
    return functools.partial(self.function, instance)

어쨌든, 이것이 디스크립터가 클래스에서만 작동하는 이유라고 생각합니다. 처음에는 클래스를 강화하는 것들의 형식화입니다. 그것들은 규칙에 대한 예외이기도합니다 type. 분명히 기술자를 클래스에 할당 할 수 있으며 클래스 자체는 인스턴스입니다 ! 실제로, Foo.b여전히 전화 를 읽으려고 노력 property.__get__; 클래스 속성으로 액세스 할 때 설명자가 자신을 반환하는 것은 관용적입니다.

거의 모든 파이썬의 OO 시스템이 파이썬으로 표현 될 수 있다는 것은 멋진 일이라고 생각합니다. :)

아, 그리고 당신이 관심이 있다면 설명자관한 간단한 블로그 게시물을 작성했습니다 .


목표는 DB 결과 집합처럼 동작하는 모의 클래스를 만드는 것입니다.

그래서 당신이 원하는 것은 a [ 'b']를 ab로 철자 할 수있는 사전입니까?

쉽습니다.

class atdict(dict):
    __getattr__= dict.__getitem__
    __setattr__= dict.__setitem__
    __delattr__= dict.__delitem__

namedtuple필드의 전체 목록을 미리 알고 있기 때문에이 문제를 훨씬 더 간단하게 해결할 수있는 것 같습니다 .

from collections import namedtuple

Foo = namedtuple('Foo', ['bar', 'quux'])

foo = Foo(bar=13, quux=74)
print foo.bar, foo.quux

foo2 = Foo()  # error

자신 만의 세터를 작성해야하는 경우 클래스 수준에서 메타 프로그래밍을 수행해야합니다. property()인스턴스에서는 작동하지 않습니다.


이를 위해 속성을 사용할 필요는 없습니다. __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!

파이썬 클래스에 동적으로 속성을 추가하는 방법은 무엇입니까?

속성을 추가하려는 객체가 있다고 가정하십시오. 일반적으로 일관된 API를 유지할 수 있도록 다운 스트림 사용 코드의 속성에 대한 액세스 관리를 시작해야 할 때 속성을 사용하고 싶습니다. 이제는 일반적으로 객체가 정의 된 소스 코드에 추가하지만 액세스 권한이 없거나 프로그래밍 방식으로 함수를 동적으로 선택해야한다고 가정 해 보겠습니다.

수업 만들기

에 대한 문서를property 기반으로 한 예제를 사용하여 "hidden"속성을 가진 객체 클래스를 만들고 인스턴스를 만들어 봅시다.

class C(object):
    '''basic class'''
    _x = None

o = C()

파이썬에서는 일을하는 확실한 방법이있을 것으로 기대합니다. 그러나이 경우 데코레이터 표기법과없는 두 가지 방법을 보여 드리겠습니다. 먼저 데코레이터 표기법이 없습니다. 게터, 세터 또는 삭제 기의 동적 할당에 더 유용 할 수 있습니다.

동적 (일명 원숭이 패치)

우리 수업을 위해 몇 가지를 만들어 봅시다 :

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

그리고 프로비저닝 된 setter 및 deleter가있는 특성 오브젝트를 클래스에 지정하십시오.

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

이 스택 오버플로 게시물에 비슷한 질문을 하여 간단한 유형을 만든 클래스 팩토리를 만들었습니다. 그 결과 클래스 팩토리의 작동 버전이있는 이 답변 이었습니다 . 다음은 답변의 스 니펫입니다.

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__인스턴스에서 데이터 디스크립터를 처리하려면 새 클래스를 동적으로 작성하거나 과부하해야합니다 .


질문을 완전히 이해했는지 확실하지 않지만 런타임에 내장 __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

검색 엔진에서 오는 사람들을 위해 동적 속성 에 대해 이야기 할 때 찾고 있던 두 가지가 있습니다.

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 () 내장 함수를 살펴보십시오 .


달성하는 가장 좋은 방법은을 정의하는 것 __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

그 인쇄 12

    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

다음 코드를 사용하여 사전 객체를 사용하여 클래스 속성을 업데이트 할 수 있습니다.

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

해결책은 다음과 같습니다.

  • 특성 이름을 string으로 지정할 수 있으므로 프로그램에 모두 나열되지 않고 외부 데이터 소스에서 가져올 수 있습니다.
  • 객체가 생성 될 때마다 대신 클래스가 정의 될 때 속성을 추가합니다 .

클래스가 정의 된 후에는 속성을 동적으로 추가하기 위해이 작업을 수행합니다.

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 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 식을 사용하여 데이터베이스 쿼리가 반환하는 경우 {'ab':100, 'cd':200}, 다음 나는 것 확인합니다

>>> dummy.ab
100

모듈 namedtuple에서 from 을 사용 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>

많은 답변이 제공되었지만 만족스러운 답변을 찾지 못했습니다. 나는 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 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

이것은 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

혼란 스럽습니까? 그렇습니다. 유의 한 실제 사례를 제시 할 수 없었습니다. 또한 이것은 가벼운 마음을위한 것이 아닙니다.


속성을 동적으로 연결하는 유일한 방법은 새 속성으로 새 클래스와 해당 인스턴스를 만드는 것입니다.

class Holder: p = property(lambda x: vs[i], self.fn_readonly)
setattr(self, k, Holder().p)

내가 최근에 비슷한 문제로 실행, 내가 사용 내놓았다하는 솔루션 __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

제공된 답변을 많이 얻으려면 속성 당 너무 많은 줄이 필요합니다. 즉, / 및 / 또는-여러 속성에 필요한 반복성으로 인해 추악하거나 지루한 구현으로 간주되는 것. 끓는 것을 줄이거 나 단순화 할 때까지 선호합니다. 더 이상 단순화 할 수 없거나 그렇게 할 목적이 없을 때까지

간단히 말하면, 완성 된 작품에서 2 줄의 코드를 반복하면 일반적으로 한 줄 도우미 함수로 변환하는 등 ... (start_x, start_y, end_x, end_y)와 같은 수학 또는 홀수 인수를 단순화합니다. (x, y, w, h) 즉, x, y, x + w, y + h (때로는 min / max가 필요하거나 w / h가 음수이고 구현이 마음에 들지 않으면 x에서 빼겠습니다. y 및 abs w / h 등.).

내부 게터 / 세터를 재정의하는 것은 괜찮은 방법이지만 문제는 모든 클래스에 대해 그렇게해야하거나 클래스를 해당베이스에 부모로 지정해야한다는 것입니다 ... 이것이 나에게 바람직하지 않습니다. 상속, 자식 노드 등을 위해 어린이 / 부모를 자유롭게 선택할 수 있습니다.

Dict 데이터 유형을 사용하여 데이터를 입력하지 않고 지루한 데이터를 제공하지 않고 질문에 대답하는 솔루션을 만들었습니다.

내 솔루션을 사용하려면 클래스 위에 2 줄을 추가하여 속성을 추가하려는 클래스의 기본 클래스를 만든 다음 한 줄당 1 개를 추가해야하며 콜백을 추가하여 데이터를 제어하고 데이터 변경 시점을 알려주는 옵션이 있습니다 값 및 / 또는 데이터 유형 등을 기반으로 설정할 수있는 데이터를 제한하십시오.

_object.x, _object.x = value, _object.GetX (), _object.SetX (value)를 사용하는 옵션도 있으며 동등하게 처리됩니다.

또한 값은 클래스 인스턴스에 할당 된 비 정적 데이터 만 유일하지만 실제 속성은 클래스에 할당되어 반복하고 싶지 않으며 반복 할 필요가없는 것을 의미합니다. 기본 기본값을 재정의하는 옵션이 있지만 getter가 매번 필요하지 않도록 기본값을 할당 할 수 있으며 다른 옵션이 있으므로 getter가 기본 반환 값을 재정 의하여 원시 저장된 값을 반환합니다 (참고 :이 방법 값이 할당 될 때만 원시 값이 할당되고 그렇지 않으면 None입니다. 값이 Reset 인 경우 None 등이 할당됩니다.)

많은 도우미 함수도 있습니다-추가되는 첫 번째 속성은 인스턴스 값을 참조하기 위해 클래스에 도우미를 2 개 정도 추가합니다 ... 이들은 ResetAccessors (_key, ..) varargs입니다 (모두 첫 번째 명명 된 인수를 사용하여 반복 할 수 있음) ) 및 SetAccessors (_key, _value)는 효율성을 높이기 위해 메인 클래스에 추가되는 옵션이 있습니다. 계획된 방법은 다음과 같습니다. 접근자를 그룹화하는 방법입니다. 따라서 매번 몇 번 재설정하는 경향이 있습니다. , 매번 이름 지정된 키를 반복하는 대신 그룹에 할당하고 그룹을 재설정 할 수 있습니다.

인스턴스 / 원시 저장 값은 클래스에 저장됩니다 . , 클래스. 속성에 대한 정적 변수 / 값 / 함수를 보유하는 접근 자 클래스를 참조합니다. _수업. 설정 / 가져 오기 등의 동안 인스턴스 클래스를 통해 액세스 할 때 호출되는 속성 자체입니다.

접근 자 _class .__은 클래스를 가리 키지 만 내부적으로 클래스에 할당해야하기 때문에 __Name = AccessorFunc (...)를 사용하여 할당합니다. 사용할 인수 (키워드 된 varargs를 사용하면 더 쉽고 효율적으로 식별하고 유지 관리 할 수 ​​있음) ...

또한 언급 한 것처럼 많은 함수를 작성합니다. 일부 함수접근 자 함수 정보 를 사용 하므로 호출 할 필요가 없습니다 (지금은 약간 불편하므로 _class를 사용해야합니다. .FunctionName (_class_instance , args)-스택 / 추적을 사용 하여이 비트 마라톤을 실행하는 함수를 추가하거나 객체에 접근자를 추가하고 self를 사용하여 값을 가져 오기 위해 인스턴스 참조를 가져옵니다. '인스턴스를위한 것이며 함수 정의 내에서 self, AccessorFunc 클래스 참조 및 기타 정보에 대한 액세스 권한을 유지해야합니다.

아직 완료되지는 않았지만 환상적인 발판입니다. 참고 : 속성을 만드는 데 __Name = AccessorFunc (...)를 사용하지 않으면 init 함수 내에서 __ 키를 정의하더라도 __ 키에 액세스 할 수 없습니다. 그렇게하면 아무런 문제가 없습니다.

또한 : 이름과 키가 다릅니다. 이름은 기능 이름 작성에 사용되는 '공식'이며 키는 데이터 저장 및 액세스를위한 것입니다. 즉, 소문자 x가 키인 _class.x, 이름은 대문자 X가되므로 GetX ()가 Getx () 대신 함수가되어 조금 이상해 보입니다. 이를 통해 self.x가 작동하고 적절하게 보일 수 있지만 GetX ()도 적절하게 보일 수 있습니다.

키 / 이름이 동일하고 표시하는 것과 다른 예제 클래스가 있습니다. 데이터를 출력하기 위해 많은 도우미 함수가 생성되므로 (참고 :이 작업이 모두 완료된 것은 아님) 진행 상황을 확인할 수 있습니다.

key : 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)

그리고 이것은 'string', 1.0, True, 9, 10, False와 같은 순서로 모든 _foo 속성 (name 제외)에 다음 값을 할당 한 후입니다.

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)                                                                                                                                                                                                   |

제한된 데이터 유형 또는 값 제한으로 인해 일부 데이터가 할당되지 않았습니다. 이것은 의도적으로 설계된 것입니다. 세터는 잘못된 데이터 유형 또는 값이 기본값으로 지정되지 않은 경우 (기본값 보호 동작을 무시하지 않는 한) 금지합니다.

예제와 설명 뒤에 공간이 없었기 때문에 코드가 게시되지 않았습니다 ... 또한 변경 될 것입니다.

참고 :이 게시물을 게시 할 때 파일이 지저분합니다-변경됩니다. 그러나 Sublime Text에서 실행하고 컴파일하거나 Python에서 실행하면 많은 정보를 컴파일하고 뱉어냅니다 .AccessorDB 부분은 수행되지 않습니다 (인쇄 Getter 및 GetKeyOutput 도우미를 업데이트하는 데 사용됨) 함수를 인스턴스 함수로 변경하고 단일 함수에 넣고 이름을 바꿉니다.)

다음 : 모든 것이 실행되는 데 필요한 것은 아닙니다. 하단에 주석이 달린 많은 부분은 디버깅에 사용되는 자세한 정보를위한 것입니다. 다운로드 할 때 없을 수도 있습니다. 그렇다면 주석 처리를 해제하고 다시 컴파일하여 자세한 정보를 얻을 수 있어야합니다.

MyClassBase : pass, MyClass (MyClassBase) : ...-해결책을 알고 있다면 게시하십시오.

클래스에 필요한 유일한 것은 __ 줄입니다. str초기화 상태 그대로 디버깅을위한 것입니다. 데모 클래스에서 제거 할 수 있지만 아래 줄을 주석 처리하거나 제거해야합니다 (_foo / 2 / 3 ) ..

맨 위에있는 String, Dict 및 Util 클래스는 내 Python 라이브러리의 일부입니다. 완료되지 않았습니다. 도서관에서 필요한 몇 가지를 복사하고 몇 가지 새로운 것을 만들었습니다. 전체 코드는 전체 라이브러리에 연결되며 업데이트 된 호출 제공 및 코드 제거와 함께 포함됩니다 (실제로 남은 코드는 데모 클래스 및 인쇄 명령문-AccessorFunc 시스템이 라이브러리로 이동 됨). ..

파일의 일부 :

##
## 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 = { } )

이러한 아름다움 덕분에 AccessorFuncs / 콜백 / 데이터 유형 / 값 적용 등을 통해 동적으로 추가 된 속성으로 새 클래스를 작성하는 것이 매우 쉽습니다.

현재 링크는 다음과 같습니다 (이 링크는 문서 변경 사항을 반영해야합니다.) : https://www.dropbox.com/s/6gzi44i7dh58v61/dynamic_properties_accessorfuncs_and_more.py?dl=0

또한 : Sublime Text를 사용하지 않으면 적절한 스레딩 구현으로 인해 메모장 ++, Atom, Visual Code 및 기타를 사용하는 것이 훨씬 더 빠르므로 사용하는 것이 좋습니다. 저는 IDE와 같은 코드로 작업하고 있습니다. 그것을 위해 매핑 시스템 - 한 번 봐 걸릴 : https://bitbucket.org/Acecool/acecoolcodemappingsystem/src/master/은 - 버전 1.0.0가 준비되면, 내가 추가 할 것 (다음 설치를 먼저 패키지 관리자에서 플러그인을 리포 추가 메인 플러그인 목록으로 ...)

이 솔루션이 도움이 되길 바랍니다.

Josh 'Acecool'Moser

참고 URL : https://stackoverflow.com/questions/1325673/how-to-add-property-to-a-class-dynamically

반응형