Python มีชุดสั่งซื้อหรือไม่


477

Python มีพจนานุกรมที่เรียงลำดับแล้ว สิ่งที่เกี่ยวกับชุดสั่ง?


18
สิ่งที่เกี่ยวกับการสนทนา, กระเป๋าของสิ่งต่าง ๆ ? (เรียงลำดับและไม่ซ้ำกัน)
Wim

19
@wim collections.Counterคือกระเป๋าของ Python
flornquake

1
เกิดอะไรขึ้นถ้ามีบางสิ่งเพิ่มเข้ามาสองครั้ง ตำแหน่งควรเป็นอย่างไร
แมคเคย์

2
@McKay - ถ้ามันเป็นไปตามพฤติกรรมของคอลเลกชันสั่งซื้อมันจะยังคงอยู่ในตำแหน่งของการเพิ่มเริ่มต้น
wojtow

คำตอบ:


206

มีเป็นชุดที่สั่งซื้อ (เป็นไปได้เชื่อมโยงใหม่ ) สูตรสำหรับที่จะเรียกจากงูหลาม 2 เอกสาร สิ่งนี้รันบน Py2.6 หรือใหม่กว่าและ 3.0 หรือใหม่กว่าโดยไม่มีการดัดแปลงใด ๆ อินเทอร์เฟซเกือบจะเหมือนกับชุดปกติยกเว้นว่าการเริ่มต้นควรทำกับรายการ

OrderedSet([1, 2, 3])

นี่คือ MutableSet ดังนั้นลายเซ็นสำหรับ.unionไม่ตรงกับชุด แต่เนื่องจากมี__or__สิ่งที่คล้ายกันสามารถเพิ่มได้อย่างง่ายดาย:

@staticmethod
def union(*sets):
    union = OrderedSet()
    union.union(*sets)
    return union

def union(self, *sets):
    for set in sets:
        self |= set

6
ฉันเลือกคำตอบของตัวเองเพราะข้อมูลอ้างอิงจากเอกสารทำให้ใกล้กับคำตอบอย่างเป็นทางการ
Casebash

49
อินเตอร์เฟซที่ไม่ตรงเช่นเดียวกับวัตถุชุดปกติวิธีการที่จำเป็นหลายจะหายไปเช่นupdate, ,union intersection
xApple

5
FYI ฉันสังเกตเห็นว่ามีการเพิ่มสูตรการแก้ไขเล็กน้อยที่อ้างถึงในคำตอบนี้ในPyPiว่าเป็น "ชุดคำสั่ง"
Geoffrey Hing

7
ฉันค่อนข้างมั่นใจว่าคุณไม่ได้รับอนุญาตให้มีสองวิธีที่ทั้งสองเรียกunionในชั้นเรียนเดียวกัน คนสุดท้ายจะ "ชนะ" และคนแรกจะล้มเหลวที่จะอยู่ที่รันไทม์ นี่เป็นเพราะOrderedSet.union(ไม่มีการ parens) ต้องอ้างถึงวัตถุเดี่ยว
Kevin

3
นอกจากนี้ยังมี "orderedset แพ็กเกจ" ซึ่งจะขึ้นอยู่กับสูตรเดียวกัน แต่การดำเนินการใน Cython - pypi.python.org/pypi/orderedset
mbdevpl

149

ชุดที่สั่งซื้อจะเป็นกรณีพิเศษของพจนานุกรมที่เรียงลำดับ

กุญแจของพจนานุกรมนั้นไม่เหมือนใคร ดังนั้นหากใครไม่สนใจค่าในพจนานุกรมสั่ง (เช่นโดยการกำหนดพวกเขาNone) แล้วหนึ่งมีชุดสั่งซื้อเป็นหลัก

ในฐานะของงูหลาม 3.1collections.OrderedDictมี ต่อไปนี้เป็นตัวอย่างการใช้งานของ OrderedSet (โปรดทราบว่าจำเป็นต้องกำหนดหรือลบล้างวิธีการเพียงไม่กี่วิธีcollections.OrderedDictและcollections.MutableSetทำการยกของหนัก)

import collections

