IT

어디에서 가변 명명 된 튜플이 있습니까?

lottoking 2020. 8. 20. 19:09
반응형

어디에서 가변 명명 된 튜플이 있습니까?


누구나 namedtuple을 수정 하거나 변경 가능한 object-에 대해 작동하도록 대체 클래스를 제공 할 수 있습니까 ?

주로 가독성을 위해 다음을 수행하는 namedtuple을 포함하는 것을 원합니다.

from Camelot import namedgroup

Point = namedgroup('Point', ['x', 'y'])
p = Point(0, 0)
p.x = 10

>>> p
Point(x=10, y=0)

>>> p.x *= 10
Point(x=100, y=0)

결과물을 피클 할 수 있습니다. 그리고 명명 된 튜플의 특성에 따라 표현 될 때 출력의 순서는 배열을 구성 할 때 변수 목록의 순서와 일치해야합니다.


로 변경 가능한 대안이 collections.namedtuple- recordclass은 .

API와 메모리 공간이 동일 namedtuple하고 할당을 지원합니다 (더 빠를 것입니다). 예를 들면 :

from recordclass import recordclass

Point = recordclass('Point', 'x y')

>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)

Python 3.6 이상 recordclass(0.5 이후)의 경우 typehints가 지원됩니다.

from recordclass import recordclass, RecordClass

class Point(RecordClass):
   x: int
   y: int

>>> Point.__annotations__
{'x':int, 'y':int}
>>> p = Point(1, 2)
>>> p
Point(x=1, y=2)
>>> print(p.x, p.y)
1 2
>>> p.x += 2; p.y += 3; print(p)
Point(x=3, y=5)

더 완전한 예가 있습니다 (성능 비교도 포함됨).

0.9 이후 recordclass라이브러리는 recordclass.structclass공장 기능이라는 또 다른 변형을 제공 합니다. 인스턴스가 __slots__기반 인스턴스 생성보다 메모리를 덜 차지하는 클래스를 할 수 있습니다 . 이는 참조주기를 갖지 않는 속성 값이있는 인스턴스에 중요 할 수 있습니다. 수백만 개의 인스턴스를 하나의 경우 메모리 사용을 줄이는 데 도움이 될 수 있습니다. 다음은 예시적인 입니다.


SimpleNamespace 는 Python 3.3에서 채택 된 요구 사항을 지원합니다.

from types import SimpleNamespace
t = SimpleNamespace(foo='bar')
t.ham = 'spam'
print(t)
namespace(foo='bar', ham='spam')
print(t.foo)
'bar'
import pickle
with open('/tmp/pickle', 'wb') as f:
    pickle.dump(t, f)

이 질문에 대한 대답은 '아니오'인 것입니다.

아래는 매우 가깝지만 기술적으로 설명 수는 없습니다. namedtuple()업데이트 된 x 값으로 인스턴스를 만듭니다 .

Point = namedtuple('Point', ['x', 'y'])
p = Point(0, 0)
p = p._replace(x=10) 

반면에 __slots__클래스 인스턴스 속성을 자주 업데이트하는 데 잘 작동 하는 간단한 클래스를 만들 수 있습니다 .

class Point:
    __slots__ = ['x', 'y']
    def __init__(self, x, y):
        self.x = x
        self.y = y

이 대답에 추가 비용 많은 __slots__클래스 인스턴스를 만들 때 메모리이기 때문에 여기에서 좋은 사용 이라고 생각 합니다. 유일한 단점은 새 클래스 속성을 만들 수 있습니다.

다음은 메모리 효율성을 하나의 표현입니다 . 더 오래이며 그 이유는 무엇입니까?

이 내용의 답변에 인용 된 내용은 왜 __slots__메모리 효율성이 더 높은지 매우 간결한 설명 입니다 -Python 작성자


