क्या अजगर में परिवर्तन करना संभव है जो बाहरी है, लेकिन वैश्विक नहीं है, गुंजाइश है?


107

निम्नलिखित कोड दिए गए:

def A() :
    b = 1

    def B() :
        # I can access 'b' from here.
        print( b )
        # But can i modify 'b' here? 'global' and assignment will not work.

    B()
A()

B()फंक्शन चर में कोड bबाहरी दायरे में है, लेकिन वैश्विक दायरे में नहीं है। क्या फ़ंक्शन के bभीतर से चर को संशोधित करना संभव है B()? निश्चित रूप से मैं इसे यहां से पढ़ सकता हूं और print(), लेकिन इसे कैसे संशोधित करूं?


क्षमा करें, निश्चित रूप से 2.7 :)। अजगर के लिए 3 स्कूपिंग नियम बदल गए हैं।
— grigoryvp

आप तब तक हो सकते हैं जब तक bआपस में मिलते हैं। bबाहरी दायरे को पूरा करने के लिए एक असाइनमेंट ।
— 15

4
यह पायथन की शर्मिंदगी में से एक है nonlocalजिसे 2.x पर वापस नहीं लाया गया है। यह बंद समर्थन का एक आंतरिक हिस्सा है।
— ग्लेन मेनार्ड

जवाबों:


96

पायथन 3.x में nonlocalकीवर्ड है । मुझे लगता है कि यह वही करता है जो आप चाहते हैं, लेकिन मुझे यकीन नहीं है कि आप अजगर 2 या 3 चला रहे हैं।

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

अजगर 2 के लिए, मैं आमतौर पर एक उत्परिवर्तित वस्तु (जैसे एक सूची, या तानाशाही) का उपयोग करता हूं, और पुन: असाइन करने के बजाय मूल्य को म्यूट करता हूं।

उदाहरण:

def foo():
    a = []
    def bar():
        a.append(1)
    bar()
    bar()
    print a

foo()

आउटपुट:

[1, 1]

16
ऐसा करने का एक अच्छा तरीका class nonlocal: passबाहरी दायरे में है। फिर nonlocal.xआंतरिक दायरे में सौंपा जा सकता है।
— किन्नर

1
अब तक, मेरे पास पहले से ही दो अजगर युक्तियां हैं जो सरल हैं, लेकिन बहुत उपयोगी हैं: आपका दूसरा एक है :) धन्यवाद @kindall!
— स्वदेव

@kindall एक महान हैक है - यह पायथन 3 सिंटैक्स से न्यूनतम रूप से भिन्न है और एक उत्परिवर्तित वस्तु के चारों ओर से गुजरने की तुलना में बहुत अधिक पठनीय है।
— dimo414

2
@kindall बहुत साफ धन्यवाद ढेर :) शायद एक अलग नाम की जरूरत है, क्योंकि यह आगे संगतता को तोड़ता है। अजगर 3 में यह एक कीवर्ड संघर्ष है और इसका कारण होगा SyntaxError। शायद NonLocal?
— एडम टेरी

या, चूंकि यह तकनीकी रूप से एक वर्ग है Nonlocal,? :-)
— टाइप करें

19

आप अस्थायी स्कोप रखने के लिए एक खाली वर्ग का उपयोग कर सकते हैं। यह म्यूटेबल जैसा है लेकिन थोड़ा प्रेट्रियर है।

def outer_fn():
   class FnScope:
     b = 5
     c = 6
   def inner_fn():
      FnScope.b += 1
      FnScope.c += FnScope.b
   inner_fn()
   inner_fn()
   inner_fn()

यह निम्न इंटरैक्टिव आउटपुट देता है:

>>> outer_fn()
8 27
>>> fs = FnScope()
NameError: name 'FnScope' is not defined

यह अजीब है कि इसके खेतों के साथ वर्ग एक आंतरिक फ़ंक्शन में "दृश्यमान" है लेकिन चर तब तक नहीं होते हैं, जब तक कि आप "नॉनक्लोकल" कीवर्ड के साथ बाहरी चर को परिभाषित नहीं करते हैं।
— Celdor

12

मैं पायथन के लिए थोड़ा नया हूं, लेकिन मैंने इस बारे में थोड़ा पढ़ा है। मेरा मानना ​​है कि आप जो सबसे अच्छा काम करने जा रहे हैं, वह जावा वर्क-अराउंड के समान है, जो आपके बाहरी वेरिएबल को एक सूची में लपेटना है।

def A():
   b = [1]
   def B():
      b[0] = 2
   B()
   print(b[0])

# The output is '2'

संपादित करें: मुझे लगता है कि यह शायद पायथन 3 से पहले सच था। ऐसा लगता है nonlocalकि आपका जवाब है।


4

नहीं, आप कम से कम इस तरह से नहीं कर सकते।

क्योंकि "सेट ऑपरेशन" मौजूदा दायरे में एक नया नाम बनाएगा, जो बाहरी हिस्से को कवर करता है।