class OrderedSet(collections.OrderedDict, collections.MutableSet):

    def update(self, *args, **kwargs):
        if kwargs:
            raise TypeError("update() takes no keyword arguments")

        for s in args:
            for e in s:
                 self.add(e)

    def add(self, elem):
        self[elem] = None

    def discard(self, elem):
        self.pop(elem, None)

    def __le__(self, other):
        return all(e in other for e in self)

    def __lt__(self, other):
        return self <= other and self != other

    def __ge__(self, other):
        return all(e in self for e in other)

    def __gt__(self, other):
        return self >= other and self != other

    def __repr__(self):
        return 'OrderedSet([%s])' % (', '.join(map(repr, self.keys())))

    def __str__(self):
        return '{%s}' % (', '.join(map(repr, self.keys())))

    difference = __sub__ 
    difference_update = __isub__
    intersection = __and__
    intersection_update = __iand__
    issubset = __le__
    issuperset = __ge__
    symmetric_difference = __xor__
    symmetric_difference_update = __ixor__
    union = __or__

1
@Casebash: ใช่หนึ่งอาจต้องการกำหนดชั้นเรียนOrderedSetซึ่ง subclasses OrderedDictและabc.Setแล้วกำหนด__len__, และ__iter__ __contains__
Stephan202

1
@ Stephan202: น่าเสียใจ ABCs คอลเลกชันที่อาศัยอยู่ในcollectionsแต่อย่างอื่นคำแนะนำที่ดี
u0b34a0f6ae

4
นี่เป็นเรื่องจริง แต่คุณมีพื้นที่ว่างเปล่าจำนวนมากเป็นผลซึ่งนำไปสู่ประสิทธิภาพที่ไม่ดี
Daniel Kats

3
เพิ่มเติม; collection.OrderedDict มีให้ใน python 2.7
Nurbldoff

2
การOrderedSet([1,2,3])เพิ่ม TypeError คอนสตรัคทำงานอย่างไร? ไม่มีตัวอย่างการใช้งาน
xApple

90

คำตอบคือไม่ แต่คุณสามารถใช้collections.OrderedDictจากไลบรารีมาตรฐาน Python ด้วยปุ่มเพียง (และค่าเป็นNone) เพื่อจุดประสงค์เดียวกัน

ปรับปรุง : ณ หลาม 3.7 (และ CPython 3.6) มาตรฐานdictมีการรับประกันว่าจะรักษาลำดับและเป็น performant OrderedDictมากกว่า (สำหรับความเข้ากันได้แบบย้อนกลับและโดยเฉพาะการอ่านอย่างไรก็ตามคุณอาจต้องการใช้งานต่อOrderedDictไป)

นี่คือตัวอย่างของวิธีการใช้dictเป็นชุดสั่งซื้อเพื่อกรองรายการที่ซ้ำกันในขณะที่รักษาลำดับดังนั้นการเลียนแบบชุดสั่งซื้อ ใช้dictวิธีการเรียนfromkeys()เพื่อสร้าง dict จากนั้นเพียงแค่ถามkeys()กลับ

>>> keywords = ['foo', 'bar', 'bar', 'foo', 'baz', 'foo']

>>> list(dict.fromkeys(keywords))
['foo', 'bar', 'baz']

4
อาจจะมีมูลค่าการกล่าวขวัญว่านี้ยังทำงาน (เร็วกว่า) dict.fromkeys()กับวานิลลา แต่ในกรณีนั้นคำสั่งซื้อที่สำคัญจะได้รับการเก็บรักษาไว้ในการใช้งาน CPython 3.6+ เท่านั้นดังนั้นจึงOrderedDictเป็นโซลูชันที่พกพาได้มากขึ้นเมื่อมีคำสั่ง
jez

1
จะไม่ทำงานหากค่าไม่ใช่สตริง
Anwar Hossain

4
@AnwarHossain keys = (1,2,3,1,2,1) list(OrderedDict.fromkeys(keys).keys())-> [1, 2, 3], python-3.7 มันได้ผล.
raratiru

1
เราสามารถอนุมานได้ว่า Set in Python 3.7+ รักษาลำดับ
user474491

2
@ user474491 ซึ่งแตกต่างdict, setในหลาม 3.7+ น่าเสียดายที่ไม่ได้รักษาลำดับ
cz

39

