[python] 파이썬에서 “명명 된 튜플”이란 무엇입니까?

Python 3.1변경 사항을 읽으면 예기치 않은 무언가가 발견되었습니다.

sys.version_info 튜플은 이제 명명 된 튜플입니다 .

나는 전에 명명 된 튜플에 대해 들어 본 적이 없으며 요소 (튜플 및 목록과 같은) 또는 키 (dicts와 같은)로 요소를 색인 할 수 있다고 생각했습니다. 나는 그들이 두 가지 방법으로 색인을 생성 할 것으로 예상하지 못했습니다.

따라서 내 질문은 다음과 같습니다.

  • 튜플이란?
  • 그것들을 사용하는 방법?
  • 왜 / 언제 일반 튜플 대신 명명 된 튜플을 사용해야합니까?
  • 왜 명명 된 튜플 대신 일반 튜플을 사용해야합니까?
  • 어떤 종류의 “명명 된 목록”(명명 된 튜플의 변경 가능한 버전)이 있습니까?


답변

명명 된 튜플은 기본적으로 작성하기 쉽고 가벼운 객체 유형입니다. 명명 된 튜플 인스턴스는 객체와 같은 변수 역 참조 또는 표준 튜플 구문을 사용하여 참조 할 수 있습니다. struct변경 불가능하다는 점을 제외하고 다른 공통 레코드 유형 과 유사하게 사용할 수 있습니다 . Python 2.4 및 Python 3.0에 추가되었지만 Python 2.4에는 구현 방법이 있습니다.

예를 들어, 점을 튜플로 나타내는 것이 일반적 (x, y)입니다. 이것은 다음과 같은 코드로 이어집니다.

pt1 = (1.0, 5.0)
pt2 = (2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)

명명 된 튜플을 사용하면 더 읽기 쉽습니다.

from collections import namedtuple
Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
line_length = sqrt((pt1.x-pt2.x)**2 + (pt1.y-pt2.y)**2)

그러나 명명 된 튜플은 여전히 ​​일반 튜플과 역 호환되므로 다음과 같이 작동합니다.

Point = namedtuple('Point', 'x y')
pt1 = Point(1.0, 5.0)
pt2 = Point(2.5, 1.5)

from math import sqrt
# use index referencing
line_length = sqrt((pt1[0]-pt2[0])**2 + (pt1[1]-pt2[1])**2)
 # use tuple unpacking
x1, y1 = pt1

따라서 객체 표기법을 사용하면 코드를 더 pythonic하고 더 쉽게 읽을 수 있다고 생각하는 곳이라면 어디서나 튜플 대신 명명 된 튜플을 사용해야합니다 . 나는 개인적으로 그것들을 매우 간단한 값 유형을 나타 내기 위해 사용하기 시작했습니다. 튜플 패킹의 컨텍스트를 보지 않고도 함수를 더 읽기 쉽게 만듭니다.

또한 함수가없고 필드 만 있는 일반 불변 클래스를 바꿀 수도 있습니다. 명명 된 튜플 유형을 기본 클래스로 사용할 수도 있습니다.

class Point(namedtuple('Point', 'x y')):
    [...]

그러나 튜플과 마찬가지로 명명 된 튜플의 속성은 변경할 수 없습니다.

