Python คำสั่ง nonlocal


341

nonlocalคำสั่งPython ทำอะไร (ใน Python 3.0 และใหม่กว่า)

ไม่มีเอกสารประกอบในเว็บไซต์ Python อย่างเป็นทางการและhelp("nonlocal")ใช้งานไม่ได้



18
นี่คือเอกสารเว็บไซต์ Python อย่างเป็นทางการสำหรับ nonlocal: docs.python.org/3/reference/ ..... (เอกสารนี้มีให้ตั้งแต่ Python 3.0 ดังนั้นการยืนยันของ OP ว่าเอกสารที่เป็นทางการไม่ถูกต้อง)
wkschwartz

3
"There is no documentation for nonlocal".จริงๆแล้วคุณสามารถทำhelp(keyword_in_string)เอกสารใน Python 3 ขึ้นไป
ytpillai

10
เพื่อความเป็นธรรมเอกสารทางการชนิดของการดูดในเรื่อง ตัวอย่างคำตอบที่เลือกทำให้สิ่งต่าง ๆ ชัดเจนมากทำให้นี่เป็นคำถามที่มีค่า
นักฟิสิกส์บ้า

อย่างเป็นทางการในหลามกวดวิชามีคำอธิบายที่ดีของแนวคิดของขอบเขตและ namespacesกับตัวอย่างที่ดี
jammon

คำตอบ:


473

เปรียบเทียบสิ่งนี้โดยไม่ใช้nonlocal:

x = 0
def outer():
    x = 1
    def inner():
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 0

นี้ใช้nonlocalที่inner()เป็นxอยู่ในขณะนี้นอกจากนี้ยังมีouter()'s x:

x = 0
def outer():
    x = 1
    def inner():
        nonlocal x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 2
# global: 0

ถ้าเราจะใช้globalมันจะผูกxกับค่า "โลก" ที่ถูกต้อง:

x = 0
def outer():
    x = 1
    def inner():
        global x
        x = 2
        print("inner:", x)

    inner()
    print("outer:", x)

outer()
print("global:", x)

# inner: 2
# outer: 1
# global: 2

32
มันแตกต่างจาก global x อย่างไร
ooboo

52
มันคล้ายกันมาก - แต่โปรดทราบว่า outer x ไม่ได้เป็นแบบโกลบอลในตัวอย่าง แต่มีการกำหนดไว้ในฟังก์ชันด้านนอกแทน
Anon

3
@Dustin - จริง ๆ แล้วถ้าคุณมีคลาส A พร้อมกับแอตทริบิวต์ x และคลาสย่อย B ที่กำหนดไว้คุณจะอ้างถึง x จากภายใน B เป็น Ax
Anon

2
รหัสจะได้รับการเยื้องอย่างหนักเมื่อกำหนดฟังก์ชั่นด้านในและท้ายที่สุดจะเป็นการละเมิดข้อแนะนำ 79 ตัวอักษร PEP8 มีวิธีแก้ไขปัญหานี้อย่างไร? ฟังก์ชั่นด้านในสามารถวางนอกฟังก์ชั่นด้านนอกได้หรือไม่? ฉันรู้ว่าคำถามฟังดูงี่เง่า แต่ฉันตั้งใจจริง
tommy.carstensen

3
@ tommy.carstensen คุณสามารถผ่านฟังก์ชั่นในฐานะที่เป็น ARG นั่นคือความงามของฟังก์ชั่นการสั่งซื้อที่สูงขึ้น ในการเขียนโปรแกรมฟังก์ชั่นนี้เรียกว่าการแต่งเพลงไพ ธ อนไม่ใช่ภาษา FP ที่แท้จริง แต่แน่นอนว่าคุณสามารถเล่นได้ด้วยคุณสมบัติ (เครื่องกำเนิดไฟฟ้าฟังก์ชั่นการสั่งซื้อที่สูงขึ้นเป็นตัวอย่าง)
superuseroi

90

ในระยะสั้นมันช่วยให้คุณกำหนดค่าให้กับตัวแปรในขอบเขตด้านนอก (แต่ไม่ใช่แบบโกลบอล) ดูPEP 3104สำหรับรายละเอียดที่เต็มไปด้วยเลือดทั้งหมด


41

การค้นหา google สำหรับ "python nonlocal" เปิดใช้ข้อเสนอPEP 3104ซึ่งอธิบายไวยากรณ์และการใช้เหตุผลเบื้องหลังคำสั่งอย่างสมบูรณ์ ในระยะสั้นมันทำงานในลักษณะเดียวกับglobalคำสั่งยกเว้นว่ามันจะใช้ในการอ้างถึงตัวแปรที่ไม่ได้ทั่วโลกหรือท้องถิ่นเพื่อฟังก์ชั่น

