अजगर पंडों डेटाफ्रेम, यह पास-पास-मूल्य या पास-बाय-संदर्भ है


88

यदि मैं किसी फ़ंक्शन के लिए एक डेटाफ़्रेम पास करता हूं और फ़ंक्शन के अंदर इसे संशोधित करता हूं, तो क्या यह पास-बाय-वैल्यू या पास-बाय-संदर्भ है?

मैं निम्नलिखित कोड चलाता हूं

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
def letgo(df):
    df = df.drop('b',axis=1)
letgo(a)

aफ़ंक्शन कॉल के बाद का मान नहीं बदलता है। क्या इसका मतलब यह पास-दर-मूल्य है?

मैंने निम्नलिखित प्रयास भी किया

xx = np.array([[1,2], [3,4]])
def letgo2(x):
    x[1,1] = 100
def letgo3(x):
    x = np.array([[3,3],[3,3]])

यह पता चला है letgo2()कि परिवर्तन नहीं करता है xxऔर letgo3()नहीं करता है। यह इस तरह क्यों है?


जवाबों:


95

संक्षिप्त उत्तर है, पायथन हमेशा पास-पास-मूल्य करता है, लेकिन प्रत्येक पायथन चर वास्तव में किसी वस्तु का सूचक होता है, इसलिए कभी-कभी यह पास-बाय-रेफरेंस जैसा दिखता है।

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

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

यदि आप मूल ऑब्जेक्ट (केवल संभव डेटा प्रकारों के साथ) को बदलना चाहते हैं, तो आपको ऐसा कुछ करना होगा जो स्थानीय चर के लिए पूरी तरह से नया मान निर्दिष्ट किए बिना ऑब्जेक्ट को बदल देता है । यही कारण है कि letgo()और letgo3()बाहरी आइटम अनछुए छोड़ देते हैं, लेकिन letgo2()यह बदलती जाती है।

जैसा कि @ursan ने बताया, यदि letgo()इसके बजाय ऐसा कुछ इस्तेमाल किया जाता है, तो यह मूल वस्तु को बदल देगा ( dfइंगित करेगा), जो वैश्विक aचर के माध्यम से देखे गए मूल्य को बदल देगा :

def letgo(df):
    df.drop('b', axis=1, inplace=True)

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo(a)  # will alter a

कुछ मामलों में, आप मूल चर को पूरी तरह से खोखला कर सकते हैं और इसे नए डेटा के साथ फिर से भरना कर सकते हैं, वास्तव में एक प्रत्यक्ष असाइनमेंट किए बिना, उदाहरण के लिए यह मूल वस्तु को बदल देगा जो vइंगित करता है, जो vबाद में उपयोग किए जाने पर देखे गए डेटा को बदल देगा :

def letgo3(x):
    x[:] = np.array([[3,3],[3,3]])

v = np.empty((2, 2))
letgo3(v)   # will alter v

ध्यान दें कि मैं सीधे कुछ नहीं बता रहा हूँ x; मैं संपूर्ण आंतरिक श्रेणी में कुछ निर्दिष्ट कर रहा हूं x

यदि आपको पूरी तरह से एक नई वस्तु बनानी चाहिए और इसे बाहरी रूप से दिखाई देना चाहिए (जो कि कभी-कभी पांडा के साथ होता है), तो आपके पास दो विकल्प हैं। 'स्वच्छ' विकल्प नई वस्तु को वापस करने के लिए होगा, जैसे,

def letgo(df):
    df = df.drop('b',axis=1)
    return df

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
a = letgo(a)

एक अन्य विकल्प आपके फ़ंक्शन के बाहर तक पहुंचना और सीधे एक वैश्विक चर को बदलना होगा। यह परिवर्तन aएक नई वस्तु की ओर इशारा करता है, और aबाद में संदर्भित किसी भी फ़ंक्शन को वह नई वस्तु दिखाई देगी:

def letgo():
    global a
    a = a.drop('b',axis=1)

a = pd.DataFrame({'a':[1,2], 'b':[3,4]})
letgo()   # will alter a!

प्रत्यक्ष रूप से वैश्विक चर को बदलना आमतौर पर एक बुरा विचार है, क्योंकि जो कोई भी आपके कोड को पढ़ता है, उसे यह पता लगाने में कठिन समय होगा कि कैसे aबदल गया। (मैं आम तौर पर एक स्क्रिप्ट में कई कार्यों द्वारा उपयोग किए जाने वाले साझा मापदंडों के लिए वैश्विक चर का उपयोग करता हूं, लेकिन मैं उन्हें उन वैश्विक चर को बदलने नहीं देता।)


8

@ माइक ग्रैहम के उत्तर में जोड़ने के लिए, जिन्होंने बहुत अच्छा पढ़ने का संकेत दिया:

