पायथन इंटरऑपरेबिलिटी

इस परियोजना के लिए पायथन एपीआई इंटरऑपरेबिलिटी एक महत्वपूर्ण आवश्यकता है। जबकि स्विफ्ट को अन्य प्रोग्रामिंग भाषाओं (और उनके रनटाइम) के साथ एकीकृत करने के लिए डिज़ाइन किया गया है, गतिशील भाषाओं की प्रकृति को स्थिर भाषाओं का समर्थन करने के लिए आवश्यक गहन एकीकरण की आवश्यकता नहीं होती है। विशेष रूप से पायथन को अन्य अनुप्रयोगों में एम्बेड करने के लिए डिज़ाइन किया गया है और इसमें एक सरल सी इंटरफ़ेस एपीआई है। हमारे काम के प्रयोजनों के लिए, हम एक मेटा-एम्बेडिंग प्रदान कर सकते हैं, जो स्विफ्ट प्रोग्राम को पायथन एपीआई का उपयोग करने की अनुमति देता है जैसे कि वे सीधे पायथन को एम्बेड कर रहे हों।

इसे पूरा करने के लिए, स्विफ्ट स्क्रिप्ट/प्रोग्राम बस पायथन इंटरप्रेटर को अपने कोड से जोड़ता है। हमारा लक्ष्य "हम पायथन एपीआई के साथ कैसे काम करते हैं" से "हम स्विफ्ट कोड से पायथन एपीआई को प्राकृतिक, सुलभ और उन तक पहुंचने में आसान कैसे बनाएं?" के प्रश्न में बदल जाता है। यह कोई मामूली समस्या नहीं है - स्विफ्ट और पायथन के बीच महत्वपूर्ण डिज़ाइन अंतर हैं, जिसमें त्रुटि प्रबंधन के लिए उनके दृष्टिकोण, पायथन की सुपर-डायनामिक प्रकृति, दो भाषाओं के बीच सतह-स्तरीय वाक्यविन्यास में अंतर और ऐसा न करने की इच्छा शामिल है। उन चीजों से "समझौता" करें जिनकी स्विफ्ट प्रोग्रामर अपेक्षा करते आए हैं। हम सुविधा और एर्गोनॉमिक्स की भी परवाह करते हैं और सोचते हैं कि SWIG जैसे रैपर जनरेटर की आवश्यकता अस्वीकार्य है।

समग्र दृष्टिकोण

हमारा समग्र दृष्टिकोण इस अवलोकन पर आधारित है कि पायथन दृढ़ता से टाइप किया गया है लेकिन - अधिकांश गतिशील रूप से टाइप की गई भाषाओं की तरह - इसकी प्रकार प्रणाली रनटाइम पर लागू होती है। हालाँकि इसके शीर्ष पर एक स्थिर प्रकार की प्रणाली को फिर से स्थापित करने के कई प्रयास किए गए हैं (उदाहरण के लिए mypy , pytype और अन्य ), वे बेकार प्रकार की प्रणालियों पर भरोसा करते हैं, इसलिए वे एक पूर्ण समाधान नहीं हैं जिस पर हम भरोसा कर सकते हैं, और इसके अलावा वे कई के खिलाफ कट करते हैं डिज़ाइन परिसर जो पायथन और उसके पुस्तकालयों को वास्तव में महान बनाता है।

बहुत से लोग स्विफ्ट को एक स्थिर रूप से टाइप की गई भाषा के रूप में देखते हैं और इसलिए इस निष्कर्ष पर पहुंचते हैं कि सही समाधान पायथन के तरल रूप को एक स्थिर रूप से परिभाषित छेद में डालना है। हालाँकि, दूसरों को एहसास है कि स्विफ्ट एक शक्तिशाली स्थिर प्रकार प्रणाली के लाभों को एक (अक्सर कम सराहना की गई!) गतिशील प्रकार प्रणाली के साथ जोड़ती है। पाइथन की गतिशील प्रकार प्रणाली को कुछ ऐसा करने के लिए मजबूर करने का प्रयास करने के बजाय, हम पाइथन से मिलना चुनते हैं जहां यह है और इसके गतिशील रूप से टाइप किए गए दृष्टिकोण को पूरी तरह से अपनाना है।