최신 namedlist 1.7은 2016 년 1 월 11 일로 Python 2.7 및 Python 3.5를 사용하여 모든 테스트를 통과합니다 . 순수한 Python 구현 이지만 recordclass은 C 확장입니다. 물론 C 확장이 선호 여부는 요구 사항에 따라 달라집니다.

테스트 (아래 참고 참조) :

from __future__ import print_function
import pickle
import sys
from namedlist import namedlist

Point = namedlist('Point', 'x y')
p = Point(x=1, y=2)

print('1. Mutation of field values')
p.x *= 10
p.y += 10
print('p: {}, {}\n'.format(p.x, p.y))

print('2. String')
print('p: {}\n'.format(p))

print('3. Representation')
print(repr(p), '\n')

print('4. Sizeof')
print('size of p:', sys.getsizeof(p), '\n')

print('5. Access by name of field')
print('p: {}, {}\n'.format(p.x, p.y))

print('6. Access by index')
print('p: {}, {}\n'.format(p[0], p[1]))

print('7. Iterative unpacking')
x, y = p
print('p: {}, {}\n'.format(x, y))

print('8. Iteration')
print('p: {}\n'.format([v for v in p]))

print('9. Ordered Dict')
print('p: {}\n'.format(p._asdict()))

print('10. Inplace replacement (update?)')
p._update(x=100, y=200)
print('p: {}\n'.format(p))

print('11. Pickle and Unpickle')
pickled = pickle.dumps(p)
unpickled = pickle.loads(pickled)
assert p == unpickled
print('Pickled successfully\n')

print('12. Fields\n')
print('p: {}\n'.format(p._fields))

print('13. Slots')
print('p: {}\n'.format(p.__slots__))

Python 2.7의 출력

1. 필드 값의 변형  
페이지 : 10, 12

2. 공유  
p : 점 (x = 10, y = 12)

3. 표현  
점 (x = 10, y = 12) 

4. 크기  
p의 크기 : 64 

5. 필드 명으로 접근  
페이지 : 10, 12

6. 강화에 의한 접근  
페이지 : 10, 12

7. 반복적 인 압축 풀기  
페이지 : 10, 12

8. 반복  
p : [10, 12]

9. 정렬 된 사전  
p : OrderedDict ([( 'x', 10), ( 'y', 12)])

10. 내부 교체 (업데이트?)  
p : 점 (x = 100, y = 200)

11. 피클 및 언 피클  
바보로 절임

12. 필드  
p : ( 'x', 'y')

13.  
p : ( 'x', 'y')

Python 3.5와의 유일한 차이점 namedlist은 크기가 더 작아지고 크기가 56이라는 것입니다 (Python 2.7 보고서 64).

내부 교체를 위해 테스트 10을 변경했습니다. namedlist_replace()얕은 복사를 수행하는 방법을하고 있기 때문에 그것은 나에게 완벽한 의미가 namedtuple표준 라이브러리 에서이 같은 방식으로 동작합니다. _replace()방법 의 의미를 변경하면 스러울 것입니다. 제 생각 에이 _update()방법은 내부 업데이트에 있습니다. 아니면 시험 10의 의도를 이해하지 않을까요?


이 작업에 대한 매우 파이썬 대안으로서, 파이썬 3.7 이후로, 또는 일반 클래스 정의를 사용하므로 다른 클래스 기능 현관도 계명 지원하기 때문에 dataclasses변경 가능한 것처럼 동작 하는 모듈을 사용할 수 NamedTuple있습니다.

PEP-0557에서 :

매우 다른 사용하지만 데이터 클래스는 ""이있는 변경 가능한 명명 된 튜플 "로 생각할 수 있습니다. 데이터 클래스는 일반 클래스 정의 구문을 사용하기 때문에 상속, 메타 클래스, 독염, 사용자 정의 메서드, 클래스 팩토리 및 기타 Python 클래스 기능을 자유롭게 사용할 수 있습니다.

