การใช้คำสำคัญ“ ทั่วโลก” ใน Python


285

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

ฉันใช้ Python 2.7 และฉันลองทดสอบเล็ก ๆ นี้

>>> sub = ['0', '0', '0', '0']
>>> def getJoin():
...     return '.'.join(sub)
...
>>> getJoin()
'0.0.0.0'

globalดูเหมือนว่าสิ่งที่มีการปรับการทำงานได้โดยไม่ต้อง ฉันสามารถเข้าถึงตัวแปรทั่วโลกได้โดยไม่มีปัญหา

ฉันไม่มีอะไรเลยหรือ นอกจากนี้ต่อไปนี้มาจากเอกสาร Python:

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

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


1
ฉันคิดว่าคุณกำลังสับสนกับ php ซึ่งต้องการการใช้คำสำคัญทั่วโลก - เอกสาร python ยืนยันสิ่งนี้ - โดยพื้นฐานแล้วหากไม่ได้กำหนดไว้ในบริบทของท้องถิ่นจะถือว่าเป็นทั่วโลก
tobyodavies

11
ระวังข้อความของคุณ: Python ไม่มี namespace ที่แยกจากกันสำหรับฟังก์ชั่น (ซึ่งหมายความว่าคุณสามารถมีdef foo(): ...และfoo = ...ในเวลาเดียวกัน) มันสร้างขอบเขตใหม่สำหรับการเรียกใช้ฟังก์ชันทุกครั้ง (แต่สิ่งนี้แตกต่างจากภาษาระดับสูงระยะไกลอื่น ๆ ในโลกนี้อย่างไร)

คำตอบ:


366

คำหลักglobalมีประโยชน์ในการเปลี่ยนแปลงหรือสร้างตัวแปรทั่วโลกในบริบทท้องถิ่นแม้ว่าการสร้างตัวแปรทั่วโลกจะถือว่าเป็นวิธีที่ดี

def bob():
    me = "locally defined"    # Defined only in local context
    print(me)

bob()
print(me)     # Asking for a global variable

ข้างต้นจะทำให้คุณ:

locally defined
Traceback (most recent call last):
  File "file.py", line 9, in <module>
    print(me)
NameError: name 'me' is not defined

ในขณะที่ถ้าคุณใช้globalคำสั่งตัวแปรจะกลายเป็น "นอก" ขอบเขตของฟังก์ชั่นอย่างมีประสิทธิภาพกลายเป็นตัวแปรทั่วโลก

def bob():
    global me
    me = "locally defined"   # Defined locally but declared as global
    print(me)

bob()
print(me)     # Asking for a global variable

ดังนั้นโค้ดด้านบนจะให้:

locally defined
locally defined

นอกจากนี้เนื่องจากลักษณะของ python คุณสามารถใช้globalเพื่อประกาศฟังก์ชันคลาสหรือวัตถุอื่น ๆ ในบริบทท้องถิ่น แม้ว่าฉันจะให้คำแนะนำกับมันเพราะมันทำให้ฝันร้ายหากมีสิ่งผิดปกติหรือต้องการแก้จุดบกพร่อง


59
"ทั่วโลก" ในบริบทนี้ดูเหมือนจะแตกต่างจากภาษาอื่น ๆ ให้พิจารณาว่า "ทั่วโลก" ใน Python การอ้างอิง "global" ยังคงอยู่ในขอบเขตของโมดูลและจะต้องมีการอ้างอิงจากนอกโมดูลนั้นเป็น "module.global_var" แทนที่จะเป็นเพียงแค่ "global_var" อย่างที่ใคร ๆ ก็คาดหวัง
น้ำผลไม้

17
@juice - ใน Python ไม่มีสิ่งใดที่globalsกำหนดแน่นอนโดยอัตโนมัติในทุก namespaces (ขอบคุณ) ในขณะที่คุณได้อย่างถูกต้องชี้ให้เห็นทั่วโลกถูกผูกไว้กับ namespace ภายในโมดูล แต่ก็สามารถที่จะนำเข้ามาในโมดูลอื่นเป็นหรือfrom module import variable import module.variableในกรณีแรกที่นำเข้าจะทำให้เข้าถึงตัวแปรโดยไม่ต้องมีการอ้างอิงเป็นvariable module.หากพิจารณาว่าเป็นส่วนกลางในขอบเขตของโมดูลจะขึ้นอยู่กับตำแหน่งที่นำเข้า ดูเพิ่มเติมnonlocalว่าเป็นคำหลักที่เกี่ยวข้องกับขอบเขตใหม่ใน python 3
เปิดเครื่องใน