इसका अंतिम परिणाम यह है कि हम एक बहुत ही प्राकृतिक पायथन अनुभव प्राप्त कर सकते हैं - सीधे स्विफ्ट कोड में। यह कैसा दिखता है इसका एक उदाहरण यहां दिया गया है; टिप्पणी किया गया कोड तुलना के लिए शुद्ध-पायथन सिंटैक्स दिखाता है:

import PythonKit

// Python:
//    import numpy as np
//    a = np.arange(15).reshape(3, 5)
//    b = np.array([6, 7, 8])
let np = Python.import("numpy")
let a = np.arange(15).reshape(3, 5)
let b = np.array([6, 7, 8])

// Python:
//    import gzip as gzip
//    import pickle as pickle
let gzip = Python.import("gzip")
let pickle = Python.import("pickle")

// Python:
//    file = gzip.open("mnist.pkl.gz", "rb")
//    (images, labels) = pickle.load(file)
//    print(images.shape)  # (50000, 784)
let file = gzip.open("mnist.pkl.gz", "rb")
let (images, labels) = pickle.load(file).tuple2
print(images.shape) // (50000, 784)

जैसा कि आप देख सकते हैं, यहां सिंटैक्स एक पायथन प्रोग्रामर के लिए तुरंत समझ में आता है: प्रमुख अंतर यह है कि स्विफ्ट को उपयोग से पहले मूल्यों को घोषित करने की आवश्यकता होती है ( let या var के साथ) और हमने import , type , slice जैसे पायथन अंतर्निहित कार्यों को रखना चुना है आदि एक Python. नेमस्पेस (केवल वैश्विक दायरे को अव्यवस्थित करने से बचने के लिए)। यह स्विफ्ट भाषा के वैश्विक डिज़ाइन से समझौता न करते हुए, पायथन को प्राकृतिक और परिचित महसूस कराने की कोशिश के बीच एक सचेत संतुलन का परिणाम है।

यह लाइन एक साधारण आवश्यकता के माध्यम से स्थापित की गई है: हमें पायथन इंटरऑप प्राप्त करने के लिए किसी भी पायथन-विशिष्ट कंपाइलर या भाषा सुविधाओं पर निर्भर नहीं होना चाहिए - इसे पूरी तरह से स्विफ्ट लाइब्रेरी के रूप में कार्यान्वित किया जाना चाहिए। आख़िरकार, जबकि पायथन मशीन लर्निंग समुदाय के लिए अविश्वसनीय रूप से महत्वपूर्ण है, अन्य गतिशील भाषाएँ (जावास्क्रिप्ट, रूबी, आदि) हैं जिनकी अन्य डोमेन में मजबूत पकड़ है, और हम नहीं चाहते कि इनमें से प्रत्येक डोमेन एक अंतहीन जटिलता रेंगना थोपे। स्विफ्ट भाषा पर.

आप हमारी ब्रिजिंग परत के वर्तमान कार्यान्वयन को Python.swift में देख सकते हैं। यह शुद्ध स्विफ्ट कोड है जो असंशोधित स्विफ्ट के साथ काम करता है।

इस दृष्टिकोण की सीमाएँ

चूँकि हम स्विफ्ट में पायथन की गतिशील प्रकृति को अपनाने का विकल्प चुनते हैं, इसलिए हमें गतिशील भाषाओं के फायदे और नुकसान दोनों मिलते हैं। विशेष रूप से, कई स्विफ्ट प्रोग्रामर अद्भुत कोड पूर्णता की उम्मीद करते हैं और उस पर निर्भर रहते हैं और संकलन के समय कंपाइलर द्वारा उनके लिए टाइपो और अन्य तुच्छ बग पकड़ने की सुविधा की सराहना करते हैं। इसके विपरीत, पायथन प्रोग्रामर्स के पास ये सुविधाएं नहीं हैं (इसके बजाय, बग आमतौर पर रनटाइम पर पकड़े जाते हैं), और क्योंकि हम पायथन की गतिशील प्रकृति को अपना रहे हैं, स्विफ्ट में पायथन एपीआई उसी तरह काम करते हैं।

