어디에서 가변 명명 된 튜플이 있습니까?
누구나 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
'IT' 카테고리의 다른 글
| React Native 오류 : " '9.0.1'에서 Java 버전을 확인할 수 없습니다." (0) | 2020.08.21 |
|---|---|
| Google CDN의 최신 jQuery 버전 (0) | 2020.08.20 |
| CSS에서 마우스 다운 선택기는 무엇입니까? (0) | 2020.08.20 |
| XML 문서를 공유로 (0) | 2020.08.20 |
| Vim에서 검색 줄에 텍스트를 넣는 방법이 있습니까? (0) | 2020.08.20 |