นี่คือตัวอย่างสั้น ๆ ของสิ่งที่คุณสามารถทำได้กับสิ่งนี้ ตัวสร้างตัวนับสามารถเขียนใหม่เพื่อใช้สิ่งนี้เพื่อให้ดูเหมือนกับสำนวนภาษาที่มีการปิด

def make_counter():
    count = 0
    def counter():
        nonlocal count
        count += 1
        return count
    return counter

เห็นได้ชัดว่าคุณสามารถเขียนสิ่งนี้เป็นเครื่องกำเนิดไฟฟ้าเช่น:

def counter_generator():
    count = 0
    while True:
        count += 1
        yield count

แต่ในขณะนี้เป็นงูหลามที่สมบูรณ์แบบ แต่ดูเหมือนว่าเวอร์ชั่นแรกจะค่อนข้างชัดเจนสำหรับผู้เริ่มต้น การใช้เครื่องกำเนิดไฟฟ้าอย่างถูกต้องโดยการเรียกฟังก์ชันที่ส่งคืนมานั้นเป็นจุดที่เกิดความสับสนได้ รุ่นแรกส่งคืนฟังก์ชันอย่างชัดเจน


1
ฉันแน่ใจว่านั่นคือสิ่งที่คำหลัก 'ทั่วโลก' ทำ - ทำงานได้สูงกว่าสภาพแวดล้อมจนกว่าจะถึงตัวแปรที่มีชื่อนั้น ตัวแปร x สามารถประกาศได้ที่ระดับโมดูลภายในคลาสจากนั้นแยกกันในฟังก์ชั่นภายในคลาสนี้และจากนั้นในฟังก์ชั่นด้านในของฟังก์ชั่นนั้น - มันรู้ได้อย่างไรว่า x อ้างอิงถึงอะไร?
ooboo

7
สิ่งที่เกี่ยวกับโลกคือมันใช้ได้เฉพาะกับตัวแปรทั่วโลก มันไม่สามารถมองเห็นตัวแปรในขอบเขต nonglobal ที่ล้อมรอบ
SingleNegationElimination

ฉันลองใช้ make_counter - อย่างไรก็ตามมันไม่ได้ส่งคืนตัวสร้าง แต่เป็นฟังก์ชัน มีวิธีคืนเครื่องกำเนิดไฟฟ้าหรือไม่ดังนั้นในภายหลังฉันสามารถทำซ้ำได้?
Dejell

@Dejel: ตัวอย่างนี้มีวัตถุประสงค์เพื่อแสดงnonlocalคำสั่งใน Python; หากคุณต้องการลำดับของตัวเลขธรรมชาติจริง ๆ แล้วสำนวนไพ ธ อนคือitertools.count()
SingleNegationElimination

ฉันต้องการสาธิตความสามารถในการส่งคืนเครื่องกำเนิดไฟฟ้าเช่นเดียวกับผลผลิต - ผลผลิตจริงแล้วคืนเครื่องกำเนิดไฟฟ้า ความคิดของฉันคือไม่ใช้อัตราผลตอบแทนและอาจใช้ nonlocal หรือโซลูชันอื่นแทน
Dejell

15

@ooboo:

มันใช้จุด "ใกล้" ที่สุดในการอ้างอิงในซอร์สโค้ด สิ่งนี้เรียกว่า "Lexical Scoping" และเป็นมาตรฐานสำหรับ> 40 ปีแล้ว

สมาชิกในชั้นเรียนของงูใหญ่อยู่ในพจนานุกรมชื่อจริง ๆ__dict__และจะไม่สามารถเข้าถึงได้โดยการกำหนดขอบเขตคำศัพท์

หากคุณไม่ได้ระบุnonlocalแต่ทำx = 7มันจะสร้างตัวแปรท้องถิ่นใหม่ "x" หากคุณระบุnonlocalจะพบ "ที่ใกล้เคียงที่สุด" "x" และกำหนดให้ หากคุณระบุnonlocalและไม่มี "x" จะมีข้อความแสดงข้อผิดพลาด

คำหลักglobalนั้นดูแปลกสำหรับฉันเสมอเพราะมันจะไม่สนใจ "x" อื่น ๆ ทั้งหมดยกเว้นคำที่อยู่นอกสุด แปลก.


14

help ('nonlocal') nonlocalข้อความ


    nonlocal_stmt ::= "nonlocal" identifier ("," identifier)*

