पाइथन में छँटाई sorted() और list.sort() का उपयोग करके की जाती है। समझते हैं कि कैसे यह काम करता है, उदाहरणों पर चर्चा करता है।
पाइथन में छँटाई sorted() फ़ंक्शन द्वारा की जाती है, यदि ये पुनरावृत्त वस्तुएँ हैं, और list.sort() विधि द्वारा, यदि यह एक सूची है। आइए अधिक विस्तार से देखें कि यह पुराने संस्करणों में कैसे काम करता था और अब कैसे काम करता है।
सॉर्टिंग की मूल बातें
पायथन सूची को कैसे क्रमबद्ध करें? आरोही क्रम में सॉर्ट करने के लिए, पायथन सॉर्ट फ़ंक्शन sorted() को कॉल करने के लिए पर्याप्त है, जो एक नई सॉर्ट की गई सूची लौटाएगा:
sorted([5, 2, 3, 1, 4])
[1, 2, 3, 4, 5]
सूची को क्रमबद्ध करने के लिए आप पायथन सूची पद्धति list.sort() का भी उपयोग कर सकते हैं, जो मूल सूची को संशोधित करती है (और भ्रम से बचने के लिए None लौटाता है)। आमतौर पर, सूची को पायथन में सॉर्ट करना sorted() उपयोग करने जितना सुविधाजनक नहीं है, लेकिन यदि आपको मूल सूची की आवश्यकता नहीं है, तो यह थोड़ा अधिक कुशल होगा:
a = [5, 2, 3, 1, 4]
a.sort()
a
[1, 2, 3, 4, 5]
टिप्पणी: पायथन में, None लौटाना और कुछ भी न लौटाना एक ही बात है।
एक और अंतर यह है कि विधि list.sort() केवल सूचियों के लिए परिभाषित की गई है, जबकि पायथन फ़ंक्शन sorted सभी iterable ऑब्जेक्ट्स के साथ काम करता है। मोटे तौर पर, पायथन फ़ंक्शन sort सूची को क्रमबद्ध करता है और इसे क्रमबद्ध रूप में संग्रहीत करता है, जबकि पायथन फ़ंक्शन sorted मूल को संशोधित किए बिना एक नई सॉर्ट की गई सूची बनाता है।
sorted({1: 'D', 2: 'B', 3: 'B', 4: 'E', 5: 'A'})
[1, 2, 3, 4, 5]
टिप्पणी: पायथन में किसी शब्दकोश पर पुनरावृति करते समय, यह अपनी कुंजियों को लौटाता है। यदि आपको उनके मानों या “कुंजी-मान” जोड़े की आवश्यकता है, तो क्रमशः पद्धतियाँ dict.values() और dict.items() का उपयोग करें।
आइए पायथन में क्रमबद्ध करने के मूल कार्यों पर एक नज़र डालें।
कुंजी फ़ंक्शन
पायथन 2.4 से शुरू होकर, list.sort() और sorted() में एक पैरामीटर key है जो एक फ़ंक्शन को निर्दिष्ट करता है जिसे तुलना करने से पहले प्रत्येक तत्व पर कॉल किया जाएगा। यहाँ स्ट्रिंग्स की एक पंक्ति-असंवेदनशील तुलना है:
sorted("This is a test string from Andrew".split(), key=str.lower)
['a', 'Andrew', 'from', 'is', 'string', 'test', 'This']
किसी कुंजी का मान एक फ़ंक्शन होना चाहिए जो एक तर्क लेता है और सॉर्टिंग के लिए एक कुंजी लौटाता है। यह तेज़ है क्योंकि कुंजी फ़ंक्शन को प्रत्येक तत्व के लिए केवल एक बार कॉल किया जाता है।
आप अक्सर उस कोड का सामना कर सकते हैं जहाँ किसी जटिल वस्तु को उसके किसी एक सूचकांक में क्रमबद्ध किया जाता है। उदाहरण के लिए:
student_tuples = [
('john', 'A', 15),
('jane', 'B', 12),
('dave', 'B', 10),
]
sorted(student_tuples, key=lambda student: student[2]) # Sort by age
# Output: [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
वही विधि नेम किए गए एट्रीब्यूट वाली वस्तुओं के लिए भी काम करती है:
class Student:
def __init__(self, name, grade, age):
self.name = name
self.grade = grade
self.age = age
def __repr__(self):
return repr((self.name, self.grade, self.age))
def weighted_grade(self):
return 'CBA'.index(self.grade) / self.age
student_objects = [
Student('john', 'A', 15),
Student('jane', 'B', 12),
Student('dave', 'B', 10),
]
sorted(student_objects, key=lambda student: student.age) # Sort by age
# Output: [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
ऑपरेटर मॉड्यूल के कार्य
ऊपर दिखाए गए कुंजी फ़ंक्शन उदाहरण इतने सामान्य हैं कि पायथन चीजों को आसान और तेज़ बनाने के लिए आसान कार्य प्रदान करता है। ऑपरेटर मॉड्यूल में itemgetter(), attrgetter() और, पायथन 2.6 से शुरू होकर, methodcaller() फ़ंक्शन शामिल हैं। उनके साथ, यह और भी आसान है:
from operator import itemgetter, attrgetter, methodcaller
sorted(student_tuples, key=itemgetter(2))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
sorted(student_objects, key=attrgetter('age'))
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
ऑपरेटर फ़ंक्शन पायथन सरणियों को सॉर्ट करने के लिए कई स्तरों का उपयोग करने की क्षमता प्रदान करते हैं। आइए पहले ग्रेड के अनुसार और फिर उम्र के अनुसार छात्रों को सॉर्ट करें:
sorted(student_tuples, key=itemgetter(1, 2))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
sorted(student_objects, key=attrgetter('grade', 'age'))
[('john', 'A', 15), ('dave', 'B', 10), ('jane', 'B', 12)]
भारित ग्रेड के अनुसार छात्रों को सॉर्ट करने के लिए methodcaller() फ़ंक्शन का उपयोग करते हैं:
[(student.name, student.weighted_grade()) for student in student_objects]
[('john', 0.13333333333333333), ('jane', 0.08333333333333333), ('dave', 0.1)]
>>> sorted(student_objects, key=methodcaller('weighted_grade'))
[('jane', 'B', 12), ('dave', 'B', 10), ('john', 'A', 15)]
पाइथन में आरोही क्रमानुसार और अवरोही क्रमानुसार क्रमबद्ध करना
list.sort()
और sorted()
में एक पैरामीटर reverse
होता है जो एक बूलियन मान लेता है। इसका उपयोग अवरोही क्रम में सॉर्ट करने के लिए किया जाता है। आइए आयु के अवरोही क्रम में छात्रों को सॉर्ट करें:
sorted(student_tuples, key=itemgetter(2), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
sorted(student_objects, key=attrgetter('age'), reverse=True)
[('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
क्रमबद्ध करने की स्थिरता और पाइथन में जटिल क्रमबद्धता
पाइथन 2.2 संस्करण से शुरू होकर, यह गारंटी दी जाती है कि क्रमबद्धता स्थिर होगी: यदि कई प्रविष्टियों में समान कुंजियाँ हैं, तो उनका क्रम वही रहेगा। उदाहरण:
data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
sorted(data, key=itemgetter(0))
[('blue', 1), ('blue', 2), ('red', 1), ('red', 2)]
ध्यान दें कि blue
वाली दोनों प्रविष्टियों ने अपना प्रारंभिक क्रम बनाए रखा है। यह संपत्ति क्रमिक सॉर्टिंग द्वारा जटिल सॉर्टिंग बनाने की अनुमति देती है। इसके बाद हम छात्र डेटा को पहले आयु के आरोही क्रम में सॉर्ट करते हैं, और फिर अंकों के अवरोही क्रम में, ताकि डेटा को पहले अंकों और फिर आयु के क्रम में सॉर्ट किया जा सके:
s = sorted(student_objects, key=attrgetter('age')) # Sort by secondary key
sorted(s, key=attrgetter('grade'), reverse=True) # Sort by primary key in reverse
# Output: [('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
टिमसॉर्ट जैसे पाइथन सॉर्टिंग एल्गोरिदम कई सॉर्टिंग को इतनी कुशलता से निष्पादित करते हैं क्योंकि वे डेटासेट में पहले से मौजूद किसी भी ऑर्डर का लाभ उठा सकते हैं।
डेकोरेट-सॉर्ट-अनडेकोरेट
- सबसे पहले, मूल सूची को नए मानों से भरा जाता है जो क्रम को नियंत्रित करते हैं। फिर नई सूची को छाँटा
- जाता है। इसके बाद, जुड़े हुए मान हटा दिए जाते हैं और अंत में एक छाँटी गई सूची शेष रह जाती है जिसमें मूल तत्व ही होते हैं।
- इस तरह विद्यार्थियों के डेटा को उनकी ग्रेड के अनुसार छांटा जा सकता है:
decorated = [(student.grade, i, student) for i, student in enumerate(student_objects)]
decorated.sort()
[student for grade, i, student in decorated] # Undecorate
# Output: [('john', 'A', 15), ('jane', 'B', 12), ('dave', 'B', 10)]
यह इस कारण से काम करता है क्योंकि कॉर्टेजेज की तुलना लेक्सिकोग्राफ़िक रूप से की जाती है, पहले तत्वों की तुलना की जाती है, और अगर वे एक जैसे हैं, तब दूसरे तत्वों की तुलना की जाती है और इसी तरह आगे बढ़ते जाते हैं।
हमेशा सजाए गए सूची में इंडेक्स को शामिल करना आवश्यक नहीं है, लेकिन इसके फायदे हैं:
- सॉर्टिंग स्थिर है – यदि दो तत्वों की कुंजी समान है, तो उनका क्रम नहीं बदलेगा।
- स्रोत तत्वों की तुलना करने में सक्षम होने की ज़रूरत नहीं है, क्योंकि सजाए गए ट्यूपल्स का क्रम अधिकतम पहले दो तत्वों द्वारा निर्धारित किया जाएगा। उदाहरण के लिए, मूल सूची में जटिल संख्याएँ हो सकती हैं जिनकी सीधे तुलना नहीं की जा सकती।
- इस मुहावरे को Perl प्रोग्रामर के बीच लोकप्रिय बनाने वाले रैंडल श्वार्ट्ज़ के नाम पर श्वार्ट्ज़ परिवर्तन भी कहा जाता है।
बड़ी सूचियों और पायथन के 2.4 से कम संस्करणों के लिए, “सजाना-छँटाई करना-अनसजाना” छँटाई का इष्टतम तरीका होगा। 2.4+ संस्करणों के लिए, कुंजी फ़ंक्शन वही कार्यक्षमता प्रदान करते हैं।
cmp पैरामीटर का उपयोग
पायथन 2.x के सभी संस्करण कस्टम तुलना फ़ंक्शन को संभालने के लिए cmp पैरामीटर का समर्थन करते थे। पायथन 3.0 में, इस पैरामीटर को पूरी तरह से हटा दिया गया था। पायथन 2.x में, sort() में एक फ़ंक्शन पास किया जा सकता है जिसका उपयोग तत्वों की तुलना के लिए किया जाएगा। इसे दो तर्क लेने चाहिए और “कम से कम” के मामले के लिए एक नकारात्मक मान वापस करना चाहिए, “इससे अधिक” के लिए एक सकारात्मक मान और यदि वे बराबर हैं तो शून्य:
def numeric_compare(x, y):
return x - y
sorted([5, 2, 4, 1, 3], cmp=numeric_compare)
[1, 2, 3, 4, 5]
तुलना को उल्टे क्रम में किया जा सकता है:
def reverse_numeric(x, y):
return y - x
sorted([5, 2, 4, 1, 3], cmp=reverse_numeric)
[5, 4, 3, 2, 1]
कोड को संस्करण 2.x से 3.x में पोर्ट करते समय, ऐसी स्थिति आ सकती है जहाँ आपको तुलना के लिए कस्टम फ़ंक्शन को कुंजी फ़ंक्शन में बदलने की आवश्यकता होती है। निम्नलिखित रैपर पायथन में इस कार्य को आसान बनाता है:
def cmp_to_key(mycmp):
'Convert a cmp= function into a key= function'
class K(object):
def __init__(self, obj, *args):
self.obj = obj
def __lt__(self, other):
return mycmp(self.obj, other.obj) < 0
def __gt__(self, other):
return mycmp(self.obj, other.obj) > 0
def __eq__(self, other):
return mycmp(self.obj, other.obj) == 0
def __le__(self, other):
return mycmp(self.obj, other.obj) <= 0
def __ge__(self, other):
return mycmp(self.obj, other.obj) >= 0
def __ne__(self, other):
return mycmp(self.obj, other.obj) != 0
return K
रूपांतरण करने के लिए, पुराने फ़ंक्शन को पैक करें:
sorted([5, 2, 4, 1, 3], key=cmp_to_key(reverse_numeric)) [5, 4, 3, 2, 1]
पाइथन 2.7 में, cmp_to_key() फ़ंक्शन को functools मॉड्यूल में जोड़ा गया था।
छँटाई क्रम बनाए रखना
पाइथन की मानक लाइब्रेरी में C++ डेटा प्रकारों जैसे सेट और मैप के समान कोई मॉड्यूल नहीं है। पाइथन इन कार्यों को तृतीय पक्ष लाइब्रेरी में प्रत्यायोजित करता है जो पाइथन पैकेज इंडेक्स में उपलब्ध हैं: वे सूची, शब्दकोश और सेट प्रकारों को छँटाई क्रम में रखने के लिए विभिन्न तरीकों का उपयोग करते हैं। विशेष डेटा संरचना का उपयोग करके क्रम बनाए रखने से डेटा को संपादित करने और बार-बार पुन: क्रमबद्ध करने की भोली प्रणाली में बहुत धीमी प्रक्रिया (वर्ग समय जटिलता) से बचने में मदद मिल सकती है। ये डेटा प्रकारों को लागू करने वाले कुछ मॉड्यूल हैं:
- SortedContainers — शुद्ध पाइथॉन में C लेक्स में छँटाई सूची, शब्दकोश और सेट प्रकारों का कार्यान्वयन, जो C कार्यान्वयन से मेल खाने की गति का दावा करता है। परीक्षण में 100% कोड कवरेज और तनाव परीक्षण के लिए कई घंटे शामिल हैं। दस्तावेज़ में पूर्ण API संदर्भ, प्रदर्शन तुलना और योगदान दिशानिर्देश शामिल हैं।
- rbtree — शब्दकोश और सेट के प्रकारों का C में तेज़ कार्यान्वयन। कार्यान्वयन रेड-ब्लैक ट्री नामक डेटा संरचना का उपयोग करता है।
- treap — छँटाई शब्दकोश। कार्यान्वयन कार्टेशियन ट्री का उपयोग करता है और साइथन का उपयोग करके प्रदर्शन को बढ़ाया जाता है।
- bintrees — C में ट्री-आधारित शब्दकोश और सेट प्रकारों के कई कार्यान्वयन। सबसे तेज़ कार्यान्वयन AVL और रेड-ब्लैक पेड़ों पर आधारित हैं। शब्दकोश के लिए सेट संचालन प्रदान करने के लिए मानक API का विस्तार करता है।
- banyan — C में शब्दकोश और सेट का तेज़ कार्यान्वयन।
- skiplistcollections — शुद्ध पाइथॉन में कार्यान्वयन, जो स्किप सूची पर आधारित है, शब्दकोश और सेट प्रकारों के लिए एक सीमित API प्रदान करता है।
- blist — पाइथॉन और C में लिखा गया, छँटाई सूची, शब्दकोश और सेट प्रकार प्रदान करता है जो “blist” डेटा प्रकार पर आधारित हैं, जो एक B-tree कार्यान्वयन है।
अन्य
भाषा ध्यान में रखते हुए सॉर्ट करने के लिए, की फ़ंक्शन के रूप में locale.strxfrm() या तुलना फ़ंक्शन के रूप में locale.strcoll() का उपयोग करें। reverse का पैरामीटर सॉर्टिंग को स्थिर रखता है। इस प्रभाव को, किसी पैरामीटर के बिना, अंतर्निहित फ़ंक्शन reversed() का दो बार उपयोग करके अनुकरण किया जा सकता है:
data = [('red', 1), ('blue', 1), ('red', 2), ('blue', 2)]
assert sorted(data, reverse=True) == list(reversed(sorted(reversed(data))))
किसी क्लास के लिए डिफ़ॉल्ट सॉर्टिंग ऑर्डर बनाने के लिए, बस संबंधित तुलना मेथड का implementation जोड़ें:
Student.__eq__ = lambda self, other: self.age == other.age
Student.__ne__ = lambda self, other: self.age != other.age
Student.__lt__ = lambda self, other: self.age < other.age
Student.__le__ = lambda self, other: self.age <= other.age
Student.__gt__ = lambda self, other: self.age > other.age
Student.__ge__ = lambda self, other: self.age >= other.age
sorted(student_objects)
[('dave', 'B', 10), ('jane', 'B', 12), ('john', 'A', 15)]
उन टाइप्स के लिए जिनकी तुलना सामान्य रूप से की जाती है, सभी 6 ऑपरेटरों को परिभाषित करने की अनुशंसा की जाती है। क्लास डेकोरेटर functools.total_ordering उनके implementation को सरल बनाता है। की फ़ंक्शन को सॉर्ट किए जाने वाले ऑब्जेक्ट के आंतरिक डेटा तक एक्सेस की आवश्यकता नहीं होती है। वे बाहरी संसाधनों तक भी पहुँच सकते हैं। उदाहरण के लिए, यदि छात्रों के ग्रेड को एक शब्दकोश में संग्रहीत किया जाता है, तो उनका उपयोग छात्रों के नामों की एक अलग सूची को सॉर्ट करने के लिए किया जा सकता है:
students = ['dave', 'john', 'jane']
newgrades = {'john': 'F', 'jane':'A', 'dave': 'C'}
sorted(students, key=newgrades.__getitem__)
['jane', 'dave', 'john']