Python ฟังก์ชั่นตัวแปรทั่วโลก?


272

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

x = "somevalue"

def func_A ():
   global x
   # Do things to x
   return x

def func_B():
   x = func_A()
   # Do things
   return x

func_A()
func_B()

ไม่xว่าการใช้งานฟังก์ชั่นที่สองมีค่าเดียวกันของสำเนาทั่วโลกของxที่func_aใช้และการปรับเปลี่ยน? เมื่อเรียกใช้ฟังก์ชั่นหลังจากนิยามคำสั่งนั้นสำคัญหรือไม่


1
ระวังอย่าคิดเพียงเพราะคุณมีตัวแปรที่ได้รับมอบหมายในฟังก์ชั่นของคุณที่ไพ ธ อนจะปฏิบัติต่อการอ้างอิงก่อนการมอบหมายเช่นนี้ จนกว่าจะมีการมอบหมายครั้งแรกหากคุณใช้ x มันจะไม่เป็นแบบโกลบอลหรือแบบโลคอล คุณจะได้รับข้อยกเว้น UnboundLocalError ที่น่าอับอายในหน้าของคุณ :)
osirisgothra

คำตอบ:


412

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

เช่น

global someVar
someVar = 55

สิ่งนี้จะเปลี่ยนค่าของตัวแปรโกลบอลเป็น 55 มิฉะนั้นจะกำหนด 55 ให้กับตัวแปรโลคัล

ลำดับของรายการคำนิยามฟังก์ชันไม่สำคัญ (สมมติว่าพวกเขาไม่ได้อ้างถึงกันในทางใดทางหนึ่ง) ลำดับที่พวกเขาเรียกว่าทำ


2
ในรหัสที่ฉันให้คือ func_B กำลังทำสิ่งต่าง ๆ (1) ให้กับสำเนาทั่วโลกของ x (จากที่ได้รับจาก func_A), (2) ไปยังตัวแปรโลคอล x ที่มีค่าเดียวกันกับผลลัพธ์ของ func_A หรือ (3) ถึง ตัวแปรท้องถิ่น x ที่ไม่มีค่าและ (ในสายตาของคอมไพเลอร์) ไม่มีความสัมพันธ์กับ "ค่าบางอย่าง" หรือ x ใน func_A?
Akshat Shekhar

xในการfunc_Bเป็นตัวแปรในท้องถิ่นที่ได้รับความคุ้มค่าจากค่าตอบแทนของการเรียกร้องให้func_A- ดังนั้นฉันเดาว่าจะทำให้มันเป็นของคุณ (2)
เฮล์มส

ตกลงสมมติว่า x เป็นลำดับแบบสุ่มของบางประเภทที่สร้างขึ้นโดย func_A (เช่น func_A สร้าง x ที่แตกต่างกันในแต่ละครั้งที่เรียกใช้) จะเรียกใช้โปรแกรมตามที่เขียนไว้ทำให้ func_b ปรับเปลี่ยน x ที่แตกต่างจากที่ผลิตเมื่อ func_a เรียกว่า? ถ้าเป็นเช่นนั้นฉันจะแก้ไขได้อย่างไร
Akshat Shekhar

1
ใช่หากfunc_Aเปลี่ยนตัวแปรทั่วโลกในระหว่างการเรียกใช้แต่ละครั้งและคืนค่าเป็นfunc_Bใช้แล้วfunc_Bจะทำงานกับค่าที่เปลี่ยนแปลงในแต่ละครั้ง ฉันไม่แน่ใจเกี่ยวกับ "วิธีการแก้ไข" ของคุณ คุณอาจต้องการยอมรับคำตอบที่เป็นประโยชน์มากที่สุดสำหรับคำถามปัจจุบัน / คำถามดั้งเดิมของคุณแล้วลองพิจารณาเปิดคำถามอื่นเกี่ยวกับสิ่งที่ดูเหมือนคำถามติดตาม
Levon

1
ที่จริงแล้วมันขึ้นอยู่กับว่า x คืออะไร หาก x ไม่เปลี่ยนรูปดังนั้น x ใน func_B จะยังคงอยู่ในนั้นเพราะมันถูกประกาศในเครื่องแม้ว่าจะมีค่าเท่ากันก็ตาม สิ่งนี้นำไปใช้กับ tuples, ints ... ถ้ามันเป็นตัวอย่างของรายการและคุณทำx.append("...")มันเป็นตัวแปรทั่วโลก x ที่มีการเปลี่ยนแปลงเพราะคนในท้องถิ่นอ้างอิงถึงคนทั่วโลก
jadkik94

111

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

ลองดูที่ pseudocode ของคุณในเวอร์ชันดัดแปลงเพื่อดูว่าเกิดอะไรขึ้น:

# Here, we're creating a variable 'x', in the __main__ scope.
x = 'None!'

def func_A():
  # The below declaration lets the function know that we
  #  mean the global 'x' when we refer to that variable, not
  #  any local one

  global x
  x = 'A'
  return x

def func_B():
  # Here, we are somewhat mislead.  We're actually involving two different
  #  variables named 'x'.  One is local to func_B, the other is global.

  # By calling func_A(), we do two things: we're reassigning the value
  #  of the GLOBAL x as part of func_A, and then taking that same value
  #  since it's returned by func_A, and assigning it to a LOCAL variable
  #  named 'x'.     
  x = func_A() # look at this as: x_local = func_A()

  # Here, we're assigning the value of 'B' to the LOCAL x.
  x = 'B' # look at this as: x_local = 'B'

  return x # look at this as: return x_local