स्विफ्ट समुदाय के साथ सावधानीपूर्वक विचार करने के बाद, यह स्पष्ट हो गया कि यह एक संतुलन है: स्विफ्ट के दर्शन और मूल्य प्रणाली को पायथन लाइब्रेरी पारिस्थितिकी तंत्र पर कितना प्रक्षेपित किया जा सकता है... उन चीजों को तोड़े बिना जो पायथन के बारे में सच और सुंदर हैं और इसके पुस्तकालय? अंत में, हमने निष्कर्ष निकाला कि पायथन-केंद्रित मॉडल सबसे अच्छा समझौता है: हमें इस तथ्य को स्वीकार करना चाहिए कि पायथन एक गतिशील भाषा है, इसमें स्थिर संकलन समय पर कभी भी सही कोड पूर्णता और त्रुटि का पता नहीं चल सकता है।

यह काम किस प्रकार करता है

हम Python के डायनेमिक टाइप सिस्टम को PythonObject नामक एकल स्थिर स्विफ्ट प्रकार में मैप करते हैं, और PythonObject रनटाइम पर किसी भी डायनेमिक Python मान को लेने की अनुमति देते हैं ( अबादी एट अल के दृष्टिकोण के समान)। PythonObject सीधे Python C बाइंडिंग में प्रयुक्त PyObject* से मेल खाता है, और Python मान Python में कुछ भी कर सकता है। उदाहरण के लिए, यह ठीक वैसे ही काम करता है जैसे आप Python में अपेक्षा करते हैं:

var x: PythonObject = 42  // x is an integer represented as a Python value.
print(x + 4)         // Does a Python addition, then prints 46.

x = "stringy now"    // Python values can hold strings, and dynamically change Python type!
print("super " + x)  // Does a Python addition, then prints "super stringy now".

क्योंकि हम स्विफ्ट के वैश्विक डिज़ाइन से समझौता नहीं करना चाहते हैं, हम सभी पायथन व्यवहार को इस PythonObject प्रकार से जुड़े अभिव्यक्तियों तक सीमित रखते हैं। यह सुनिश्चित करता है कि सामान्य स्विफ्ट कोड का शब्दार्थ अपरिवर्तित रहता है, भले ही यह पायथन मूल्यों के साथ मिश्रण, मिलान, इंटरफेसिंग और इंटरमीलिंग हो।

बुनियादी अंतरसंचालनीयता

स्विफ्ट 4.0 के अनुसार, मौजूदा भाषा सुविधाओं के माध्यम से बुनियादी अंतरसंचालनीयता का एक उचित स्तर पहले से ही सीधे प्राप्त किया जा सकता था: हम बस PythonObject एक स्विफ्ट संरचना के रूप में परिभाषित करते हैं जो एक निजी स्विफ्ट PyReference वर्ग को लपेटता है, जिससे स्विफ्ट को पायथन संदर्भ गणना की जिम्मेदारी लेने की अनुमति मिलती है:

/// Primitive reference to a Python value.  This is always non-null and always
/// owning of the underlying value.
private final class PyReference {
  var state: UnsafeMutablePointer<PyObject>

  init(owned: UnsafeMutablePointer<PyObject>) {
    state = owned
  }

  init(borrowed: UnsafeMutablePointer<PyObject>) {
    state = borrowed
    Py_IncRef(state)
  }

  deinit {
    Py_DecRef(state)
  }
}

// This is the main type users work with.
public struct PythonObject {
  /// This is a handle to the Python object the PythonObject represents.
  fileprivate var state: PyReference
  ...
}

इसी तरह, हम मौजूदा पायथन रनटाइम इंटरफ़ेस के संदर्भ में PythonObject पर func + (और बाकी समर्थित पायथन ऑपरेटरों) को लागू कर सकते हैं। हमारा कार्यान्वयन इस तरह दिखता है:

// Implement the + operator in terms of the standard Python __add__ method.
public static func + (lhs: PythonObject, rhs: PythonObject) -> PythonObject {
  return lhs.__add__.call(with: rhs)
}
// Implement the - operator in terms of the standard Python __sub__ method.
public static func - (lhs: PythonObject, rhs: PythonObject) -> PythonObject {
  return lhs.__sub__.call(with: rhs)
}
// Implement += and -= in terms of + and -, as usual.
public static func += (lhs: inout PythonObject, rhs: PythonObject) {
  lhs = lhs + rhs
}
public static func -= (lhs: inout PythonObject, rhs: PythonObject) {
  lhs = lhs - rhs
}
// etc...

हम PythonObject Sequence और अन्य प्रोटोकॉल के अनुरूप भी बनाते हैं, जिससे इस तरह के कोड को काम करने की अनुमति मिलती है:

func printPythonCollection(_ collection: PythonObject) {
  for elt in collection {
    print(elt)
  }
}

इसके अलावा, क्योंकि PythonObject MutableCollection के अनुरूप है, आपको Collections के लिए स्विफ्ट एपीआई तक पूरी पहुंच मिलती है, जिसमें map , filter , sort इत्यादि जैसे फ़ंक्शन शामिल हैं।

स्विफ्ट मानों से और उनमें रूपांतरण

अब जब स्विफ्ट पायथन मूल्यों का प्रतिनिधित्व और संचालन कर सकती है, तो स्विफ्ट मूल प्रकारों जैसे Int और Array<Float> और पायथन समकक्षों के बीच परिवर्तित करने में सक्षम होना महत्वपूर्ण हो जाता है। इसे PythonConvertible प्रोटोकॉल द्वारा नियंत्रित किया जाता है - जिसके लिए Int जैसे बुनियादी स्विफ्ट प्रकार अनुरूप होते हैं, और Array और Dictionary जैसे स्विफ्ट संग्रह प्रकार सशर्त रूप से अनुरूप होते हैं (जब उनके तत्व अनुरूप होते हैं)। इससे रूपांतरण स्वाभाविक रूप से स्विफ्ट मॉडल में फिट हो जाते हैं।

उदाहरण के लिए, यदि आप जानते हैं कि आपको स्विफ्ट पूर्णांक की आवश्यकता है या आप स्विफ्ट पूर्णांक को पायथन में परिवर्तित करना चाहते हैं, तो आप इसका उपयोग कर सकते हैं:

let pyInt = PythonObject(someSwiftInteger)     // Always succeeds.
if let swiftInt = Int(somePythonValue) {  // Succeeds if the Python value is convertible to Int.
  print(swiftInt)
}

इसी प्रकार, सारणी जैसे समुच्चय प्रकार बिल्कुल उसी तरह काम करते हैं:

// This succeeds when somePythonValue is a collection of values that are convertible to Int.
if let swiftIntArray = Array<Int>(somePythonValue) {
  print(swiftIntArray)
}

यह बिल्कुल उस मॉडल में फिट बैठता है जिसकी एक स्विफ्ट प्रोग्रामर अपेक्षा करता है: असफल रूपांतरणों को वैकल्पिक परिणामों में प्रक्षेपित किया जाता है (जैसे "स्ट्रिंग टू इंट" रूपांतरण होते हैं), सुरक्षा और पूर्वानुमान प्रदान करते हैं जिसकी स्विफ्ट प्रोग्रामर अपेक्षा करते हैं।

अंत में, क्योंकि आपके पास Python की पूरी शक्ति तक पहुंच है, Python की सभी सामान्य परावर्तक क्षमताएं भी सीधे उपलब्ध हैं, जिनमें Python.type , Python.id , Python.dir और Python inspect मॉड्यूल शामिल हैं।

अंतरसंचालनीयता चुनौतियाँ

उपरोक्त समर्थन संभव है क्योंकि स्विफ्ट के डिज़ाइन का उद्देश्य लाइब्रेरी-स्तरीय वाक्यविन्यास विस्तार के लक्ष्य की सराहना करना है। हम भी भाग्यशाली हैं कि पायथन और स्विफ्ट अभिव्यक्ति (ऑपरेटर और फ़ंक्शन/विधि कॉल) के लिए एक समान सतह-स्तरीय वाक्यविन्यास साझा करते हैं। जैसा कि कहा गया है, स्विफ्ट 4.0 की सिंटैक्स एक्स्टेंसिबिलिटी की सीमाओं और जानबूझकर डिज़ाइन के अंतर के कारण हमें कुछ चुनौतियों का सामना करना पड़ा, जिन्हें हमें दूर करने की आवश्यकता है।

गतिशील सदस्य लुकअप

हालाँकि स्विफ्ट एक आम तौर पर एक्स्टेंसिबल भाषा है, आदिम सदस्य लुकअप एक लाइब्रेरी-एक्स्टेंसिबल सुविधा नहीं थी। विशेष रूप से, फॉर्म xy की अभिव्यक्ति को देखते हुए, x का प्रकार यह नियंत्रित करने में असमर्थ था कि जब सदस्य y उस पर एक्सेस किया गया तो क्या हुआ। यदि x के प्रकार ने स्थिर रूप से y नाम के सदस्य को घोषित कर दिया है तो यह अभिव्यक्ति हल हो जाएगी, अन्यथा इसे कंपाइलर द्वारा अस्वीकार कर दिया जाएगा।

