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

목표는 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)
>>>

이 변형을 사용하여 목표 인 기본값을 만들 수 있습니다 (이 질문 에이 문제를 해결하는 답변도 있습니다).


답변

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