ในความเป็นจริงคุณสามารถเขียนfunc_Bตัวแปรทั้งหมดที่มีชื่อx_localและมันจะทำงานเหมือนกัน

คำสั่งซื้อมีความสำคัญต่อลำดับการทำงานของคุณที่เปลี่ยนค่าของ Global x ดังนั้นในตัวอย่างของเราเพื่อไม่สำคัญตั้งแต่การโทรfunc_B func_Aในตัวอย่างนี้คำสั่งมีความสำคัญ:

def a():
  global foo
  foo = 'A'

def b():
  global foo
  foo = 'B'

b()
a()
print foo
# prints 'A' because a() was the last function to modify 'foo'.

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

x = 5

def access_only():
  return x
  # This returns whatever the global value of 'x' is

def modify():
  global x
  x = 'modified'
  return x
  # This function makes the global 'x' equal to 'modified', and then returns that value

def create_locally():
  x = 'local!'
  return x
  # This function creates a new local variable named 'x', and sets it as 'local',
  #  and returns that.  The global 'x' is untouched.

สังเกตเห็นความแตกต่างระหว่างcreate_locallyและaccess_only- access_onlyกำลังเข้าถึง x ส่วนกลางแม้ว่าจะไม่ได้โทรglobalถึงแม้ว่าcreate_locallyจะไม่ได้ใช้globalก็ตาม แต่ก็สร้างสำเนาภายในเครื่องเนื่องจากได้กำหนดค่า

ความสับสนที่นี่คือเหตุผลที่คุณไม่ควรใช้ตัวแปรทั่วโลก


2
ฉันไม่คิดว่ามันจะสับสนในทางปฏิบัติคุณแค่ต้องเข้าใจกฎการกำหนดขอบเขตของงูใหญ่
Casey Kuball

20

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

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

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

d = {}
l = []
o = type("object", (object,), {})()

def valid():     # these are all valid without declaring any names global!
   d[0] = 1      # changes what's in d, but d still points to the same object
   d[0] += 1     # ditto
   d.clear()     # ditto! d is now empty but it`s still the same object!
   l.append(0)   # l is still the same list but has an additional member
   o.test = 1    # creating new attribute on o, but o is still the same object

8

นี่คือกรณีหนึ่งที่ทำให้ฉันหมดความสนใจโดยใช้ค่าโกลบอลเป็นค่าเริ่มต้นของพารามิเตอร์

globVar = None    # initialize value of global variable

def func(param = globVar):   # use globVar as default value for param
    print 'param =', param, 'globVar =', globVar  # display values

def test():
    global globVar
    globVar = 42  # change value of global
    func()

test()
=========
output: param = None, globVar = 42

ฉันคาดว่าพารามิเตอร์จะมีค่า 42 ประหลาดใจ Python 2.7 ประเมินค่าของ globVar เมื่อวิเคราะห์ฟังก์ชัน func เป็นครั้งแรก การเปลี่ยนค่าของ globVar ไม่ส่งผลต่อค่าเริ่มต้นที่กำหนดให้กับพารามิเตอร์ การชะลอการประเมินผลดังต่อไปนี้ทำงานได้ตามที่ฉันต้องการ

def func(param = eval('globVar')):       # this seems to work
    print 'param =', param, 'globVar =', globVar  # display values

หรือถ้าคุณต้องการความปลอดภัย

def func(param = None)):
    if param == None:
        param = globVar
    print 'param =', param, 'globVar =', globVar  # display values

นั่นทำให้ผมนึกถึงปัญหาของการกำหนดรายการที่ว่างเปล่าเป็นค่าเริ่มต้น และเช่นในตัวอย่างใช้isเพื่อตรวจสอบว่ามีบางอย่างNoneแทนการเปรียบเทียบปกติ==หรือไม่
berna1111

6

คุณสามารถเข้าถึงตัวแปรส่วนกลางได้โดยตรงภายในฟังก์ชั่น หากคุณต้องการเปลี่ยนค่าของตัวแปรกลางนั้นให้ใช้ "global variable_name" ดูตัวอย่างต่อไปนี้:

var = 1
def global_var_change():
      global var
      var = "value changed"
global_var_change() #call the function for changes
print var

โดยทั่วไปแล้วนี่ไม่ใช่วิธีการเขียนโปรแกรมที่ดี โดยการทำลายเนมสเปซตรรกะรหัสอาจกลายเป็นเรื่องยากที่จะเข้าใจและแก้ปัญหา


2

คุณต้องใช้การglobalประกาศเมื่อคุณต้องการแก้ไขค่าที่กำหนดให้กับตัวแปรทั่วโลก

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


2
ถ้อยคำนี้เป็นโชคร้าย ใน Python ค่าที่กำหนดให้กับตัวแปรเป็นการอ้างอิงดังนั้นมันจึงถูกต้องทางเทคนิค (และฉันไม่สงสัยเลยว่าคุณหมายถึง) แต่ผู้อ่านหลายคนอาจตีความ "เปลี่ยนค่า" เป็น "กลายพันธุ์วัตถุ" ซึ่งไม่ใช่ กรณี - ทำงานได้ดีโดยไม่ต้องxs.append(xs.pop(0)) global xs

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