स्विफ्ट की बाधाओं के भीतर, हमने एक बाइंडिंग बनाई जो इसके आसपास काम करती थी। उदाहरण के लिए, Python के PyObject_GetAttrString और PyObject_SetAttrString के संदर्भ में सदस्य पहुंच को लागू करना सीधा था। इसने कोड की अनुमति दी जैसे:

// Python: a.x = a.x + 1
a.set(member: "x", to: a.get(member: "x") + 1)

हालाँकि, हम सभी शायद इस बात से सहमत हो सकते हैं कि यह पायथन मूल्यों के साथ काम करने के लिए एक प्राकृतिक और एर्गोनोमिक इंटरफ़ेस प्रदान करने के हमारे लक्ष्य को प्राप्त नहीं करता है! इसके अलावा, यह स्विफ्ट एल-वैल्यू के साथ काम करने के लिए कोई खर्चा प्रदान नहीं करता है: ax += 1 के समतुल्य वर्तनी का कोई तरीका नहीं है। ये दोनों समस्याएँ मिलकर एक महत्वपूर्ण अभिव्यंजना अंतर थीं।

स्विफ्ट समुदाय के साथ चर्चा के बाद, इस समस्या का समाधान विफल सदस्य लुकअप को संभालने के लिए लाइब्रेरी कोड को फ़ॉलबैक हुक लागू करने की अनुमति देना है। यह सुविधा ऑब्जेक्टिव-सी सहित कई गतिशील भाषाओं में मौजूद है, और इस तरह, हमने SE-0195 को प्रस्तावित और कार्यान्वित किया: उपयोगकर्ता-परिभाषित "डायनामिक सदस्य लुकअप" प्रकारों का परिचय दें जो एक स्थिर प्रकार को अनसुलझे लुकअप के लिए फ़ॉलबैक हैंडलर प्रदान करने की अनुमति देता है। इस प्रस्ताव पर स्विफ्ट समुदाय द्वारा स्विफ्ट इवोल्यूशन प्रक्रिया के माध्यम से विस्तार से चर्चा की गई और अंततः इसे स्वीकार कर लिया गया। स्विफ्ट 4.1 के बाद से इसकी शिपिंग की जा रही है।

इसके परिणामस्वरूप, हमारी इंटरऑपरेबिलिटी लाइब्रेरी निम्नलिखित हुक को लागू करने में सक्षम है:

@dynamicMemberLookup
public struct PythonObject {
...
  subscript(dynamicMember member: String) -> PythonObject {
    get {
      return ... PyObject_GetAttrString(...) ...
    }
    set {
      ... PyObject_SetAttrString(...)
    }
  }
}

जो उपरोक्त कोड को सरलता से इस प्रकार व्यक्त करने की अनुमति देता है:

// Python: a.x = a.x + 1
a.x = a.x + 1

... और प्राकृतिक ax += 1 सिंटैक्स बिल्कुल वैसे ही काम करता है जैसे हम उम्मीद करते हैं। यह किसी लक्ष्य को प्राप्त करने के लिए किसी भाषा, उसके पुस्तकालयों और अनुप्रयोगों के पूर्ण स्टैक को एक साथ विकसित करने में सक्षम होने का बड़ा लाभ दिखाता है।

गतिशील रूप से कॉल करने योग्य प्रकार

सदस्य लुकअप के अलावा, जब कॉलिंग मूल्यों की बात आती है तो हमारे सामने एक समान चुनौती होती है। गतिशील भाषाओं में अक्सर "कॉल करने योग्य" मानों की धारणा होती है, जो एक मनमाना हस्ताक्षर ले सकता है, लेकिन स्विफ्ट 4.1 में ऐसी चीज़ के लिए कोई समर्थन नहीं है। उदाहरण के लिए, स्विफ्ट 4.1 के अनुसार, हमारी इंटरऑपरेबिलिटी लाइब्रेरी इस तरह के इंटरफ़ेस के माध्यम से पायथन एपीआई के साथ काम करने में सक्षम है:

// Python: a = np.arange(15).reshape(3, 5)
let a = np.arange.call(with: 15).reshape.call(with: 3, 5)

// Python: d = np.array([1, 2, 3], dtype="i2")
let d = np.array.call(with: [6, 7, 8], kwargs: [("dtype", "i2")])