PEP 526 , "Syntax for Variable Annotations"에 정의 된대로 유형 주석 이있는 변수에 대한 클래스 정의를 검사하는 클래스 데코레이터가 제공 됩니다. 이 문서에서는 해당 변수를 필드라고합니다. 데코레이터는 생성 된 메소드를 사용하여 생성 된 메소드 정의를 클래스에 추가 사양 하여 섹션에 설명 된대로 인스턴스 초기화, repr, 비교 메소드 및 선택적으로 기타 메소드를 지원합니다 . 해당 클래스를 데이터 클래스라고합니다. 그러나 클래스에 특별한 것은 없습니다. 데코레이터는 생성 된 메소드를 클래스에 추가하고 동일한 클래스를 반환합니다.

이 기능은 PEP-0557에 도입되었으며 제공된 문서-link에서 자세한 내용을 읽을 수 있습니다.

예 :

In [20]: from dataclasses import dataclass

In [21]: @dataclass
    ...: class InventoryItem:
    ...:     '''Class for keeping track of an item in inventory.'''
    ...:     name: str
    ...:     unit_price: float
    ...:     quantity_on_hand: int = 0
    ...: 
    ...:     def total_cost(self) -> float:
    ...:         return self.unit_price * self.quantity_on_hand
    ...:    

시험 :

In [23]: II = InventoryItem('bisc', 2000)

In [24]: II
Out[24]: InventoryItem(name='bisc', unit_price=2000, quantity_on_hand=0)

In [25]: II.name = 'choco'

In [26]: II.name
Out[26]: 'choco'

In [27]: 

In [27]: II.unit_price *= 3

In [28]: II.unit_price
Out[28]: 6000

In [29]: II
Out[29]: InventoryItem(name='choco', unit_price=6000, quantity_on_hand=0)

다음은 Python 3을위한 좋은 솔루션입니다 . 기본 클래스 __slots__Sequence추상을 사용하는 최소 클래스입니다. 멋진 오류 감지 등을 수행하지 않는 작동하며 대부분 가변 튜플처럼 작동합니다 (유형 검사 제외).

from collections import Sequence

class NamedMutableSequence(Sequence):
    __slots__ = ()

    def __init__(self, *a, **kw):
        slots = self.__slots__
        for k in slots:
            setattr(self, k, kw.get(k))

        if a:
            for k, v in zip(slots, a):
                setattr(self, k, v)

    def __str__(self):
        clsname = self.__class__.__name__
        values = ', '.join('%s=%r' % (k, getattr(self, k))
                           for k in self.__slots__)
        return '%s(%s)' % (clsname, values)

    __repr__ = __str__

    def __getitem__(self, item):
        return getattr(self, self.__slots__[item])

    def __setitem__(self, item, value):
        return setattr(self, self.__slots__[item], value)

    def __len__(self):
        return len(self.__slots__)

class Point(NamedMutableSequence):
    __slots__ = ('x', 'y')

예 :

>>> p = Point(0, 0)
>>> p.x = 10
>>> p
Point(x=10, y=0)
>>> p.x *= 10
>>> p
Point(x=100, y=0)

원하는 경우 클래스를 만드는 메서드도 사용할 수 있습니다 (명시 적 클래스를 사용하는 것이 더 투명합니다).

def namedgroup(name, members):
    if isinstance(members, str):
        members = members.split()
    members = tuple(members)
    return type(name, (NamedMutableSequence,), {'__slots__': members})

예 :

>>> Point = namedgroup('Point', ['x', 'y'])
>>> Point(6, 42)
Point(x=6, y=42)

파이썬 2에서는 약간의 조정이 필요합니다. 상속에서 Sequence하면 클래스에가__dict__ 있고 __slots__작동이 중지됩니다.

Python 2의 솔루션은 상속하지 Sequence않고 object. 경우에 isinstance(Point, Sequence) == True요구되는, 당신은 등록해야하는 NamedMutableSequence기본 클래스 Sequence:

