सबसे अधिक संभावना functools.cmp_to_key()
अजगर के प्रकार के अंतर्निहित कार्यान्वयन के साथ निकटता से जुड़ी हुई है। इसके अलावा सी.एम.पी. पैरामीटर विरासत है। आधुनिक तरीका इनपुट वस्तुओं को उन वस्तुओं में बदलना है जो वांछित समृद्ध तुलना संचालन का समर्थन करते हैं।
CPython 2.x के तहत, असमान प्रकार की वस्तुओं को आदेशित किया जा सकता है, भले ही संबंधित समृद्ध तुलना ऑपरेटरों को लागू नहीं किया गया हो। CPython 3.x के तहत, विभिन्न प्रकार की वस्तुओं को स्पष्ट रूप से तुलना का समर्थन करना चाहिए। देखें कि पायथन स्ट्रिंग और इंट की तुलना कैसे करता है? जो आधिकारिक प्रलेखन के लिए लिंक करता है । अधिकांश उत्तर इस अंतर्निहित आदेश पर निर्भर करते हैं। पायथन 3.x पर स्विच करने से संख्याओं और तारों के बीच तुलना को लागू करने और एकजुट करने के लिए एक नए प्रकार की आवश्यकता होगी।
Python 2.7.12 (default, Sep 29 2016, 13:30:34)
>>> (0,"foo") < ("foo",0)
True
Python 3.5.2 (default, Oct 14 2016, 12:54:53)
>>> (0,"foo") < ("foo",0)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: unorderable types: int() < str()
तीन अलग-अलग दृष्टिकोण हैं। पायथन की Iterable
तुलना एल्गोरिथ्म का लाभ उठाने के लिए पहले नेस्टेड कक्षाओं का उपयोग करता है। दूसरा इस घोंसले को एकल वर्ग में नियंत्रित करता है। str
प्रदर्शन पर ध्यान केंद्रित करने के लिए तीसरा अग्रभाग उपवर्ग है। सभी समयबद्ध हैं; दूसरा दोगुना तेज है जबकि तीसरा लगभग छह गुना तेज है। उपवर्गीकरणstr
की आवश्यकता नहीं है, और शायद पहली जगह में एक बुरा विचार था, लेकिन यह कुछ निश्चित आवश्यकताओं के साथ आता है।
सॉर्ट वर्णों को केस द्वारा आदेश देने के लिए डुप्लिकेट किया जाता है, और केस-स्वैप को कम केस लेटर को पहले सॉर्ट करने के लिए मजबूर किया जाता है; यह "प्राकृतिक प्रकार" की सामान्य परिभाषा है। मैं समूहीकरण के प्रकार पर निर्णय नहीं ले सका; कुछ निम्नलिखित पसंद कर सकते हैं, जो महत्वपूर्ण प्रदर्शन लाभ भी लाता है:
d = lambda s: s.lower()+s.swapcase()
जहां उपयोग किया जाता है, तुलना संचालकों के लिए निर्धारित किया जाता है object
इसलिए उन्हें नजरअंदाजfunctools.total_ordering
नहीं किया जाएगा ।
import functools
import itertools
@functools.total_ordering
class NaturalStringA(str):
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, super().__repr__()
)
d = lambda c, s: [ c.NaturalStringPart("".join(v))
for k,v in
itertools.groupby(s, c.isdigit)
]
d = classmethod(d)
@functools.total_ordering
class NaturalStringPart(str):
d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
d = staticmethod(d)
def __lt__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
try:
return int(self) < int(other)
except ValueError:
if self.isdigit():
return True
elif other.isdigit():
return False
else:
return self.d(self) < self.d(other)
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
try:
return int(self) == int(other)
except ValueError:
if self.isdigit() or other.isdigit():
return False
else:
return self.d(self) == self.d(other)
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
def __lt__(self, other):
return self.d(self) < self.d(other)
def __eq__(self, other):
return self.d(self) == self.d(other)
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
import functools
import itertools
@functools.total_ordering
class NaturalStringB(str):
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, super().__repr__()
)
d = lambda s: "".join(c.lower()+c.swapcase() for c in s)
d = staticmethod(d)
def __lt__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
zipped = itertools.zip_longest(*groups)
for s,o in zipped:
if s is None:
return True
if o is None:
return False
s_k, s_v = s[0], "".join(s[1])
o_k, o_v = o[0], "".join(o[1])
if s_k and o_k:
s_v, o_v = int(s_v), int(o_v)
if s_v == o_v:
continue
return s_v < o_v
elif s_k:
return True
elif o_k:
return False
else:
s_v, o_v = self.d(s_v), self.d(o_v)
if s_v == o_v:
continue
return s_v < o_v
return False
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
groups = map(lambda i: itertools.groupby(i, type(self).isdigit), (self, other))
zipped = itertools.zip_longest(*groups)
for s,o in zipped:
if s is None or o is None:
return False
s_k, s_v = s[0], "".join(s[1])
o_k, o_v = o[0], "".join(o[1])
if s_k and o_k:
s_v, o_v = int(s_v), int(o_v)
if s_v == o_v:
continue
return False
elif s_k or o_k:
return False
else:
s_v, o_v = self.d(s_v), self.d(o_v)
if s_v == o_v:
continue
return False
return True
__le__ = object.__le__
__ne__ = object.__ne__
__gt__ = object.__gt__
__ge__ = object.__ge__
import functools
import itertools
import enum
class OrderingType(enum.Enum):
PerWordSwapCase = lambda s: s.lower()+s.swapcase()
PerCharacterSwapCase = lambda s: "".join(c.lower()+c.swapcase() for c in s)
class NaturalOrdering:
@classmethod
def by(cls, ordering):
def wrapper(string):
return cls(string, ordering)
return wrapper
def __init__(self, string, ordering=OrderingType.PerCharacterSwapCase):
self.string = string
self.groups = [ (k,int("".join(v)))
if k else
(k,ordering("".join(v)))
for k,v in
itertools.groupby(string, str.isdigit)
]
def __repr__(self):
return "{}({})".format\
( type(self).__name__
, self.string
)
def __lesser(self, other, default):
if not isinstance(self, type(other)):
return NotImplemented
for s,o in itertools.zip_longest(self.groups, other.groups):
if s is None:
return True
if o is None:
return False
s_k, s_v = s
o_k, o_v = o
if s_k and o_k:
if s_v == o_v:
continue
return s_v < o_v
elif s_k:
return True
elif o_k:
return False
else:
if s_v == o_v:
continue
return s_v < o_v
return default
def __lt__(self, other):
return self.__lesser(other, default=False)
def __le__(self, other):
return self.__lesser(other, default=True)
def __eq__(self, other):
if not isinstance(self, type(other)):
return NotImplemented
for s,o in itertools.zip_longest(self.groups, other.groups):
if s is None or o is None:
return False
s_k, s_v = s
o_k, o_v = o
if s_k and o_k:
if s_v == o_v:
continue
return False
elif s_k or o_k:
return False
else:
if s_v == o_v:
continue
return False
return True
# functools.total_ordering doesn't create single-call wrappers if both
# __le__ and __lt__ exist, so do it manually.
def __gt__(self, other):
op_result = self.__le__(other)
if op_result is NotImplemented:
return op_result
return not op_result
def __ge__(self, other):
op_result = self.__lt__(other)
if op_result is NotImplemented:
return op_result
return not op_result
# __ne__ is the only implied ordering relationship, it automatically
# delegates to __eq__
>>> import natsort
>>> import timeit
>>> l1 = ['Apple', 'corn', 'apPlE', 'arbour', 'Corn', 'Banana', 'apple', 'banana']
>>> l2 = list(map(str, range(30)))
>>> l3 = ["{} {}".format(x,y) for x in l1 for y in l2]
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringA)', number=10000, globals=globals()))
362.4729259099986
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalStringB)', number=10000, globals=globals()))
189.7340817489967
>>> print(timeit.timeit('sorted(l3+["0"], key=NaturalOrdering.by(OrderingType.PerCharacterSwapCase))', number=10000, globals=globals()))
69.34636392899847
>>> print(timeit.timeit('natsort.natsorted(l3+["0"], alg=natsort.ns.GROUPLETTERS | natsort.ns.LOWERCASEFIRST)', number=10000, globals=globals()))
98.2531585780016
प्राकृतिक छँटाई दोनों एक समस्या के रूप में बहुत जटिल और अस्पष्ट रूप से परिभाषित है। unicodedata.normalize(...)
पहले से दौड़ना न भूलें , और str.casefold()
इसके बजाय उपयोग पर विचार करें str.lower()
। वहाँ शायद सूक्ष्म कूटबन्धन मुद्दों पर विचार नहीं किया है। इसलिए मैं अस्थायी रूप से natsort पुस्तकालय की सिफारिश करता हूं । मैंने जीथुब भंडार पर एक त्वरित नज़र डाली; कोड रखरखाव तारकीय रहा है।
मैंने जिन सभी एल्गोरिदम को देखा है वे डुप्लिकेट और लोअरिंग कैरेक्टर और स्वैपिंग केस जैसे ट्रिक्स पर निर्भर हैं। हालांकि यह रनिंग टाइम को दोगुना करता है, एक विकल्प के लिए इनपुट चरित्र सेट पर कुल प्राकृतिक ऑर्डरिंग की आवश्यकता होगी। मुझे नहीं लगता कि यह यूनिकोड विनिर्देशन का हिस्सा है, और चूंकि कई अधिक यूनिकोड अंक हैं [0-9]
, इस तरह की छंटाई करना समान रूप से चुनौतीपूर्ण होगा। यदि आप स्थानीय-जागरूक तुलना चाहते हैं, तो locale.strxfrm
पायथन के सॉर्टिंग हाउ टू के अनुसार अपने तार तैयार करें ।