"जो बाहरी को कवर करते हैं" आपका क्या मतलब है? नेस्टेड फ़ंक्शन में नाम बी के साथ एक ऑब्जेक्ट को परिभाषित करना इस फ़ंक्शन के बाहरी स्थान में समान नाम के साथ किसी ऑब्जेक्ट पर कोई प्रभाव नहीं है
— आंखों की रोशनी

1
@ पूरी तरह से, जहां भी असाइनमेंट स्टेटमेंट है, वह पूरे वर्तमान दायरे में नाम को पेश करेगा। जैसे कि प्रश्न का सैंपल कोड, अगर यह है: डिफ सी (): प्रिंट (बी) बी = 2 "बी = 2" पूरे सी फंक स्कोप में बी नाम देगा, इसलिए जब प्रिंट (बी), यह होगा स्थानीय सी फ़नल स्कोप में b प्राप्त करने का प्रयास करें, लेकिन बाहरी नहीं, स्थानीय b अभी तक इनिशियलाइज़ नहीं हुआ है, इसलिए कोई त्रुटि होगी।
— zchenah

1

किसी को भी बाद में एक सुरक्षित लेकिन भारी वर्कअराउंड को देखने के लिए। मापदंडों के रूप में चर को पारित करने की आवश्यकता के बिना।

def outer():
    a = [1]
    def inner(a=a):
        a[0] += 1
    inner()
    return a[0]

1

संक्षिप्त उत्तर जो सिर्फ स्वचालित रूप से काम करेगा

मैंने इस विशिष्ट समस्या को हल करने के लिए एक अजगर पुस्तकालय बनाया। यह अनैतिकता के तहत जारी किया गया है, इसलिए इसका उपयोग करें लेकिन आप चाहें। आप इसे यहां स्थापित कर सकते हैं pip install seapieया होम पेज https://github.com/hirsimaki-markus/SEAPIE पर देख सकते हैं

user@pc:home$ pip install seapie

from seapie import Seapie as seapie
def A():
    b = 1

    def B():
        seapie(1, "b=2")
        print(b)

    B()
A()

आउटपुट

2

तर्कों के निम्नलिखित अर्थ हैं:

  • पहला तर्क निष्पादन क्षेत्र है। 0 का मतलब होगा स्थानीय B(), 1 का अर्थ है माता A()- पिता और 2 का मतलब होगा दादा-दादी <module>उर्फ ग्लोबल
  • दूसरा तर्क एक स्ट्रिंग या कोड ऑब्जेक्ट है जिसे आप दिए गए दायरे में निष्पादित करना चाहते हैं
  • आप इसे अपने प्रोग्राम के अंदर इंटरैक्टिव शेल के लिए तर्कों के बिना भी कह सकते हैं

लंबा जवाब

यह अधिक जटिल है। CPython api का उपयोग करके कॉल स्टैक में फ़्रेम को संपादित करके सेपी काम करता है। CPython de वास्तविक मानक है, इसलिए अधिकांश लोगों को इसके बारे में चिंता करने की ज़रूरत नहीं है।

यदि आप इसे पढ़ रहे हैं तो संभवत: जादू के शब्द आप सबसे अधिक इंटरसेप्टेड हैं:

frame = sys._getframe(1)          # 1 stands for previous frame
parent_locals = frame.f_locals    # true dictionary of parent locals
parent_globals = frame.f_globals  # true dictionary of parent globals

exec(codeblock, parent_globals, parent_locals)

ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),ctypes.c_int(1))
# the magic value 1 stands for ability to introduce new variables. 0 for update-only

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

  • Assingn, import और अपनी वस्तुओं को पहले से परिभाषित करें
  • प्लेसहोल्डर को पहले से ही अपनी वस्तुओं में स्थान देना
  • प्रतीक तालिका को अद्यतन करने के लिए मुख्य कार्यक्रम में स्वयं को पुन: असाइन करें: x = स्थानीय () ["x"]
  • अनुकूलन से बचने के लिए सीधे कॉल करने के बजाय मुख्य प्रोग्राम में निष्पादन () का उपयोग करें। कॉलिंग x के बजाय करें: निष्पादित करें ("x")

यदि आप महसूस कर रहे हैं कि प्रयोग exec()कुछ ऐसा नहीं है जिसे आप अपने साथ रखना चाहते हैं, तो सही स्थानीय शब्दकोश को अपडेट करके व्यवहार का अनुकरण कर सकते हैं (स्थानीय लोगों द्वारा वापस नहीं किया गया) ()। मैं https://faster-cpython.readthedocs.io/mutable.html से एक उदाहरण की नकल करूंगा

import sys
import ctypes

def hack():
    # Get the frame object of the caller
    frame = sys._getframe(1)
    frame.f_locals['x'] = "hack!"
    # Force an update of locals array from locals dict
    ctypes.pythonapi.PyFrame_LocalsToFast(ctypes.py_object(frame),
                                          ctypes.c_int(0))

def func():
    x = 1
    hack()
    print(x)

func()

आउटपुट:

hack!

0