Sequence.register(NamedMutableSequence)

동적 유형 생성으로이를 구현해 보겠습니다.

import copy
def namedgroup(typename, fieldnames):

    def init(self, **kwargs): 
        attrs = {k: None for k in self._attrs_}
        for k in kwargs:
            if k in self._attrs_:
                attrs[k] = kwargs[k]
            else:
                raise AttributeError('Invalid Field')
        self.__dict__.update(attrs)

    def getattribute(self, attr):
        if attr.startswith("_") or attr in self._attrs_:
            return object.__getattribute__(self, attr)
        else:
            raise AttributeError('Invalid Field')

    def setattr(self, attr, value):
        if attr in self._attrs_:
            object.__setattr__(self, attr, value)
        else:
            raise AttributeError('Invalid Field')

    def rep(self):
         d = ["{}={}".format(v,self.__dict__[v]) for v in self._attrs_]
         return self._typename_ + '(' + ', '.join(d) + ')'

    def iterate(self):
        for x in self._attrs_:
            yield self.__dict__[x]
        raise StopIteration()

    def setitem(self, *args, **kwargs):
        return self.__dict__.__setitem__(*args, **kwargs)

    def getitem(self, *args, **kwargs):
        return self.__dict__.__getitem__(*args, **kwargs)

    attrs = {"__init__": init,
                "__setattr__": setattr,
                "__getattribute__": getattribute,
                "_attrs_": copy.deepcopy(fieldnames),
                "_typename_": str(typename),
                "__str__": rep,
                "__repr__": rep,
                "__len__": lambda self: len(fieldnames),
                "__iter__": iterate,
                "__setitem__": setitem,
                "__getitem__": getitem,
                }

    return type(typename, (object,), attrs)

작업을 계속하기 전에 속성이 유효한지 확인합니다.

이게 절임이 가능한가요? 다음을 수행하는 경우에만 가능합니다.

>>> import pickle
>>> Point = namedgroup("Point", ["x", "y"])
>>> p = Point(x=100, y=200)
>>> p2 = pickle.loads(pickle.dumps(p))
>>> p2.x
100
>>> p2.y
200
>>> id(p) != id(p2)
True

정의는 네임 스페이스에 있어야하며 pickle이 찾을 수있을만큼 충분히 오래 있어야합니다. 따라서 이것을 패키지에 포함하도록 정의하면 작동합니다.

Point = namedgroup("Point", ["x", "y"])

다음을 수행하거나 정의를 임시로 만들면 Pickle이 실패합니다 (예 : 함수가 종료되면 범위를 벗어남).

some_point = namedgroup("Point", ["x", "y"])

그리고 예, 유형 생성에 나열된 필드의 순서를 유지합니다.


namedtuples와 유사한 동작을 원하지만 변경 가능한 경우 namedlist를 시도 하십시오.

변경 가능하려면 튜플이 될 수 없습니다 .


튜플은 정의상 불변입니다.

그러나 점 표기법으로 속성에 액세스 할 수있는 사전 하위 클래스를 만들 수 있습니다.

In [1]: %cpaste
Pasting code; enter '--' alone on the line to stop or use Ctrl-D.
:class AttrDict(dict):
:
:    def __getattr__(self, name):
:        return self[name]
:
:    def __setattr__(self, name, value):
:        self[name] = value
:--

In [2]: test = AttrDict()

In [3]: test.a = 1

In [4]: test.b = True

In [5]: test
Out[5]: {'a': 1, 'b': True}

성능이 거의 중요하지 않다면 다음과 같은 어리석은 해킹을 사용할 수 있습니다.

from collection import namedtuple

Point = namedtuple('Point', 'x y z')
mutable_z = Point(1,2,[3])

참고 URL : https://stackoverflow.com/questions/29290359/existence-of-mutable-named-tuple-in-python

반응형