>>> Point = namedtuple('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
AttributeError: can't set attribute

값을 변경하려면 다른 유형이 필요합니다. 새로운 값을 속성으로 설정할 수있는 가변 레코드 유형에 대한 편리한 레시피가 있습니다 .

>>> from rcdtype import *
>>> Point = recordtype('Point', 'x y')
>>> pt1 = Point(1.0, 5.0)
>>> pt1 = Point(1.0, 5.0)
>>> pt1.x = 2.0
>>> print(pt1[0])
    2.0

그러나 새 필드를 추가 할 수있는 “명명 된 목록”형식은 알 수 없습니다. 이 상황에서 사전을 사용하고 싶을 수도 있습니다. 명명 된 튜플은 pt1._asdict()리턴을 사용하여 사전으로 변환 할 {'x': 1.0, 'y': 5.0}수 있으며 모든 일반 사전 기능으로 조작 할 수 있습니다.

이미 언급했듯이 이러한 예제를 구성한 자세한 내용 은 설명서확인 해야합니다 .


답변

namedtuple 은 튜플 클래스를 만들기위한 팩토리 함수 입니다. 이 클래스를 사용하면 이름으로 호출 가능한 튜플을 만들 수 있습니다.

import collections

#Create a namedtuple class with names "a" "b" "c"
Row = collections.namedtuple("Row", ["a", "b", "c"], verbose=False, rename=False)

row = Row(a=1,b=2,c=3) #Make a namedtuple from the Row class we created

print row    #Prints: Row(a=1, b=2, c=3)
print row.a  #Prints: 1
print row[0] #Prints: 1

row = Row._make([2, 3, 4]) #Make a namedtuple from a list of values

print row   #Prints: Row(a=2, b=3, c=4)


답변

튜플이란?

명명 된 튜플은 튜플입니다.

튜플이 할 수있는 모든 작업을 수행합니다.

그러나 그것은 단순한 튜플 이상입니다.

명명 된 필드와 고정 길이를 사용하여 사양에 따라 프로그래밍 방식으로 생성 된 튜플의 특정 하위 클래스입니다.

예를 들어, 이것은 튜플의 서브 클래스를 작성하며 고정 길이 (이 경우에는 3 개)를 제외하고 튜플이 중단없이 사용되는 모든 곳에서 사용될 수 있습니다. 이것은 Liskov 대체 가능성으로 알려져 있습니다.

Python 3.6의 새로운 기능으로 클래스 정의typing.NamedTuple를 사용하여 namedtuple을 만들수 있습니다.

from typing import NamedTuple

class ANamedTuple(NamedTuple):
    """a docstring"""
    foo: int
    bar: str
    baz: list

위의 유형 주석과 docstring이 추가로 있다는 점을 제외하면 위와 동일합니다. 아래는 Python 2 이상에서 사용할 수 있습니다.

>>> from collections import namedtuple
>>> class_name = 'ANamedTuple'
>>> fields = 'foo bar baz'
>>> ANamedTuple = namedtuple(class_name, fields)

이것은 그것을 인스턴스화합니다.

>>> ant = ANamedTuple(1, 'bar', [])

우리는 그것을 검사하고 그 속성을 사용할 수 있습니다 :

>>> ant
ANamedTuple(foo=1, bar='bar', baz=[])
>>> ant.foo
1
>>> ant.bar
'bar'
>>> ant.baz.append('anything')
>>> ant.baz
['anything']

더 깊은 설명

명명 된 튜플을 이해하려면 먼저 튜플이 무엇인지 알아야합니다. 튜플은 본질적으로 불변 (메모리 내에서 변경할 수 없음) 목록입니다.

정규 튜플을 사용하는 방법은 다음과 같습니다.

>>> student_tuple = 'Lisa', 'Simpson', 'A'
>>> student_tuple
('Lisa', 'Simpson', 'A')
>>> student_tuple[0]
'Lisa'
>>> student_tuple[1]
'Simpson'
>>> student_tuple[2]
'A'

반복 가능한 언 패킹으로 튜플을 확장 할 수 있습니다.

>>> first, last, grade = student_tuple
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'

명명 된 튜플은 색인 대신 이름으로 요소에 액세스 할 수있는 튜플입니다!

다음과 같이 명명 된 튜플을 만듭니다.

>>> from collections import namedtuple
>>> Student = namedtuple('Student', ['first', 'last', 'grade'])

이름을 공백으로 구분하여 단일 문자열을 사용할 수 있으며 API를 약간 더 읽기 쉽게 사용할 수 있습니다.

>>> Student = namedtuple('Student', 'first last grade')

그것들을 사용하는 방법?

튜플이 할 수있는 모든 작업 (위 참조)을 수행하고 다음을 수행 할 수 있습니다.

>>> named_student_tuple = Student('Lisa', 'Simpson', 'A')
>>> named_student_tuple.first
'Lisa'
>>> named_student_tuple.last
'Simpson'
>>> named_student_tuple.grade
'A'
>>> named_student_tuple._asdict()
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> vars(named_student_tuple)
OrderedDict([('first', 'Lisa'), ('last', 'Simpson'), ('grade', 'A')])
>>> new_named_student_tuple = named_student_tuple._replace(first='Bart', grade='C')
>>> new_named_student_tuple
Student(first='Bart', last='Simpson', grade='C')

논평자가 물었다 :

큰 스크립트 나 프로그램에서 일반적으로 명명 된 튜플을 어디에 정의합니까?

당신이 만드는 유형 namedtuple 은 기본적으로 쉬운 속기로 만들 수있는 클래스입니다. 수업처럼 취급하십시오. 피클과 다른 사용자가 찾을 수 있도록 모듈 수준에서 정의하십시오.

글로벌 모듈 레벨의 실제 예제 :

>>> from collections import namedtuple
>>> NT = namedtuple('NT', 'foo bar')
>>> nt = NT('foo', 'bar')
>>> import pickle
>>> pickle.loads(pickle.dumps(nt))
NT(foo='foo', bar='bar')

그리고 이것은 정의를 찾지 못했음을 보여줍니다.

>>> def foo():
...     LocalNT = namedtuple('LocalNT', 'foo bar')
...     return LocalNT('foo', 'bar')
...
>>> pickle.loads(pickle.dumps(foo()))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
_pickle.PicklingError: Can't pickle <class '__main__.LocalNT'>: attribute lookup LocalNT on __main__ failed

왜 / 언제 일반 튜플 대신 명명 된 튜플을 사용해야합니까?

코드에 튜플 요소의 의미가 표현되도록 코드를 향상시킬 때 사용하십시오.

데이터 속성이 변경되지 않고 기능이없는 객체를 사용하려는 경우 객체 대신 객체를 사용할 수 있습니다.

서브 클래스로 기능을 추가 할 수도 있습니다 ( :

class Point(namedtuple('Point', 'x y')):
    """adding functionality to a named tuple"""
        __slots__ = ()
        @property
        def hypot(self):
            return (self.x ** 2 + self.y ** 2) ** 0.5
        def __str__(self):
            return 'Point: x=%6.3f  y=%6.3f  hypot=%6.3f' % (self.x, self.y, self.hypot)

왜 명명 된 튜플 대신 일반 튜플을 사용해야합니까?

명명 된 튜플 사용에서 튜플로 전환하는 것은 아마도 회귀 일 것입니다. 선행 설계 결정은 튜플을 사용할 때 관련된 추가 코드의 비용이 가독성을 향상시킬 가치가 있는지 여부를 중심으로합니다.

명명 된 튜플 대 튜플에서 사용하는 추가 메모리는 없습니다.

어떤 종류의 “명명 된 목록”(명명 된 튜플의 변경 가능한 버전)이 있습니까?

정적 크기 목록의 모든 기능을 구현하는 슬롯 객체 또는 명명 된 튜플처럼 작동하는 하위 클래스 목록을 찾고 (어쨌든 목록의 크기가 변경되는 것을 차단합니다)

이제는 확장되었으며 아마도 Liskov로 대체 가능하며 첫 번째 예입니다.

from collections import Sequence

class MutableTuple(Sequence):
    """Abstract Base Class for objects that work like mutable
    namedtuples. Subclass and define your named fields with
    __slots__ and away you go.
    """
    __slots__ = ()
    def __init__(self, *args):
        for slot, arg in zip(self.__slots__, args):
            setattr(self, slot, arg)
    def __repr__(self):
        return type(self).__name__ + repr(tuple(self))
    # more direct __iter__ than Sequence's
    def __iter__(self):
        for name in self.__slots__:
            yield getattr(self, name)
    # Sequence requires __getitem__ & __len__:
    def __getitem__(self, index):
        return getattr(self, self.__slots__[index])
    def __len__(self):
        return len(self.__slots__)

그리고 서브 클래스를 정의하고 사용하려면 다음을 수행하십시오 __slots__.

class Student(MutableTuple):
    __slots__ = 'first', 'last', 'grade' # customize 


>>> student = Student('Lisa', 'Simpson', 'A')
>>> student
Student('Lisa', 'Simpson', 'A')
>>> first, last, grade = student
>>> first
'Lisa'
>>> last
'Simpson'
>>> grade
'A'
>>> student[0]
'Lisa'
>>> student[2]
'A'
>>> len(student)
3
>>> 'Lisa' in student
True
>>> 'Bart' in student
False
>>> student.first = 'Bart'
>>> for i in student: print(i)
...
Bart
Simpson
A


답변

명명 된 튜플은 훌륭한 기능이며 데이터를위한 완벽한 컨테이너입니다. 데이터를 “저장”해야 할 경우 다음과 같은 튜플 또는 사전을 사용합니다.

user = dict(name="John", age=20)

또는:

user = ("John", 20)

dict은 튜플보다 가변적이고 느리기 때문에 사전 접근 방식은 압도적입니다. 반면에 튜플은 변경 불가능하고 가벼우 나 데이터 필드의 많은 항목에 대한 가독성이 부족합니다.

명명 된 튜플은 두 가지 접근 방식의 완벽한 타협입니다. 가독성, 가벼움 및 불변성이 뛰어납니다 (또한 다형성입니다!).


답변

명명 된 튜플은 다음과 같은 버전을 확인하는 코드와의 호환성을 허용합니다.

>>> sys.version_info[0:2]
(3, 1)

이 구문을 사용하여 향후 코드를 더 명확하게 할 수 있습니다.

>>> sys.version_info.major
3
>>> sys.version_info.minor
1


답변

명명 된

코드를 정리하고 더 쉽게 읽을 수있는 가장 쉬운 방법 중 하나입니다. 튜플에서 일어나는 일을 자체 문서화합니다. Namedtuples 인스턴스는 인스턴스 당 사전이 없기 때문에 일반 튜플만큼 메모리 효율적이므로 사전보다 빠릅니다.

from collections import namedtuple

Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])

 p = Color(170, 0.1, 0.6)
 if p.saturation >= 0.5:
     print "Whew, that is bright!"
 if p.luminosity >= 0.5:
     print "Wow, that is light"

