ความแตกต่างระหว่าง globals (), locals () และ vars () คืออะไร?


146

ความแตกต่างระหว่างคืออะไรglobals(), locals()และvars()? พวกเขากลับมาทำอะไร? การอัพเดตผลลัพธ์มีประโยชน์หรือไม่


สามารถอัปเดตสิ่งเหล่านี้ใน python 3 และสคริปต์ใช้งานได้จริงหรือไม่
Charlie Parker

คำตอบ:


172

แต่ละรายการจะส่งคืนพจนานุกรม:

  • globals() มักจะส่งกลับพจนานุกรมของโมดูล namespace
  • locals() มักจะส่งกลับพจนานุกรมของปัจจุบัน namespace
  • vars()ผลตอบแทนทั้งในพจนานุกรมของ namespace ที่ปัจจุบัน (ถ้าเรียกว่ามีข้อโต้แย้งไม่ได้) หรือพจนานุกรมของการโต้แย้ง

localsและvarsสามารถใช้คำอธิบายเพิ่มเติมได้บ้าง หากlocals()เรียกว่าภายในฟังก์ชั่นมันจะอัพเดท dict ด้วยค่าของ namespace ตัวแปรโลคอลปัจจุบัน (รวมถึงตัวแปรปิดใด ๆ ) ณ ขณะนั้นและส่งคืน การเรียกหลายครั้งlocals()ในเฟรมสแต็กเดียวกันจะส่งกลับค่า Dict เดียวกันทุกครั้ง - มันถูกแนบไปกับวัตถุเฟรมสแต็กเป็นf_localsคุณลักษณะ เนื้อหาของ dict จะได้รับการอัปเดตในการlocals()โทรแต่ละครั้งและf_localsการเข้าถึงแต่ละแอตทริบิวต์ แต่จะใช้เฉพาะการโทรหรือการเข้าถึงแอตทริบิวต์ดังกล่าวเท่านั้น จะไม่อัปเดตโดยอัตโนมัติเมื่อกำหนดตัวแปรและการกำหนดรายการใน dict จะไม่กำหนดตัวแปรท้องถิ่นที่เกี่ยวข้อง:

import inspect

def f():
    x = 1
    l = locals()
    print(l)
    locals()
    print(l)
    x = 2
    print(x, l['x'])
    l['x'] = 3
    print(x, l['x'])
    inspect.currentframe().f_locals
    print(x, l['x'])

f()

ให้เรา:

{'x': 1}
{'x': 1, 'l': {...}}
2 1
2 3
2 2

รายการแรกprint(l)แสดงเฉพาะ'x'รายการเนื่องจากการมอบหมายให้lเกิดขึ้นหลังจากการlocals()โทร รายการที่สองprint(l)หลังจากโทรlocals()อีกครั้งแสดงlรายการแม้ว่าเราจะไม่ได้บันทึกค่าส่งคืน รายการที่สามและสี่printที่กำหนดตัวแปรไม่ได้อัปเดตlและในทางกลับกัน แต่หลังจากที่เราเข้าถึงf_localsแล้วตัวแปรโลคัลจะถูกคัดลอกเข้าไปlocals()อีกครั้ง

หมายเหตุสองประการ:

  1. ลักษณะการทำงานนี้เป็นเฉพาะของ CPython - Pythons อื่นอาจอนุญาตให้มีการอัปเดตเพื่อให้กลับไปที่เนมสเปซในเครื่องโดยอัตโนมัติ
  2. ใน CPython 2.x เป็นไปได้ที่จะทำให้งานนี้โดยการวางexec "pass"สายในฟังก์ชั่น สิ่งนี้จะสลับฟังก์ชั่นไปเป็นโหมดการดำเนินการที่เก่ากว่าและช้ากว่าซึ่งใช้locals()dict เป็นตัวแทนมาตรฐานของตัวแปรโลคัล

หากlocals()เรียกว่านอกฟังก์ชั่นมันจะส่งกลับพจนานุกรมจริงที่เป็น namespace ปัจจุบัน การเปลี่ยนแปลงเพิ่มเติมของเนมสเปซจะแสดงในพจนานุกรมและการเปลี่ยนแปลงในพจนานุกรมจะปรากฏในเนมสเปซ:

class Test(object):
    a = 'one'
    b = 'two'
    huh = locals()
    c = 'three'
    huh['d'] = 'four'
    print huh

ให้เรา:

{
  'a': 'one',
  'b': 'two',
  'c': 'three',
  'd': 'four',
  'huh': {...},
  '__module__': '__main__',
}

จนถึงทุกสิ่งที่ฉันพูดเกี่ยวกับlocals()ก็เป็นจริงสำหรับvars()... นี่คือความแตกต่าง: vars()ยอมรับวัตถุเดียวเป็นอาร์กิวเมนต์ของมันและถ้าคุณให้มันวัตถุมันคืนค่า__dict__ของวัตถุนั้น สำหรับวัตถุทั่วไปนั้น__dict__เป็นที่เก็บข้อมูลคุณลักษณะส่วนใหญ่ ซึ่งรวมถึงตัวแปรคลาสและโมดูล globals:

class Test(object):
    a = 'one'
    b = 'two'
    def frobber(self):
        print self.c
t = Test()
huh = vars(t)
huh['c'] = 'three'
t.frobber()

ซึ่งทำให้เรา:

three

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

def f(outer):
    if outer:
        f(False)
        print('Outer call locals:', locals())
        print('f.__dict__:', f.__dict__)
    else:
        print('Inner call locals:', locals())
        print('f.__dict__:', f.__dict__)

f.x = 3

f(True)

ซึ่งทำให้เรา:

Inner call locals: {'outer': False}
f.__dict__: {'x': 3}
Outer call locals: {'outer': True}
f.__dict__: {'x': 3}

ที่นี่fเรียกตัวเองซ้ำ ๆ ดังนั้นการโทรภายในและภายนอกซ้อนทับกัน แต่ละคนเห็นตัวแปรโลคัลของตัวเองเมื่อเรียกใช้locals()แต่การโทรทั้งสองเห็นเหมือนกันf.__dict__และf.__dict__ไม่มีตัวแปรโลคัลอยู่ในตัว


4
ส่วนที่ "และได้รับมอบหมายใด ๆ ในพจนานุกรมไม่ได้สะท้อนให้เห็นใน namespace ท้องถิ่นที่เกิดขึ้นจริง" อาจจะมีคำพูดเล็กน้อยเพื่อที่ชัดเจน
Sven Marnach

ผิดปกติพอคุณสามารถเข้าถึงตัวแปรเพิ่มเพื่อให้เป็นvars()หรือพจนานุกรมเรียกว่าฟังก์ชั่นภายในถ้าคุณใช้locals() eval()EG: def test(): huh = locals(); huh['d'] = 4; print eval('d')พิมพ์ 4 เมื่อtest()มีการดำเนินการ!
Mark Mikofski

1
ที่จริงแล้วการมอบหมายให้dict(ส่งคืนโดยlocals()) เกิดขึ้นกับภาพสะท้อนในเนมสเปซในพื้นที่และการเปลี่ยนแปลงในเนมสเปซในท้องถิ่นเกิดขึ้นที่จะสะท้อนให้เห็นในdict(ในงูหลามของฉัน) สิ่งเดียวคือสเปคไม่รับประกันพฤติกรรมนี้
skyking

ระยะชื่อขอบเขตการใช้งานดูง่ายมากขึ้นสำหรับผมกว่าnamespace
แลกเปลี่ยนที่มากเกินไป

1
@overexchange: import thisและใน googlesite:docs.python.org namespace
Ethan Furman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.