3
คุณช่วยอธิบายได้ไหมว่าทำไมตัวแปรทั่วโลกไม่ใช่วิธีแก้ปัญหาที่ดี? ฉันมักจะได้ยินสิ่งนี้ แต่ในโครงการปัจจุบันของฉันพวกเขาดูเหมือนจะทำในสิ่งที่ฉันต้องการให้พวกเขาทำ มีสิ่งที่น่ากลัวกว่าที่ฉันไม่เคยคิดมาก่อนหรือไม่?
Robin Newhouse

5
@RobinNewhouse มีคำถามสองสามในดังนั้นแล้วครอบคลุมที่ หัวข้อและอื่นอื่น ๆ ที่ไม่ใช่บทความ
Unode

213

ในขณะที่คุณสามารถเข้าถึงตัวแปรทั่วโลกโดยไม่มีglobalคำหลักหากคุณต้องการแก้ไขพวกเขาคุณต้องใช้globalคำหลัก ตัวอย่างเช่น:

foo = 1
def test():
    foo = 2 # new local foo

def blub():
    global foo
    foo = 3 # changes the value of the global foo

subในกรณีของคุณคุณเพียงแค่การเข้าถึงรายการ


3
ด้วยหนึ่ง caveat: foo = 3ไม่มีงานทั่วโลก แต่ foo ถูกกำหนดอีกครั้งในพื้นที่ในblubขอบเขตการทำงาน & ไม่แก้ไขตัวแปร foo ดั้งเดิม นี่ค่อนข้างทำให้ฉันสับสน
chhantyal

1
@chhantyal หากคุณทำงานโดยหมายความว่าไม่ผิดพลาดคุณถูกต้อง แต่ในบริบทของ Ivo Wetzel มันจะไม่ทำงานตามที่ตั้งใจไว้ มีการใช้สำหรับพฤติกรรมดังกล่าว แต่มักจะไม่ใช่สิ่งที่โปรแกรมเมอร์ต้องการ
ลัทธิประชาธิปไตย

77

นี่คือความแตกต่างระหว่างการเข้าถึงชื่อและเชื่อมโยงภายในขอบเขต

หากคุณเพียงแค่ค้นหาตัวแปรเพื่ออ่านค่าของมันคุณจะสามารถเข้าถึงขอบเขตทั่วโลกและในพื้นที่ได้

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

หากคุณต้องการที่จะสามารถกำหนดให้กับชื่อทั่วโลกคุณจะต้องบอกให้ parser ใช้ชื่อโกลบอลแทนที่จะผูกชื่อโลคอลใหม่ - ซึ่งเป็นสิ่งที่คำหลัก 'ทั่วโลก' ทำ

การผูกไว้ที่ใดก็ได้ภายในบล็อกจะทำให้ชื่อทุกที่ในบล็อกนั้นถูกผูกไว้ซึ่งอาจทำให้เกิดผลลัพธ์ที่ค่อนข้างแปลก (เช่น UnboundLocalError ปรากฏขึ้นในรหัสทำงานก่อนหน้านี้)

>>> a = 1
>>> def p():
    print(a) # accessing global scope, no binding going on
>>> def q():
    a = 3 # binding a name in local scope - hiding global
    print(a)
>>> def r():
    print(a) # fail - a is bound to local scope, but not assigned yet
    a = 4
>>> p()
1
>>> q()
3
>>> r()
Traceback (most recent call last):
  File "<pyshell#35>", line 1, in <module>
    r()
  File "<pyshell#32>", line 2, in r
    print(a) # fail - a is bound to local scope, but not assigned yet
UnboundLocalError: local variable 'a' referenced before assignment
>>> 

ใช้งานง่ายมากและยากที่จะเชื่อว่านี่เป็นรหัสไพ ธ อนที่ถูกต้อง
PlsWork

52

คำตอบอื่น ๆ ตอบคำถามของคุณ สิ่งสำคัญอีกอย่างที่ควรทราบเกี่ยวกับชื่อใน Python คือชื่อเหล่านั้นเป็นแบบโลคัลหรือแบบโกลบอลตามขอบเขตต่อกัน

ลองพิจารณาสิ่งนี้เช่น:

value = 42

def doit():
    print value
    value = 0

doit()
print value

คุณสามารถเดาได้ว่าvalue = 0คำสั่งจะถูกกำหนดให้กับตัวแปรโลคัลและไม่มีผลต่อค่าของตัวแปรเดียวกันที่ประกาศภายนอกdoit()ฟังก์ชัน คุณอาจประหลาดใจที่พบว่ารหัสข้างต้นจะไม่ทำงาน คำสั่งprint valueภายในฟังก์ชั่นผลิตUnboundLocalError.