मुझे नहीं लगता कि आपको ऐसा करना चाहिए । कार्य जो उनके घेरने के संदर्भ में चीजों को बदल सकते हैं खतरनाक हैं, क्योंकि यह संदर्भ फ़ंक्शन के ज्ञान के बिना लिखा जा सकता है।

आप इसे स्पष्ट कर सकते हैं, या तो बी को एक सार्वजनिक विधि बना सकते हैं और सी को एक कक्षा में एक निजी विधि (शायद सबसे अच्छा तरीका); या एक सूची के रूप में एक उत्परिवर्तनीय प्रकार का उपयोग करके और इसे स्पष्ट रूप से C को पास करना:

def A():
    x = [0]
    def B(var): 
        var[0] = 1
    B(x)
    print x

A()

2
आप इसके अंदर नेस्टेड फ़ंक्शन के बारे में जाने बिना एक फ़ंक्शन कैसे लिख सकते हैं? नेस्टेड फ़ंक्शंस और क्लोज़र उस फ़ंक्शन का एक आंतरिक हिस्सा हैं, जिसमें वे संलग्न हैं।
— Glenn Maynard

आपको आपके द्वारा संलग्न किए गए कार्यों के इंटरफ़ेस के बारे में जानने की आवश्यकता है, लेकिन आपको उनके बारे में जानना नहीं चाहिए। इसके अलावा, आपको यह जानने की उम्मीद नहीं की जा सकती है कि वे उन कार्यों में क्या कहते हैं, आदि! यदि कोई फ़ंक्शन एक गैर-वैश्विक या गैर-वर्ग-स्तरीय संशोधन करता है, तो उसे आमतौर पर अपने इंटरफ़ेस के माध्यम से स्पष्ट करना चाहिए, अर्थात इसे एक पैरामीटर के रूप में लें।
— सिद्धेश बोब

पायथन आपको निश्चित रूप से अच्छा होने के लिए मजबूर नहीं करता है, इसलिए nonlocalकीवर्ड - लेकिन यह आपके ऊपर है कि आप इसे बहुत सावधानी से उपयोग करें।
— सिद्धेश बॉब

5
@ याकूब: मैंने कभी भी इस तरह के बंदों का उपयोग नहीं किया है, जो भाषा की गड़बड़ियों के कारण खतरनाक हो। स्थानीय लोगों को एक अस्थायी वर्ग के रूप में, और स्थानीय कार्यों को कक्षा के तरीकों के रूप में समझें, और यह उससे अधिक जटिल नहीं है। YMMV, मुझे लगता है।
— ग्लेन मेनार्ड

0

आप कर सकते हैं, लेकिन आपको वैश्विक क़ानून का उपयोग करना होगा (वैश्विक चर का उपयोग करते समय हमेशा की तरह अच्छा समाधान नहीं, लेकिन यह काम करता है):

def A():
    global b
    b = 1

    def B():
      global b
      print( b )
      b = 2

    B()
A()

इस समाधान की संभावित कमियों को स्पष्ट करने वाले मेरे उत्तर को देखें
— आंखों की रोशनी

4
वैश्विक चर का उपयोग करना पूरी तरह से अलग है।
— ग्लेन मेनार्ड

0

मुझे नहीं पता कि किसी फ़ंक्शन की विशेषता है जो फ़ंक्शन __dict__के बाहरी स्थान को देता है जब यह बाहरी स्थान वैश्विक स्थान == मॉड्यूल नहीं है, जो कि फ़ंक्शन तब होता है जब फ़ंक्शन एक नेस्टेड फ़ंक्शन है, पायथन 3 में।

लेकिन पायथन 2 में, जहां तक ​​मुझे पता है, ऐसी कोई विशेषता नहीं है।

तो केवल वही करने की संभावना है जो आप चाहते हैं:

1) एक परस्पर वस्तु का उपयोग करना, जैसा कि दूसरों द्वारा कहा गया है

2)

def A() :
    b = 1
    print 'b before B() ==', b

    def B() :
        b = 10
        print 'b ==', b
        return b

    b = B()
    print 'b after B() ==', b

A()

परिणाम

b before B() == 1
b == 10
b after B() == 10

।

नोटा

सेड्रिक जूलियन के समाधान में एक कमी है:

def A() :
    global b # N1
    b = 1
    print '   b in function B before executing C() :', b

    def B() :
        global b # N2
        print '     b in function B before assigning b = 2 :', b
        b = 2
        print '     b in function B after  assigning b = 2 :', b

    B()
    print '   b in function A , after execution of B()', b

b = 450
print 'global b , before execution of A() :', b
A()
print 'global b , after execution of A() :', b

परिणाम

global b , before execution of A() : 450
   b in function B before executing B() : 1
     b in function B before assigning b = 2 : 1
     b in function B after  assigning b = 2 : 2
   b in function A , after execution of B() 2
global b , after execution of A() : 2

के निष्पादन के बाद वैश्विक बीA() संशोधित किया गया है और इसे पूरा नहीं किया जा सकता है

यदि वैश्विक नामस्थान में पहचानकर्ता b के साथ कोई वस्तु है तो ही ऐसा है

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