ฉันสามารถทำได้หนึ่งที่ดีกว่า OrderedSet: boltons มีpure-Python, IndexedSetชนิดที่เข้ากันได้กับ 2/3 ซึ่งไม่เพียง แต่ชุดที่ได้รับคำสั่งเท่านั้น แต่ยังรองรับการจัดทำดัชนี (เช่นเดียวกับรายการ)

เพียงpip install boltons(หรือคัดลอกsetutils.pyไปยัง codebase ของคุณ) นำเข้าIndexedSetและ:

>>> from boltons.setutils import IndexedSet
>>> x = IndexedSet(list(range(4)) + list(range(8)))
>>> x
IndexedSet([0, 1, 2, 3, 4, 5, 6, 7])
>>> x - set(range(2))
IndexedSet([2, 3, 4, 5, 6, 7])
>>> x[-1]
7
>>> fcr = IndexedSet('freecreditreport.com')
>>> ''.join(fcr[:fcr.index('.')])
'frecditpo'

ทุกอย่างไม่เหมือนใครและคงความเป็นระเบียบเรียบร้อย การเปิดเผยข้อมูลเต็มรูปแบบ: ผมเขียนIndexedSetแต่ยังหมายถึงการที่คุณสามารถข้อผิดพลาดฉันถ้ามีปัญหาใด:)


39

การใช้งานบน PyPI

ในขณะที่คนอื่น ๆ ชี้ให้เห็นว่าไม่มีการติดตั้งชุดการเรียงลำดับการแทรกไว้ใน Python ในตัว (แต่) ฉันรู้สึกว่าคำถามนี้ไม่มีคำตอบที่ระบุว่ามีอะไรในPyPI PyPI

มีแพ็คเกจ:

การนำไปใช้งานบางส่วนจะขึ้นอยู่กับสูตรที่โพสต์โดย Raymond Hettinger ไปยัง ActiveStateซึ่งจะกล่าวถึงในคำตอบอื่น ๆ ที่นี่ด้วย

ความแตกต่างบางอย่าง

  • สั่งซื้อชุด (รุ่น 1.1)
    • ข้อได้เปรียบ: O (1) สำหรับการค้นหาโดยดัชนี (เช่นmy_set[5])
  • oset (เวอร์ชั่น 0.1.3)
    • ข้อได้เปรียบ: O (1) สำหรับ remove(item)
    • ข้อเสีย: เห็นได้ชัดว่า O (n) สำหรับการค้นหาโดยดัชนี

การใช้งานทั้งสองมี O (1) สำหรับadd(item)และ__contains__(item)( item in my_set)


2
คู่แข่งใหม่เป็นcollections_extended.setlist ฟังก์ชั่นเช่นset.unionไม่สามารถใช้งานได้แม้ว่าจะสืบทอดcollections.abc.Setมา
ไทม์เดล

3
OrderedSetขณะนี้สนับสนุนremove
warvariuc

17

หากคุณกำลังใช้ชุดที่สั่งซื้อเพื่อรักษาลำดับการเรียงให้ลองใช้ชุดการเรียงที่เรียงลำดับจาก PyPI sortedcontainersโมดูลให้SortedSetเพียงเพื่อการนี้ ประโยชน์บางประการ: pure-Python, การใช้งานที่รวดเร็ว -C, การครอบคลุมการทดสอบ 100%, ชั่วโมงการทดสอบความเครียด

การติดตั้งจาก PyPI ทำได้ง่ายด้วย pip:

pip install sortedcontainers

โปรดทราบว่าหากคุณไม่สามารถ pip installเพียงแค่ดึงไฟล์ sortedlist.py และ sortedset.py จากพื้นที่เก็บข้อมูลโอเพนซอร์ส

เมื่อติดตั้งแล้วคุณสามารถ:

from sortedcontainers import SortedSet
help(SortedSet)

โมดูลที่จัดเรียงยังรักษา เปรียบเทียบประสิทธิภาพกับการนำไปใช้ทางเลือกต่าง ๆ

สำหรับความคิดเห็นที่ถามเกี่ยวกับประเภทของกระเป๋า Python มีอีกทางเลือกหนึ่งคือประเภทข้อมูลSortedListซึ่งสามารถใช้ในการใช้ถุงอย่างมีประสิทธิภาพ