튜플에서 각 요소의 이름을 지정하지 않으면 다음과 같이 나타납니다.

p = (170, 0.1, 0.6)
if p[1] >= 0.5:
    print "Whew, that is bright!"
if p[2]>= 0.5:
   print "Wow, that is light"

첫 번째 예에서 무슨 일이 일어나고 있는지 이해하기가 훨씬 어렵습니다. 명명 된 튜플을 사용하면 각 필드에 이름이 있습니다. 위치 나 색인이 아닌 이름으로 액세스합니다. 대신 p[1]p.saturation이라고 부를 수 있습니다. 이해하기 쉽습니다. 그리고 더 깨끗해 보입니다.

명명 된 튜플의 인스턴스를 만드는 것이 사전을 만드는 것보다 쉽습니다.

# dictionary
>>>p = dict(hue = 170, saturation = 0.1, luminosity = 0.6)
>>>p['hue']
170

#nametuple
>>>from collections import namedtuple
>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)
>>>p.hue
170

언제 명명 된 튜플을 사용할 수 있습니까?

  1. 방금 언급 한 것처럼 명명 된 튜플을 사용하면 튜플을 훨씬 쉽게 이해할 수 있습니다. 따라서 튜플에서 항목을 참조 해야하는 경우 명명 된 튜플로 항목을 만드는 것이 좋습니다.
  2. 튜플이라는 이름은 사전보다 더 가벼울뿐만 아니라 사전과 달리 순서를 유지합니다.
  3. 위의 예에서와 같이 사전보다 명명 된 튜플의 인스턴스를 만드는 것이 더 간단합니다. 그리고 명명 된 튜플에서 항목을 참조하면 사전보다 더 깨끗 해 보입니다. p.hue오히려
    p['hue'].

