orderedset 什麼是Python中的“命名元組”?




python orderedset (8)

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)

閱讀Python 3.1變化 ,我發現了一些......意想不到的事情:

sys.version_info元組現在是一個命名元組

我以前從來沒有聽說過命名元組,並且我認為元素可以通過數字(如元組和列表)或按鍵(如在字典中)進行索引。 我從未想到他們可以通過兩種方式進行索引。

因此,我的問題是:

  • 什麼是命名元組?
  • 如何使用它們?
  • 為什麼/何時應該使用命名元組而不是普通元組?
  • 為什麼/何時應該使用普通的元組而不是命名的元組?
  • 是否有任何一種“命名列表”(命名元組的可變版本)?

什麼是姓名?

顧名思義,namedtuple就是一個名字的元組。 在標準元組中,我們使用索引訪問元素,而namedtuple允許用戶為元素定義名稱。 這非常方便,尤其是處理csv(逗號分隔值)文件和處理複雜和大型的數據集,其中的代碼由於使用索引而變得混亂(不是pythonic)。

如何使用它們?

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

名為元組允許向後兼容檢查版本的代碼

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

同時通過使用此語法允許將來的代碼更加明確

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

namedtuple

是清理代碼並使其更具可讀性的最簡單方法之一。 它自我記錄元組中正在發生的事情。 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"

要理解第一個例子中發生的事情是非常困難的。 使用namedtuple,每個字段都有一個名稱。 你可以通過名字而不是位置或索引來訪問它。 我們可以將它稱為p.saturation,而不是p[1] 。 這很容易理解。 它看起來更乾淨。

創建namedtuple的實例比創建字典更容易。

# 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

什麼時候可以使用namedtuple

  1. 如前所述,namedtuple使理解元組更容易。 因此,如果您需要引用元組中的項目,那麼將它們創建為namedtuples才有意義。
  2. 除了比字典更輕量級之外,namedtuple還保持與字典不同的順序。
  3. 如上例所示,創建namedtuple實例比字典更簡單。 引用指定元組中的項目看起來比字典更清晰。 p.hue而不是p['hue']

語法

collections.namedtuple(typename, field_names[, verbose=False][, rename=False])
  • namedtuple在收藏庫中。
  • typename:這是新的元組子類的名稱。
  • field_names:每個字段的名稱序列。 它可以是列表['x', 'y', 'z']或字符串xyz (不含逗號,只是空格)或x, y, z序列。
  • 重命名:如果重命名為True ,則無效的字段名會自動替換為位置名稱。 例如, ['abc', 'def', 'ghi','abc']被轉換為['abc', '_1', 'ghi', '_3'] ,從而消除關鍵字'def'是定義函數的保留字)和重複的字段名'abc'
  • 詳細:如果verbose為True ,則在構建之前打印類定義。

如果您願意,您仍然可以通過其位置訪問namedtuples。 p[1] == p.saturation 。 它仍然像常規元組一樣解包。

方法

所有常規元組方法都受支持。 例如:min(),max(),len(),in,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並且具有_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://www.reddit.com/r/Python/comments/38ee9d/intro_to_namedtuple/

還有一個名為類似於命名元組的名單,但是可變的https://pypi.python.org/pypi/namedlist


什麼是命名元組?

一個命名的元組是一個元組。

它完成了所有元組的功能。

但它不僅僅是一個元組。

它是一個元組的特定子類,它是按照您的規範以編程方式創建的,具有命名字段和固定長度。

例如,這創建了一個元組的子類,除了具有固定長度(在本例中為三個)之外,它可以在任何地方使用,而不會中斷元組。 這就是所謂的Liskov可替代性:

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

命名元組是元組可以通過名稱而不是索引來訪問它們的元素!

你這樣做一個namedtuple:

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

為什麼/何時應該使用命名元組而不是普通元組?

當它改進你的代碼以在代碼中表達元組元素的語義時使用它們。 如果否則將使用具有不變數據屬性和功能的對象,則可以使用它們而不是對象。 您也可以將它們子類化以添加功能,例如

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)

為什麼/何時應該使用普通的元組而不是命名的元組?

這可能是從使用命名元組切換到元組的回歸。 前期設計決策的核心是在使用元組時,所涉及的額外代碼的代價是否值得提高可讀性。

命名元組與元組沒有使用額外的內存。

是否有任何一種“命名列表”(命名元組的可變版本)?

您正在尋找實現靜態大小列表的所有功能的分割對像或像命名元組一樣工作的子類列表(並以某種方式阻止列表大小發生變化)。

現在已經擴大了,甚至可能是里斯科可替代的第一個例子:

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

其他人已經回答了這個問題,但我認為我還有其他東西需要補充。

可以直觀地將Namedtple視為定義班級的捷徑。

查看定義class的繁瑣而常規的方法。

class Duck:
    def __init__(self, color, weight):
        self.color = color
        self.weight = weight
red_duck = Duck('red', '10')

    In [50]: red_duck
    Out[50]: <__main__.Duck at 0x1068e4e10>
    In [51]: red_duck.color
    Out[51]: 'red'

至於namedtuple

from collections import namedtuple
Duck = namedtuple('Duck', ['color', 'weight'])
red_duck = Duck('red', '10')

In [54]: red_duck
Out[54]: Duck(color='red', weight='10')
In [55]: red_duck.color
Out[55]: 'red'

命名元組基本上是易於創建的輕量級對像類型。 命名的元組實例可以使用類似於對象的變量解引用或標準元組語法來引用。 它們可以類似地用於struct或其他常見記錄類型,除非它們是不可變的。 它們是在Python 2.6和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()轉換為字典,該pt1._asdict()返回{'x': 1.0, 'y': 5.0}並且可以使用所有常用的字典功能進行操作。

如前所述,您應該查看文檔以獲取更多關於構建這些示例的信息。


嘗試這個:

collections.namedtuple()

基本上, namedtuples很容易創建,輕量級對像類型。 他們將元組變成方便的容器,用於簡單的任務。 使用namedtuples ,您不必使用整數索引來訪問元組的成員。

例子:

代碼1:

>>> from collections import namedtuple

>>> Point = namedtuple('Point','x,y')

>>> pt1 = Point(1,2)

>>> pt2 = Point(3,4)

>>> dot_product = ( pt1.x * pt2.x ) +( pt1.y * pt2.y )

>>> print dot_product
11

代碼2:

>>> from collections import namedtuple

>>> Car = namedtuple('Car','Price Mileage Colour Class')

>>> xyz = Car(Price = 100000, Mileage = 30, Colour = 'Cyan', Class = 'Y')

>>> print xyz

Car(Price=100000, Mileage=30, Colour='Cyan', Class='Y')
>>> print xyz.Class
Y




namedtuple