โปรดทราบว่าSortedSetชั้นเรียนมีสมาชิกที่จะต้องเปรียบเทียบและ hashable
gsnedders

4
@gsnedders builtins setและfrozensetยังต้องมีองค์ประกอบที่จะแฮช ข้อ จำกัด ที่เปรียบเทียบได้คือส่วนเพิ่มเติมสำหรับSortedSetแต่ก็เป็นข้อ จำกัด ที่ชัดเจนเช่นกัน
gotgenes

2
ตามชื่อแนะนำสิ่งนี้จะไม่รักษาความสงบเรียบร้อย มันไม่มีอะไรนอกจากเรียง (ชุด ([ลำดับ])) ซึ่งทำให้ดีขึ้น?
ldmtwo

@ldmtwo ฉันไม่แน่ใจว่าคุณหมายถึงอะไร แต่เพื่อให้ชัดเจนSortedSetซึ่งเป็นส่วนหนึ่งของSorted Containersจะรักษาลำดับการเรียง
GrantJ

2
@GrantJ - มันเป็นความแตกต่างระหว่างว่าจะรักษาคำสั่งแทรกหรือเรียงลำดับ คำตอบอื่น ๆ ส่วนใหญ่เกี่ยวข้องกับลำดับการแทรก ฉันคิดว่าคุณได้รับรู้ถึงสิ่งนี้จากประโยคแรกของคุณแล้ว แต่มันอาจเป็นสิ่งที่ ldmtwo พูด
Justin

8

ในกรณีที่คุณใช้หมีแพนด้าในรหัสของคุณแล้วIndexวัตถุนั้นจะทำงานเหมือนชุดสั่งซื้อดังที่แสดงในบทความนี้บทความนี้

ตัวอย่างจากบทความ:

indA = pd.Index([1, 3, 5, 7, 9])
indB = pd.Index([2, 3, 5, 7, 11])

indA & indB  # intersection
indA | indB  # union
indA - indB  # difference
indA ^ indB  # symmetric difference

คุณสามารถใส่ตัวอย่างในคำตอบนี้ได้ไหม ลิงค์มักจะถูกทำลายหลังจากเวลาผ่านไป
Alechan

1
สำหรับความแตกต่างระหว่างชุดคุณต้องใช้จริงindA.difference(indB)เครื่องหมายลบทำการลบมาตรฐาน
gg349

7

ช้าไปนิดหน่อยกับเกม แต่ฉันเขียนชั้นเรียนsetlistซึ่งเป็นส่วนหนึ่งของcollections-extendedการดำเนินการทั้งสองอย่างSequenceและSet

>>> from collections_extended import setlist
>>> sl = setlist('abracadabra')
>>> sl
setlist(('a', 'b', 'r', 'c', 'd'))
>>> sl[3]
'c'
>>> sl[-1]
'd'
>>> 'r' in sl  # testing for inclusion is fast
True
>>> sl.index('d')  # so is finding the index of an element
4
>>> sl.insert(1, 'd')  # inserting an element already in raises a ValueError
ValueError
>>> sl.index('d')
4

GitHub: https://github.com/mlenzen/collections-extended

เอกสารประกอบ: http://collections-extended.lenzm.net/en/latest/

PyPI: https://pypi.python.org/pypi/collections-extended


7

ไม่มีOrderedSetในห้องสมุดอย่างเป็นทางการ ฉันทำ cheatsheet ครบถ้วนสมบูรณ์ของโครงสร้างข้อมูลทั้งหมดสำหรับการอ้างอิงของคุณ

DataStructure = {
    'Collections': {
        'Map': [
            ('dict', 'OrderDict', 'defaultdict'),
            ('chainmap', 'types.MappingProxyType')
        ],
        'Set': [('set', 'frozenset'), {'multiset': 'collection.Counter'}]
    },
    'Sequence': {
        'Basic': ['list', 'tuple', 'iterator']
    },
    'Algorithm': {
        'Priority': ['heapq', 'queue.PriorityQueue'],
        'Queue': ['queue.Queue', 'multiprocessing.Queue'],
        'Stack': ['collection.deque', 'queue.LifeQueue']
        },
    'text_sequence': ['str', 'byte', 'bytearray']
}