문법

collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
  • namedtuple은 콜렉션 라이브러리에 있습니다.
  • typename : 이것은 새로운 튜플 서브 클래스의 이름입니다.
  • field_names : 각 필드의 이름 시퀀스. 목록 ['x', 'y', 'z']이나 문자열 x y z(쉼표없이 공백 만 있음)과 같은 시퀀스 이거나x, y, z .
  • rename : rename이 인 True경우 잘못된 필드 이름이 자동으로 위치 이름으로 바뀝니다. 예를 들어, 키워드 (함수 정의를위한 예약어이므로)와 중복 필드 이름을 제거하여 ['abc', 'def', 'ghi','abc']로 변환됩니다 ['abc', '_1', 'ghi', '_3'].'def''abc' .
  • verbose : verbose가 True인 경우 클래스 정의는 빌드 직전에 인쇄됩니다.

원하는 경우 해당 위치별로 명명 된 튜플에 계속 액세스 할 수 있습니다. p[1] == p.saturation. 여전히 일반 튜플처럼 압축이 풀립니다.

행동 양식

모든 일반 튜플 방법 이 지원됩니다. 예 : min (), max (), len (), in, not in, concatenation (+), index, slice 등. namedtuple에 대한 몇 가지 추가 항목이 있습니다. 참고 : 이들은 모두 밑줄로 시작합니다. _replace, _make, _asdict.