हालाँकि इसके साथ काम पूरा करना संभव है, लेकिन यह स्पष्ट रूप से सुविधा और एर्गोनॉमिक्स के हमारे लक्ष्य को प्राप्त नहीं कर रहा है।

स्विफ्ट समुदाय और #2 के साथ इस समस्या का मूल्यांकन करते हुए, हम देखते हैं कि पायथन और स्विफ्ट नामित और अनाम दोनों तर्कों का समर्थन करते हैं: नामित तर्क एक शब्दकोश के रूप में पारित किए जाते हैं। उसी समय, स्मॉलटॉक-व्युत्पन्न भाषाएँ एक अतिरिक्त झुर्रियाँ जोड़ती हैं: विधि संदर्भ परमाणु इकाई हैं, जिसमें किसी भी कीवर्ड तर्क के साथ विधि का आधार नाम शामिल होता है। हालाँकि भाषा की इस शैली के साथ इंटरऑपरेबिलिटी पायथन के लिए महत्वपूर्ण नहीं है, हम यह सुनिश्चित करना चाहते हैं कि स्विफ्ट को एक कोने में चित्रित नहीं किया गया है जो रूबी, स्क्वीक और अन्य स्मॉलटॉक-व्युत्पन्न भाषाओं के साथ महान इंटरऑप को रोकता है।

हमारा समाधान, जिसे स्विफ्ट 5 में लागू किया गया था , यह इंगित करने के लिए एक नई @dynamicCallable विशेषता पेश करना है कि एक प्रकार (जैसे PythonObject ) गतिशील कॉल रिज़ॉल्यूशन को संभाल सकता है। @dynamicCallable सुविधा को PythonKit इंटरऑप मॉड्यूल में लागू और उपलब्ध कराया गया है।

// Python: a = np.arange(15).reshape(3, 5)
let a = np.arange(15).reshape(3, 5)

// Python: d = np.array([1, 2, 3], dtype="i2")
let d = np.array([6, 7, 8], dtype: "i2")

हमारा मानना ​​है कि यह काफी सम्मोहक है, और इन मामलों के लिए मौजूद शेष अभिव्यंजना और एर्गोनोमिक अंतर को बंद कर देता है। हमारा मानना ​​है कि यह सुविधा रूबी, स्क्वीक और अन्य गतिशील भाषाओं के लिए एक अच्छा समाधान होगी, साथ ही यह आम तौर पर उपयोगी स्विफ्ट भाषा सुविधा होगी जो अन्य स्विफ्ट पुस्तकालयों पर लागू हो सकती है।

अपवाद प्रबंधन बनाम त्रुटि प्रबंधन

अपवाद प्रबंधन के लिए पायथन का दृष्टिकोण C++ और कई अन्य भाषाओं के समान है, जहां कोई भी अभिव्यक्ति किसी भी समय अपवाद फेंक सकती है, और कॉल करने वाले उन्हें स्वतंत्र रूप से संभालना (या नहीं) चुन सकते हैं। इसके विपरीत, स्विफ्ट का त्रुटि प्रबंधन दृष्टिकोण "थ्रोएबिलिटी" को एक विधि के एपीआई अनुबंध का एक स्पष्ट हिस्सा बनाता है और कॉल करने वालों को यह संभालने (या कम से कम स्वीकार करने) के लिए मजबूर करता है कि एक त्रुटि फेंकी जा सकती है।

यह दो भाषाओं के बीच एक अंतर्निहित अंतर है, और हम इस अंतर को भाषा विस्तार के साथ खत्म नहीं करना चाहते हैं। इसके लिए हमारा वर्तमान समाधान इस अवलोकन पर आधारित है कि भले ही कोई भी फ़ंक्शन कॉल विफल हो सकती है , अधिकांश कॉल ऐसा नहीं करती हैं। इसके अलावा, यह देखते हुए कि स्विफ्ट भाषा में त्रुटि प्रबंधन को स्पष्ट करती है, पाइथॉन-इन-स्विफ्ट प्रोग्रामर के लिए यह भी सोचना उचित है कि वे त्रुटियों को फेंकने योग्य और पकड़ने योग्य कहां होने की उम्मीद करते हैं। हम इसे PythonObject पर एक स्पष्ट .throwing प्रक्षेपण के साथ करते हैं। यहाँ एक उदाहरण है:

  // Open a file.  If this fails, the program is terminated, just like an
  // unhandled exception in Python.

  // file = open("foo.txt")
  let file = Python.open("foo.txt")
  // blob = file.read()
  let blob = file.read()

  // Open a file, a thrown "file not found" exception is turned into a Swift error.
  do {
    let file = try Python.open.throwing.dynamicallyCall("foo.txt")
    let blob = file.read()
    ...
  } catch {
    print(error)
  }