เหตุผลก็คือว่างูหลามได้สังเกตเห็นว่าที่อื่น ๆ ในฟังก์ชั่นที่คุณกำหนดชื่อvalueและยังมีการประกาศไม่มีที่ไหนเลยvalue globalนั่นทำให้มันเป็นตัวแปรท้องถิ่น แต่เมื่อคุณพยายามพิมพ์มันชื่อท้องถิ่นยังไม่ได้รับการกำหนด Python ในกรณีนี้ไม่ถอยกลับไปค้นหาชื่อเป็นตัวแปรกลางเช่นเดียวกับภาษาอื่น ๆ โดยพื้นฐานแล้วคุณไม่สามารถเข้าถึงตัวแปรโกลบอลได้หากคุณกำหนดตัวแปรโลคัลที่มีชื่อเดียวกันทุกที่ในฟังก์ชัน


1
ขอบคุณที่ชี้นำสิ่งนี้ ดังนั้นตราบใดที่ขอบเขตในพื้นที่ของคุณไม่ได้กำหนดให้กับชื่อที่มีอยู่นอกขอบเขตการอ้างอิงในขอบเขตภายในจะใช้ชื่อภายนอก อย่างไรก็ตามหากคุณทำหน้าที่ใดก็ตามที่คุณกำหนดให้กับชื่อนั้นการอ้างอิงในขอบเขตท้องถิ่นนั้นแม้กระทั่งก่อนการมอบหมายงานในพื้นที่อย่ามองออกไปข้างนอก
Dmitry Minkovsky

1
คุณเข้าใจแล้ว global(หรือnonlocalใน Python 3.x) จะลบล้างพฤติกรรมนี้และอนุญาตให้คุณกำหนดชื่อภายนอกอีกครั้ง
ใจดี

2
ในภาษาอื่น ๆ สิ่งนี้ดูเหมือนจะถูกเรียกว่า "hoisting" เช่นนี้: github.com/shichuan/javascript-patterns/blob/master/…
Dmitry Minkovsky

หากต้องการเพิ่มการดูถูกการบาดเจ็บตัวแปรที่ประกาศด้วยคำหลัก JavaScript ที่ใหม่กว่าletจะไม่ถูกยก
kindall

มันเร็วกว่าในการเข้าถึงตัวแปรโกลบอลที่ประกาศด้วยglobalหรือไม่สำคัญ?
โต

14

การเข้าถึงชื่อและการกำหนดชื่อนั้นแตกต่างกัน ในกรณีของคุณคุณกำลังเข้าถึงชื่อ

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

>>> x = 1         # global 
>>> def foo():
        print x       # accessing it, it is global

>>> foo()
1
>>> def foo():   
        x = 2        # local x
        print x 

>>> x            # global x
1
>>> foo()        # prints local x
2

7
  • คุณสามารถเข้าถึงคำหลักทั่วโลกโดยไม่มีคำหลัก global
  • เพื่อให้สามารถแก้ไขได้คุณต้องระบุอย่างชัดเจนว่าคำหลักนั้นเป็นสากล มิฉะนั้นคำหลักจะถูกประกาศในขอบเขตท้องถิ่น

ตัวอย่าง:

words = [...] 

def contains (word): 
    global words             # <- not really needed
    return (word in words) 

def add (word): 
    global words             # must specify that we're working with a global keyword
    if word not in words: 
        words += [word]

2

ตัวแปรใด ๆ ที่ประกาศนอกฟังก์ชั่นจะถือว่าเป็นโกลบอลเฉพาะเมื่อประกาศจากภายในฟังก์ชัน (ยกเว้นตัวสร้าง) ที่คุณต้องระบุว่าตัวแปรนั้นเป็นโกลบอล


1

สิ่งนี้อธิบายได้ดีใน Python FAQ

กฎสำหรับตัวแปรโลคอลและตัวแปรโกลบอลใน Python มีอะไรบ้าง

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

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

https://docs.python.org/3/faq/programming.html#what-are-the-rules-for-local-and-global-variables-in-python


0

หมายความว่าคุณไม่ควรทำสิ่งต่อไปนี้:

x = 1

def myfunc():
  global x

  # formal parameter
  def localfunction(x):
    return x+1

  # import statement
  import os.path as x

  # for loop control target
  for x in range(10):
    print x

  # class definition
  class x(object):
    def __init__(self):
      pass

  #function definition
  def x():
    print "I'm bad"

ikostia กำลังแจกแจงทุกสิ่งที่คุณไม่สามารถใช้ 'x' สำหรับการใช้งานทั่วโลก - docs.python.org/reference/…
pycruft

1
@ Unode: ฉันแค่เอาแต่กรณีทั้งหมดที่ NikhilRathod ให้ไว้ในคำพูดของเขา
ikostia

0

ทั่วโลกทำให้ตัวแปร "ทั่วโลก"

def out():
    global x
    x = 1
    print(x)
    return


out()

print (x)

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

def out():
     # Taking out the global will give you an error since the variable x is no longer 'global' or in other words: accessible for other commands
    x = 1
    print(x)
    return


out()

print (x)
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.