_replace
지정된 필드를 새 값으로 바꾸는 명명 된 튜플의 새 인스턴스를 반환합니다.

문법

somenamedtuple._replace(kwargs)

>>>from collections import namedtuple

>>>Color = namedtuple('Color', ['hue', 'saturation', 'luminosity'])
>>>p = Color(170, 0.1, 0.6)

>>>p._replace(hue=87)
Color(87, 0.1, 0.6)

>>>p._replace(hue=87, saturation=0.2)
Color(87, 0.2, 0.6)

주의 : 필드 이름은 따옴표로 묶지 않았습니다. 여기 키워드입니다.
기억하십시오 : 튜플은 이름이 튜플이고 _replace방법 이 있더라도 불변 입니다. 는 _replace생산 new인스턴스; 원본을 수정하거나 이전 값을 바꾸지 않습니다. 물론 새로운 결과를 변수에 저장할 수 있습니다.p = p._replace(hue=169)

_make

기존 시퀀스에서 새 인스턴스를 만들거나 반복 가능하게 만듭니다.

문법

somenamedtuple._make(iterable)

 >>>data = (170, 0.1, 0.6)
 >>>Color._make(data)
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make([170, 0.1, 0.6])  #the list is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make((170, 0.1, 0.6))  #the tuple is an iterable
Color(hue=170, saturation=0.1, luminosity=0.6)

>>>Color._make(170, 0.1, 0.6)
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
    File "<string>", line 15, in _make
TypeError: 'float' object is not callable

마지막 것은 어떻게 되었습니까? 괄호 안의 항목은 반복 가능해야합니다. 따라서 괄호 안의 목록이나 튜플은 작동하지만 반복 가능으로 묶지 않은 값 시퀀스는 오류를 반환합니다.

_asdict

필드 이름을 해당 값에 매핑 하는 새 OrderedDict 를 반환 합니다.

문법

somenamedtuple._asdict()

 >>>p._asdict()
OrderedDict([('hue', 169), ('saturation', 0.1), ('luminosity', 0.6)])

참고 : https://www.reddit.com/r/Python/comments/38ee9d/intro_to_namedtuple/

명명 된 튜플과 비슷하지만 변경 가능한 https://pypi.python.org/pypi/namedlist 라는 명명 된 목록도
있습니다.


답변

튜플이란 무엇입니까?

이름에서 알 수 있듯이 namedtuple은 이름이있는 튜플입니다. 표준 튜플에서는 인덱스를 사용하여 요소에 액세스하는 반면 namedtuple을 사용하면 요소의 이름을 정의 할 수 있습니다. 이것은 특히 csv (쉼표로 구분 된 값) 파일을 처리하고 복잡하고 큰 데이터 세트로 작업하는 데 매우 유용합니다.이 경우 코드는 파이썬을 사용하지 않는 인덱스를 사용하여 복잡해집니다.

그들을 사용하는 방법?

>>>from collections import namedtuple
>>>saleRecord = namedtuple('saleRecord','shopId saleDate salesAmout totalCustomers')
>>>
>>>
>>>#Assign values to a named tuple
>>>shop11=saleRecord(11,'2015-01-01',2300,150)
>>>shop12=saleRecord(shopId=22,saleDate="2015-01-01",saleAmout=1512,totalCustomers=125)

독서

>>>#Reading as a namedtuple
>>>print("Shop Id =",shop12.shopId)
12
>>>print("Sale Date=",shop12.saleDate)
2015-01-01
>>>print("Sales Amount =",shop12.salesAmount)
1512
>>>print("Total Customers =",shop12.totalCustomers)
125

CSV 처리에 대한 흥미로운 시나리오 :

from csv import reader
from collections import namedtuple

saleRecord = namedtuple('saleRecord','shopId saleDate totalSales totalCustomers')
fileHandle = open("salesRecord.csv","r")
csvFieldsList=csv.reader(fileHandle)
for fieldsList in csvFieldsList:
    shopRec = saleRecord._make(fieldsList)
    overAllSales += shopRec.totalSales;

print("Total Sales of The Retail Chain =",overAllSales)