nonlocalคำสั่งทำให้ตัวบ่งชี้ที่ระบุไว้ในการอ้างถึงตัวแปรที่ถูกผูกไว้ก่อนหน้านี้อยู่ในขอบเขตการปิดล้อมที่ใกล้ที่สุด สิ่งนี้มีความสำคัญเนื่องจากพฤติกรรมเริ่มต้นสำหรับการผูกคือการค้นหา namespace ท้องถิ่นก่อน คำสั่งอนุญาตให้มีการห่อหุ้มโค้ดเพื่อเชื่อมโยงตัวแปรนอกขอบเขตภายในเครื่องนอกเหนือจากขอบเขตส่วนกลาง (โมดูล)

ชื่อที่ระบุไว้ในnonlocalคำสั่งซึ่งแตกต่างจากที่ระบุไว้ใน globalคำสั่งจะต้องอ้างถึงการผูกไว้ก่อนหน้าในขอบเขตล้อมรอบ

ชื่อที่แสดงในnonlocalคำสั่งจะต้องไม่ชนกับการผูกไว้ล่วงหน้าในขอบเขตท้องถิ่น

ดูสิ่งนี้ด้วย:

PEP 3104 - การเข้าถึงชื่อในขอบเขตภายนอก
ข้อกำหนดสำหรับnonlocalคำสั่ง

หัวข้อความช่วยเหลือที่เกี่ยวข้อง: global, NAMESPACES

ที่มา: การอ้างอิงภาษา Python


11
เรียนรู้สิ่งใหม่ทุกวัน ฉันมีความคิดคุณสามารถใช้ไม่มีhelp()คำหลัก (และตอนนี้ใจของฉันจะถูกเป่า: help()มีข้อโต้แย้งไม่ไปโต้ตอบ )
Erik Youngren

6

อ้างอิงจากPython 3 อ้างอิง :

คำสั่ง nonlocal ทำให้ตัวระบุที่ระบุไว้ในการอ้างถึงตัวแปรที่ถูกผูกไว้ก่อนหน้านี้ในขอบเขตการปิดล้อมที่ใกล้ที่สุดไม่รวม globals

ดังที่ได้กล่าวไว้ในการอ้างอิงในกรณีที่ฟังก์ชั่นซ้อนหลายฟังก์ชั่นตัวแปรเฉพาะในฟังก์ชั่นการปิดล้อมที่ใกล้ที่สุดมีการแก้ไข:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        x = 2
        innermost()
        if x == 3: print('Inner x has been modified')

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Inner x has been modified

ตัวแปร "ที่ใกล้ที่สุด" อาจอยู่ห่างออกไปหลายระดับ:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    x = 1
    inner()
    if x == 3: print('Outer x has been modified')

x = 0
outer()
if x == 3: print('Global x has been modified')

# Outer x has been modified

แต่มันไม่สามารถเป็นตัวแปรส่วนกลางได้:

def outer():
    def inner():
        def innermost():
            nonlocal x
            x = 3

        innermost()

    inner()

x = 0
outer()
if x == 3: print('Global x has been modified')

# SyntaxError: no binding for nonlocal 'x' found

3
a = 0    #1. global variable with respect to every function in program

def f():
    a = 0          #2. nonlocal with respect to function g
    def g():
        nonlocal a
        a=a+1
        print("The value of 'a' using nonlocal is ", a)
    def h():
        global a               #3. using global variable
        a=a+5
        print("The value of a using global is ", a)
    def i():
        a = 0              #4. variable separated from all others
        print("The value of 'a' inside a function is ", a)

    g()
    h()
    i()
print("The value of 'a' global before any function", a)
f()
print("The value of 'a' global after using function f ", a)

2

ความเข้าใจส่วนบุคคลของฉันเกี่ยวกับคำสั่ง "nonlocal" (และขอโทษด้วยเพราะฉันยังใหม่กับ Python และการเขียนโปรแกรมโดยทั่วไป) คือ "nonlocal" เป็นวิธีการใช้ฟังก์ชั่นทั่วโลกภายในฟังก์ชั่นที่ทำซ้ำมากกว่าเนื้อความของรหัสเอง . คำสั่งทั่วโลกระหว่างฟังก์ชั่นถ้าคุณจะ


0

ด้วย 'ต่างแดน' ฟังก์ชั่นภายใน (เช่นฟังก์ชันภายในที่ซ้อนกัน) จะได้รับการอ่านและ ' เขียน ' ได้รับอนุญาตสำหรับการที่เฉพาะตัวแปรของฟังก์ชันการปกครองนอก และ nonlocal สามารถใช้ได้เฉพาะฟังก์ชันภายในเช่น:

a = 10
def Outer(msg):
    a = 20
    b = 30
    def Inner():
        c = 50
        d = 60
        print("MU LCL =",locals())
        nonlocal a
        a = 100
        ans = a+c
        print("Hello from Inner",ans)       
        print("value of a Inner : ",a)
    Inner()
    print("value of a Outer : ",a)

res = Outer("Hello World")
print(res)
print("value of a Global : ",a)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.