और निश्चित रूप से, यह स्विफ्ट त्रुटि प्रबंधन द्वारा प्रदान किए गए सभी सामान्य यांत्रिकी के साथ एकीकृत होता है, जिसमें try? यदि आप त्रुटि को संभालना चाहते हैं लेकिन अपवाद में शामिल विवरणों की परवाह नहीं करते हैं।

वर्तमान कार्यान्वयन और स्थिति

जैसा कि ऊपर उल्लेख किया गया है, पायथन इंटरऑपरेबिलिटी लाइब्रेरी का हमारा वर्तमान कार्यान्वयन GitHub पर Python.swift फ़ाइल में उपलब्ध है। व्यवहार में, हमने पाया है कि यह कई उपयोग के मामलों में अच्छी तरह से काम करता है। हालाँकि, कुछ चीजें गायब हैं जिनका हमें विकास जारी रखने और पता लगाने की आवश्यकता है:

स्विफ्ट के स्लाइसिंग सिंटैक्स की तुलना में पायथन स्लाइसिंग अधिक सामान्य है। अभी आप Python.slice(a, b, c) फ़ंक्शन के माध्यम से इसका पूर्ण एक्सेस प्राप्त कर सकते हैं। हालाँकि, हमें स्विफ्ट से सामान्य a...b रेंज सिंटैक्स को जोड़ना चाहिए, और उस मूल रेंज सिंटैक्स के विस्तार के रूप में स्ट्राइडिंग ऑपरेटरों को लागू करने पर विचार करना दिलचस्प हो सकता है। हमें पायथन कक्षाओं के उपवर्गीकरण के लिए उपयोग करने के लिए सही मॉडल की जांच और निर्णय लेने की आवश्यकता है। वर्तमान में PythonObject जैसी संरचना को टपल पैटर्न मिलान के साथ काम करने का कोई तरीका नहीं है, इसलिए हम .tuple2 जैसे प्रक्षेपण गुणों का उपयोग करते हैं। यदि यह व्यवहार में एक समस्या बन जाती है, तो हम इसे स्विफ्ट में जोड़कर जांच कर सकते हैं, लेकिन वर्तमान में हमें नहीं लगता कि यह निकट अवधि में हल करने लायक पर्याप्त समस्या होगी।

सार और निष्कर्ष

हम इस दिशा के बारे में अच्छा महसूस करते हैं और सोचते हैं कि इस काम के कई दिलचस्प पहलू हैं: यह बहुत अच्छा है कि स्विफ्ट कंपाइलर या भाषा में कोई पायथन विशिष्ट परिवर्तन नहीं हैं। हम पायथन-स्वतंत्र भाषा सुविधाओं की रचना करके स्विफ्ट में लिखी लाइब्रेरी के माध्यम से अच्छी पायथन इंटरऑपरेबिलिटी प्राप्त करने में सक्षम हैं। हमारा मानना ​​है कि अन्य समुदाय गतिशील भाषाओं (और उनके रनटाइम) के साथ सीधे एकीकृत होने के लिए समान फीचर सेट की रचना करने में सक्षम होंगे जो अन्य समुदायों (जैसे जावास्क्रिप्ट, रूबी, आदि) के लिए महत्वपूर्ण हैं।

इस काम का एक और दिलचस्प पहलू यह है कि पायथन समर्थन अन्य TensorFlow और स्वचालित भेदभाव तर्क से पूरी तरह से स्वतंत्र है जिसे हम TensorFlow के लिए स्विफ्ट के हिस्से के रूप में बना रहे हैं। यह स्विफ्ट इकोसिस्टम के लिए आम तौर पर उपयोगी एक्सटेंशन है जो अकेले खड़ा हो सकता है, सर्वर साइड डेवलपमेंट या किसी अन्य चीज के लिए उपयोगी है जो मौजूदा पायथन एपीआई के साथ इंटरऑपरेट करना चाहता है।