आपके मामले में, जो याद रखना महत्वपूर्ण है, वह है नामों और मूल्यों के बीच का अंतर । a, df, xx, x, सभी कर रहे हैं नाम है, लेकिन वे उसी या अन्य का उल्लेख मूल्यों अपने उदाहरण के विभिन्न बिंदुओं पर:

  • पहले उदाहरण में, एक और मूल्य के लिए letgo रिबंड df करता है, क्योंकि df.dropएक नया रिटर्न देता है DataFrameजब तक कि आप तर्क सेट न करें inplace = True( डॉक्टर देखें )। इसका मतलब है कि नाम df( letgoफ़ंक्शन के लिए स्थानीय ), जो कि मूल्य का उल्लेख कर रहा था a, अब एक नए मूल्य का उल्लेख कर रहा है, यहां df.dropवापसी मूल्य है। मान aअभी भी मौजूद है और परिवर्तित नहीं हुआ है।

  • दूसरे उदाहरण में, letgo2 उत्परिवर्तन x , इसे बगावत किए बिना, जिसके कारण xxइसे संशोधित किया गया है letgo2। पिछले उदाहरण के विपरीत, यहां स्थानीय नाम xहमेशा उस मूल्य को संदर्भित करता xxहै जिसका नाम उल्लेख कर रहा है, और उस मूल्य को जगह में बदलता है , यही वजह है कि मान xxबदल गया है।

  • तीसरे उदाहरण में, एक नए के लिए letgo3 विद्रोह x करता है np.array। यह नाम x, स्थानीय से letgo3और पहले के मान का xxउल्लेख करने का कारण बनता है , अब दूसरे मूल्य, नए का संदर्भ देता है np.array। मान xxपरिवर्तित नहीं होने की बात कर रहा है।


7

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

http://nedbatchelder.com/text/names.html बहुत अच्छी तरह से बताता है कि पायथन के नामों की प्रणाली क्या है।


1
पायथन में पास होने और असाइन करने का शब्दार्थ जावा के समान ही है, और आपके द्वारा कहे गए समान रूप से जावा पर लागू किए जा सकते हैं। फिर भी StackOverflow पर और इंटरनेट पर कहीं और लोग स्पष्ट रूप से यह "ज्ञानवर्धक" पाते हैं कि आप पर यह प्रभाव पड़ता है कि जब भी यह मुद्दा आता है जावा हमेशा मूल्य से गुजरता है।
newacct

3

पायथन को न तो मान से पास किया जाता है और न ही संदर्भ से गुजरता है। इसे असाइनमेंट द्वारा पास किया जाता है।

सहायक संदर्भ, अजगर अक्सर पूछे जाने वाले प्रश्न: https://docs.python.org/3/faq/programming.html#how-do-i-write-a-function-with-output-parameters-call-by-reference

IOW:

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

इसलिए यदि आप एक सूची पास करते हैं, और इसके 0 मान को बदलते हैं, तो यह परिवर्तन कॉल और कॉलर दोनों में देखा जाता है। लेकिन अगर आप नई सूची के साथ सूची को फिर से असाइन करते हैं, तो यह परिवर्तन खो जाता है। लेकिन अगर आप सूची स्लाइस और की जगह है कि एक नई सूची से, कि बदलाव में दोनों को फोन किया और फोन करने वाले देखा जाता है।

ईजी:

def change_it(list_):
    # This change would be seen in the caller if we left it alone
    list_[0] = 28

    # This change is also seen in the caller, and replaces the above
    # change
    list_[:] = [1, 2]

    # This change is not seen in the caller.
    # If this were pass by reference, this change too would be seen in
    # caller.
    list_ = [3, 4]

thing = [10, 20]
change_it(thing)
# here, thing is [1, 2]

यदि आप एक सी प्रशंसक हैं, तो आप इसे एक पॉइंटर मान के रूप में पारित करने के बारे में सोच सकते हैं - एक पॉइंटर को एक मान के लिए नहीं, केवल एक पॉइंटर को एक मान के लिए।

HTH।


0

यहाँ ड्रॉप के लिए डॉक्टर है:

हटाए गए अक्ष में लेबल के साथ नई वस्तु लौटाएं।

तो एक नया डेटाफ्रेम बनाया जाता है। मूल नहीं बदला है।

लेकिन अजगर में सभी वस्तुओं के लिए, डेटा फ़्रेम को संदर्भ से फ़ंक्शन में पास किया जाता है।


लेकिन मैंने इसे dfफ़ंक्शन के अंदर सौंपा , इसका मतलब यह नहीं है कि संदर्भित मान को नई वस्तु में बदल दिया गया है?
nos

किसी स्थानीय नाम को असाइन करने से यह कभी नहीं बदलेगा कि कोई वस्तु किसी अन्य क्षेत्र में किस सीमा तक बंधी है।
माइक ग्राहम

0

आपको फ़ंक्शन के प्रारंभ में 'a' को वैश्विक बनाने की आवश्यकता है अन्यथा यह एक स्थानीय चर है और मुख्य कोड में 'a' को नहीं बदलता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.