- Python 3.15.0b1 के feature freeze के साथ lazy imports और Tachyon profiler के अलावा कई व्यावहारिक सुधार भी तय हो गए हैं
asyncio का TaskGroup.cancel() custom exceptions और contextlib.suppress के बिना task group को सहज तरीके से cancel करता है
- ContextDecorator अब async functions, generators और async iterators के पूरे lifecycle को wrap करता है
- threading की नई utilities iterator consumption को threads के बीच serialize या duplicate कर सकती हैं, जिससे Queue के बिना abstraction बना रहता है
Counter में xor operation जोड़ा गया है, और json.loads अब array_hook और frozendict के साथ immutable JSON parsing को support करता है
Python 3.15 में कम चर्चित बदलाव
- Python 3.15.0b1 के feature freeze के साथ इस साल Python में आने वाले फीचर्स तय हो गए हैं। बड़े बदलावों में lazy imports और Tachyon profiler शामिल हैं
- Python 3.15 में बड़े PEP जितने दिखाई न देने वाले, लेकिन व्यावहारिक छोटे फीचर बदलाव भी शामिल हैं। इनमें
asyncio, context managers, thread-safe iterators, Counter और JSON parsing से जुड़े सुधार आए हैं
asyncio TaskGroup cancellation
asyncio में एक अहम बदलाव के रूप में TaskGroup को सहज तरीके से cancel करने की सुविधा जोड़ी गई है
TaskGroup, structured concurrency का एक रूप है, जो कई concurrent tasks को साफ-सुथरे तरीके से बनाकर उनके पूरा होने तक इंतज़ार करने देता है
async with asyncio.TaskGroup() as tg:
tg.create_task(run())
tg.create_task(run())
# Waits for all the tasks to complete
- Python 3.15 से पहले, अगर background signal का इंतज़ार करते हुए
TaskGroup का execution रोकना हो, तो custom exception raise करनी पड़ती थी और उसे contextlib.suppress से filter करना पड़ता था
class Interrupt(Exception):
...
with suppress(Interrupt):
async with asyncio.TaskGroup() as tg:
tg.create_task(run())
tg.create_task(run())
if await wait_for_signal():
raise Interrupt()
- यह तरीका इसलिए काम करता था क्योंकि task group के भीतर exception आने पर बाकी tasks cancel हो जाते थे, और custom
Interrupt exception, ExceptionGroup के हिस्से के रूप में उठने के बाद contextlib.suppress द्वारा filter कर दी जाती थी
ExceptionGroup के साथ काम करने वाला suppress का यह व्यवहार Python 3.12 में जोड़ा गया था, लेकिन इस पर ज़्यादा ध्यान नहीं गया
- Python 3.15 का TaskGroup.cancel वही काम कहीं अधिक सरल बना देता है
async with asyncio.TaskGroup() as tg:
tg.create_task(run())
tg.create_task(run())
if await wait_for_signal():
tg.cancel()
TaskGroup.cancel() group को exception raise किए बिना cancel करता है, इसलिए अलग exception और suppress के संयोजन की ज़रूरत नहीं रहती
context manager सुधार
- Python 3.3 से context managers को सीधे decorator की तरह भी इस्तेमाल किया जा सकता था
@contextmanager
def duration(message: str) -> Iterator[None]:
start = time.perf_counter()
try:
yield
finally:
print(f"{message} elapsed {time.perf_counter() - start:.2f} seconds")
@duration('workload')
def workload():
...
# Or simple as a wrapper
duration('stuff')(other_workload)(...)
duration() जैसे context managers, जो block execution time print करते हैं, function decorator की तरह इस्तेमाल करने में सुविधाजनक हैं, लेकिन async functions, generators और async iterators में यह हमेशा सही तरह से काम नहीं करते थे
@duration('async workload')
async def async_workload():
...
@duration('generator workload')
def workload():
while True:
yield ...
- iterators, async functions और async iterators की semantics सामान्य functions से अलग होती हैं, क्योंकि call होते ही वे क्रमशः generator object, coroutine object और async generator object return करते हैं
- पुराना decorator wrapped target के पूरे lifecycle को cover नहीं कर पाता था और तुरंत पूरा हो जाता था, इसलिए वह actual execution time को पूरी तरह wrap नहीं कर पाता था
- Python 3.15 में
ContextDecorator wrapped function के type की जाँच करता है और decorator को target के पूरे lifecycle पर लागू करता है
- इससे context manager को decorator की तरह इस्तेमाल करते समय होने वाली आम गलती से बचा जा सकता है और syntax भी अधिक साफ हो जाता है
thread-safe iterators
- iterators, Python की मुख्य abstractions में से एक हैं, जो data source और data consumer को अलग रखकर संरचना को अधिक साफ बनाते हैं
lazy from typing import Iterator
def stream_events(...) -> Iterator[str]:
while True:
yield blocking_get_event(...)
events = stream_events(...)
for event in events:
consume(event)
- लेकिन यह abstraction threading या free-threading environment में टूट सकती है, क्योंकि default iterators thread-safe नहीं होते। इससे values skip हो सकती हैं या iterator की internal state खराब हो सकती है
- Python 3.15 का threading.serialize_iterator किसी मौजूदा iterator को wrap करके threads के बीच consumption को serialize करता है
import threading
events = threading.serialize_iterator(stream_events(...))
with ThreadPoolExecutor() as executor:
fut1 = executor.submit(consume, events)
fut2 = executor.submit(consume, events)
source1, source2 = threading.concurrent_tee(squares(10), n=2)
with ThreadPoolExecutor() as executor:
fut1 = executor.submit(consume, source1)
fut2 = executor.submit(consume, source2)
- पहले threads के बीच consumption को synchronize करने के लिए आम तौर पर Queue पर निर्भर रहना पड़ता था, लेकिन नई utilities के साथ multi-threaded code में भी मौजूदा iterator abstraction को बदले बिना बनाए रखा जा सकता है
अतिरिक्त फीचर्स
-
Counter xor operation
- collections.Counter एक ऐसी class है जो discrete occurrence counts को आसानी से गिनने देती है। यह
dict[KeyType, int] की तरह काम करती है और कई उपयोगी operations देती है
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
print(f"{c + d = }") # add two counters together: c[x] + d[x]
print(f"{c - d = }") # subtract (keeping only positive counts)
Counter(a=4, b=3)
Counter(a=1, b=0)
Counter में intersection और union के अनुरूप &, | operations भी हैं
print(f"{c & d = }") # intersection: min(c[x], d[x])
print(f"{c | d = }") # union: max(c[x], d[x])
Counter(a=1, b=1)
Counter(a=3, b=2)
Counter को discrete objects के set की तरह देखा जा सकता है, और ऊपर के उदाहरणों को इस तरह समझा जा सकता है
{a_0, a_1, a_2, b_0} & {a_0, b_0, b_1} == {a_0, b_0}
{a_0, a_1, a_2, b_0} | {a_0, b_0, b_1} == {a_0, a_1, a_2, b_0, b_1}
- Python 3.15 में इसमें xor operation भी जोड़ दी गई है
c = Counter(a=3, b=1)
d = Counter(a=1, b=2)
c ^ d == c | d - c & d == Counter(a=3, b=2) - Counter(a=1, b=1) == Counter(a=2, b=1)
{a_0, a_1, a_2, b_0} ^ {a_0, b_0, b_1} == {a_1, a_2, b_1}
- अगर आपने
Counter के set operations का ज़्यादा इस्तेमाल नहीं किया है, तो xor का सीधा use case सोचना मुश्किल हो सकता है, लेकिन operation completeness के लिहाज़ से यह जोड़ा गया है
-
immutable JSON objects
- Python 3.15 में frozendict जुड़ने के साथ JSON types जैसे array, boolean, float, null, string और object को पूरी तरह immutable और hashable रूप में व्यक्त करना संभव हो गया है
- json.load और json.loads में
array_hook parameter जोड़ा गया है, जो मौजूदा object_hook को पूरक करता है
array_hook=tuple और object_hook=frozendict को साथ इस्तेमाल करने पर JSON object को सीधे immutable structure में parse किया जा सकता है
json.loads('{"a": [1, 2, 3, 4]}', array_hook=tuple, object_hook=frozendict) == frozendict({'a': (1, 2, 3, 4)})
1 टिप्पणियां
Hacker News की राय
उदाहरणों में
lazy from typing import Iteratorजैसा लिखा है, तो लगा कि क्या Python में आखिरकार lazy import आ गया?शायद मैं यह बदलाव मिस कर गया, इसलिए जानना चाहता हूँ कि यह भी Python 3.15 से है या पहले के वर्ज़न में भी था
उसके लिए annotation की lazy evaluation चाहिए होगी, और जहाँ तक मुझे पता है वह डिफ़ॉल्ट रूप से ऑन नहीं है
def __getattr__(name: str) -> object:इम्प्लीमेंट करके इसका workaround किया जा सकता हैमैं व्यक्तिगत रूप से इसे लेकर काफ़ी उत्साहित हूँ। इसी हफ़्ते मैंने देखा कि सिर्फ़ एक ऐसा मॉड्यूल import जोड़ देने से, जिसे एप्लिकेशन असल में इस्तेमाल भी नहीं करती, Python process मेमोरी सीमा पार कर गया और out of memory हो गया
importस्टेटमेंट डालकर lazy import करना संभव था। वह फ़ंक्शन कॉल होने तक लाइब्रेरी import नहीं होती3.15 में
frozendictजुड़ने से अब JSON के सभी types, यानी array, boolean, floating point, null, string और object को immutable और hashable रूप में दर्शाया जा सकता हैयह आख़िरी फीचर मुझे सच में बहुत पसंद आया
Python 3.15 में iterator synchronization primitives जुड़ना अच्छा लगा: https://docs.python.org/3.15/library/threading.html#iterator...
मेरा
threaded-generatorपैकेज भी thread/process + generator + queue के साथ यही काम करता है, तो यह उसे अच्छी तरह complement करेगा: https://pypi.org/project/threaded-generator/कहा गया कि
Counterकी set operations, खासकर xor, का उपयोग सोचना मुश्किल है, लेकिन symmetric difference को देखेंhttps://en.wikipedia.org/wiki/Symmetric_difference
Counterपर लागू करने पर यह multiset symmetric difference बन जाता है, और इसकी कोई स्वाभाविक परिभाषा नहीं हैअगर मैंने प्रस्ताव सही समझा है, तो इसे हर तत्व की count के अंतर के absolute value से परिभाषित किया जा रहा है, लेकिन तब यह associative भी नहीं रहता। अगर सिर्फ parity देखें तो इसे
F_2पर addition की तरह समझा जा सकता है, जो ज़्यादा स्वाभाविक है, फिर भी व्यावहारिक उपयोग समझ नहीं आताCounterके उदाहरणों में से एक ग़लत है। मैंने 3.13 और 3.15.0a दोनों में जाँच लियाCounter(a=3, b=1) - Counter(a=1, b=2)का परिणामCounter({'a': 2})हैCounterऑब्जेक्ट्स को मिलाकर multiset बनाने के लिए कई गणितीय ऑपरेशन दिए गए हैं, और addition तथा subtraction संबंधित तत्वों की counts को जोड़ते या घटाते हैं, जबकि intersection और union क्रमशः न्यूनतम/अधिकतम count लौटाते हैंहर ऑपरेशन negative count वाले input ले सकता है, लेकिन output में जिन results की count 0 या उससे कम हो, उन्हें हटा दिया जाता है। खैर, यह वाकई एक शानदार Counter-example है ;-)
मैं 10 साल तक Python में पूरी तरह डूबा रहा और उसके साथ काम करना बहुत पसंद था, लेकिन AI codebot के बाद की दुनिया में मैंने सिर्फ़ इस साल ही 1 लाख से ज़्यादा लाइनें हटाकर तेज़ भाषाओं में migrate किया है। आजकल ज़्यादातर Go में जा रहा हूँ
एक तरीका यह हो सकता है कि पहले Python में prototype बनाया जाए और फिर convert किया जाए
अगर filtering, windowing, overlap जैसी चीज़ों वाला signal processing code लिखना हो, तो मौजूदा libraries के साथ यह आसानी से करने का लगभग कोई तरीका नहीं है
Python की आंतरिक संरचना और संचालन, खासकर free-threading के संबंध में, एक अच्छी interview है: https://alexalejandre.com/programming/interview-with-ngoldba...
आह, मेरी प्यारी Python। मैंने तुम्हें लगभग 15 साल इस्तेमाल किया। याद तो आती हो, लेकिन अब इस्तेमाल नहीं करता। यह तुम्हारी ग़लती नहीं, बस ज़िंदगी बदल गई
iterators, async functions और async iterators का अर्थ सामान्य functions से अलग होने के कारण वे decorators के साथ अच्छी तरह फिट नहीं बैठते थे। कॉल करते ही वे क्रमशः generator object, coroutine function और async generator object तुरंत लौटा देते थे, इसलिए decorator जिस पूरे lifecycle को wrap करना चाहता है, उसकी जगह वह तत्काल ही समाप्त हो जाता था
3.15 में
ContextDecoratorअब wrapped function के type की जाँच करके decorator को पूरे lifecycle पर लागू करेगा। विचार मुझे बहुत पसंद है, लेकिन opt-in mechanism के बिना मौजूदा उपयोगों के व्यवहार को हल्के से बदल देना काफ़ी जोखिम भरा लगता है। यह भले ही “spacebar heating” जैसी स्थिति हो, जहाँ समस्या तभी होगी जब किसी ने जानबूझकर पुराने टूटे हुए व्यवहार के लिए decorator इस्तेमाल किया हो, फिर भी अगर वास्तव में ऐसा हुआ है तो चीज़ें अप्रत्याशित रूप से टूट सकती हैंअक्सर यही छोटे-छोटे फीचर्स अंत में सबसे ज़्यादा उपयोगी साबित होते हैं। खासकर मैं अपने मौजूदा project में नई standard library additions आज़माना चाहता हूँ