3

ParallelRegressionแพคเกจให้ลิสท์ ()สั่งซื้อชุดชั้นที่มีมากขึ้นวิธีสมบูรณ์กว่าตัวเลือกขึ้นอยู่กับสูตร ActiveState สนับสนุนวิธีการทั้งหมดที่มีอยู่สำหรับรายการและส่วนใหญ่ถ้าไม่ใช่วิธีทั้งหมดที่มีให้สำหรับชุด


2

เช่นเดียวกับคำตอบอื่น ๆ ที่กล่าวถึงเช่นเดียวกับ python 3.7+ dict นั้นเรียงตามคำจำกัดความ แทนที่จะเป็นคลาสย่อยOrderedDictเราสามารถใช้คลาสย่อยabc.collections.MutableSetหรือtyping.MutableSetใช้คีย์ของ dict เพื่อเก็บค่าของเรา

class OrderedSet(typing.MutableSet[T]):
    """A set that preserves insertion order by internally using a dict."""

    def __init__(self, iterable: t.Iterator[T]):
        self._d = dict.fromkeys(iterable)

    def add(self, x: T) -> None:
        self._d[x] = None

    def discard(self, x: T) -> None:
        self._d.pop(x)

    def __contains__(self, x: object) -> bool:
        return self._d.__contains__(x)

    def __len__(self) -> int:
        return self._d.__len__()

    def __iter__(self) -> t.Iterator[T]:
        return self._d.__iter__()

จากนั้นเพียง:

x = OrderedSet([1, 2, -1, "bar"])
x.add(0)
assert list(x) == [1, 2, -1, "bar", 0]

ฉันใส่รหัสนี้ในห้องสมุดขนาดเล็กเพื่อให้ทุกคนสามารถเพียงแค่pip installมัน


-4

สำหรับวัตถุประสงค์มากมายเพียงแค่เรียกเรียงจะพอเพียง ตัวอย่างเช่น

>>> s = set([0, 1, 2, 99, 4, 40, 3, 20, 24, 100, 60])
>>> sorted(s)
[0, 1, 2, 3, 4, 20, 24, 40, 60, 99, 100]

หากคุณกำลังจะใช้สิ่งนี้ซ้ำ ๆ จะมีค่าใช้จ่ายเกิดขึ้นโดยการเรียกฟังก์ชันเรียงลำดับดังนั้นคุณอาจต้องการบันทึกรายการผลลัพธ์ตราบใดที่คุณเปลี่ยนชุดเสร็จแล้ว หากคุณต้องการรักษาองค์ประกอบที่ไม่ซ้ำกันและเรียงลำดับฉันเห็นด้วยกับข้อเสนอแนะของการใช้ OrderedDict จากคอลเลกชันที่มีค่าโดยพลการเช่นไม่มี


43
วัตถุประสงค์สำหรับ OrderedSet คือเพื่อให้สามารถรับรายการตามลำดับที่พวกเขาเพิ่มเข้าไปในชุด คุณอาจจะเรียกตัวอย่างเช่น SortedSet ...
บำรุงรักษาเป็นระยะ

-4

ดังนั้นฉันยังมีรายการเล็ก ๆ ที่ฉันมีความเป็นไปได้ที่จะแนะนำค่าที่ไม่ซ้ำกันอย่างชัดเจน

ฉันค้นหาการมีอยู่ของรายการเฉพาะบางประเภท แต่แล้วก็รู้ว่าการทดสอบการมีอยู่ขององค์ประกอบก่อนที่จะเพิ่มการทำงานได้ดี

if(not new_element in my_list):
    my_list.append(new_element)

ฉันไม่ทราบว่ามีคำเตือนวิธีง่าย ๆ นี้หรือไม่ แต่ก็แก้ปัญหาของฉันได้


ปัญหาหลักของวิธีนี้คือการเพิ่มการรันใน O (n) หมายความว่ามันช้าลงด้วยรายการใหญ่ ๆ ชุดในตัวของ Python นั้นเก่งมากในการทำให้การเพิ่มองค์ประกอบเร็วขึ้น แต่สำหรับกรณีการใช้งานที่เรียบง่ายมันใช้งานได้อย่างแน่นอน!
Draconis
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.