Hàm Python biến toàn cục?


272

Tôi biết tôi nên tránh sử dụng các biến toàn cục ở nơi đầu tiên do nhầm lẫn như thế này, nhưng nếu tôi sử dụng chúng, đây có phải là cách hợp lệ sau đây để sử dụng chúng không? (Tôi đang cố gắng gọi bản sao toàn cầu của một biến được tạo trong một hàm riêng biệt.)

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()

Liệu các xrằng việc sử dụng chức năng thứ hai có cùng giá trị của bản sao chép toàn cầu xfunc_asử dụng và sửa đổi? Khi gọi các hàm sau định nghĩa, thứ tự có quan trọng không?


1
hãy cẩn thận đừng giả sử chỉ vì bạn có một biến được gán trong hàm của mình mà python sẽ xử lý các tham chiếu trước khi gán như vậy. Cho đến khi gán đầu tiên, nếu bạn sử dụng x, nó sẽ không phải là toàn cục hoặc là cục bộ. Bạn sẽ nhận được ngoại lệ khét tiếng UnboundLocalError trên khuôn mặt của bạn :)
osirisgothra

Câu trả lời:


412

Nếu bạn muốn truy cập một biến toàn cục, bạn chỉ cần sử dụng tên của nó. Tuy nhiên để thay đổi giá trị của nó, bạn cần sử dụng globaltừ khóa.

Ví dụ

global someVar
someVar = 55

Điều này sẽ thay đổi giá trị của biến toàn cục thành 55. Nếu không, nó sẽ chỉ gán 55 cho một biến cục bộ.

Thứ tự của danh sách định nghĩa hàm không quan trọng (giả sử chúng không đề cập đến nhau theo một cách nào đó), thứ tự chúng được gọi là không.


2
Trong mã mà tôi đã đưa ra, func_B đang thực hiện (1) cho bản sao toàn cầu của x (như nhận được từ func_A), (2) cho một biến cục bộ x có cùng giá trị kết quả của func_A hoặc (3) một biến cục bộ x không có giá trị và (trong mắt trình biên dịch) không liên quan đến "một số giá trị" hoặc x trong func_A?
Akshat Shekhar

xin func_Blà một biến cục bộ nhận giá trị của nó từ giá trị trả về của cuộc gọi đến func_A- vì vậy tôi đoán rằng nó sẽ biến nó thành (2)
Levon

ok, giả sử x là một chuỗi ngẫu nhiên của một số loại được tạo bởi func_A (tức là func_A tạo ra một x khác nhau mỗi khi nó chạy.) Sẽ chạy chương trình như đã viết khiến func_b sửa đổi một x khác so với ban đầu được tạo ra khi func_a gọi là? Nếu vậy, làm thế nào tôi có thể sửa chữa nó?
Akshat Shekhar

1
Có, nếu func_Athay đổi biến toàn cục trong mỗi lần chạy và đưa nó trở lại func_Bsử dụng, thì nó func_Bsẽ hoạt động với giá trị thay đổi mỗi lần. Tôi không chắc chắn về "cách khắc phục" của bạn. Bạn có thể muốn chấp nhận câu trả lời hữu ích nhất cho câu hỏi hiện tại / ban đầu của bạn và sau đó xem xét mở ra một câu hỏi khác về những gì trông giống như một câu hỏi tiếp theo.
Levon

1
Thật ra nó phụ thuộc vào x. Nếu x là bất biến, thì x trong func_B sẽ ở trong đó, bởi vì nó được khai báo cục bộ ngay cả khi chúng có cùng giá trị. Điều này áp dụng cho các bộ dữ liệu, ints ... Nếu đó là một ví dụ của danh sách và bạn làm x.append("..."), thì đó là biến toàn cầu x được thay đổi, bởi vì biến cục bộ tham chiếu đến toàn cục.
jadkik94

111

Trong phạm vi Python, bất kỳ sự gán nào cho một biến chưa được khai báo trong phạm vi đó sẽ tạo ra một biến cục bộ mới trừ khi biến đó được khai báo trước đó trong hàm khi tham chiếu đến một biến có phạm vi toàn cầu với từ khóa global.

Hãy xem xét một phiên bản sửa đổi của mã giả của bạn để xem điều gì xảy ra:

# 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

Trong thực tế, bạn có thể viết lại tất cả func_Bvới biến được đặt tên x_localvà nó sẽ hoạt động giống hệt nhau.

Thứ tự chỉ quan trọng theo thứ tự các hàm của bạn thực hiện các hoạt động thay đổi giá trị của x toàn cầu. Vì vậy, trong ví dụ của chúng tôi, thứ tự không quan trọng, vì func_Bcác cuộc gọi func_A. Trong ví dụ này, thứ tự không thành vấn đề:

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'.

Lưu ý rằng globalchỉ cần thiết để sửa đổi các đối tượng toàn cầu. Bạn vẫn có thể truy cập chúng từ bên trong một hàm mà không cần khai báo global. Như vậy, chúng ta có:

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.

Lưu ý sự khác biệt giữa create_locallyaccess_only- access_onlylà truy cập vào x toàn cầu mặc dù không gọi globalvà mặc dù create_locallykhông sử dụng global, nhưng nó tạo ra một bản sao cục bộ kể từ khi gán giá trị.

Sự nhầm lẫn ở đây là lý do tại sao bạn không nên sử dụng các biến toàn cục.


2
Tôi không nghĩ rằng điều này rất khó hiểu trong thực tế, bạn chỉ cần hiểu quy tắc phạm vi của trăn .
Casey Kuball

20

Như những người khác đã lưu ý, bạn cần khai báo một biến globaltrong một hàm khi bạn muốn hàm đó có thể sửa đổi biến toàn cục. Nếu bạn chỉ muốn truy cập nó, thì bạn không cần global.

Để đi sâu hơn một chút về điều đó, "sửa đổi" nghĩa là gì: nếu bạn muốn liên kết lại tên toàn cầu để nó trỏ đến một đối tượng khác, tên phải được khai báo globaltrong hàm.

Nhiều hoạt động sửa đổi (biến đổi) một đối tượng không liên kết lại tên toàn cầu để trỏ đến một đối tượng khác, và vì vậy tất cả chúng đều hợp lệ mà không cần khai báo tên globaltrong hàm.

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

Đây là một trường hợp khiến tôi bị loại, sử dụng toàn cầu làm giá trị mặc định của tham số.

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

Tôi đã dự kiến ​​param có giá trị 42. Bất ngờ. Python 2.7 đã đánh giá giá trị của globalVar khi lần đầu tiên phân tích hàm func. Thay đổi giá trị của globalVar không ảnh hưởng đến giá trị mặc định được gán cho param. Trì hoãn việc đánh giá, như sau đây, làm việc như tôi cần.

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

Hoặc, nếu bạn muốn an toàn,

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

Điều đó nhắc nhở tôi về vấn đề gán danh sách trống làm giá trị mặc định . Và, như trong ví dụ, sử dụng isđể kiểm tra xem có gì không None, thay vì so sánh thông thường ==.
berna1111

6

Bạn có thể truy cập trực tiếp vào một biến toàn cục bên trong một hàm. Nếu bạn muốn thay đổi giá trị của biến toàn cục đó, hãy sử dụng "biến toàn cục_name". Xem ví dụ sau:

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

Nói chung, đây không phải là một thực hành lập trình tốt. Bằng cách phá vỡ logic không gian tên, mã có thể trở nên khó hiểu và gỡ lỗi.


2

Bạn phải sử dụng globalkhai báo khi bạn muốn thay đổi giá trị được gán cho biến toàn cục.

Bạn không cần nó để đọc từ một biến toàn cục. Lưu ý rằng việc gọi một phương thức trên một đối tượng (ngay cả khi nó làm thay đổi dữ liệu trong đối tượng đó) không làm thay đổi giá trị của biến giữ đối tượng đó (ma thuật phản xạ vắng mặt).


2
Từ ngữ này là không may. Trong Python, giá trị được gán cho một biến là một tham chiếu, vì vậy nó đúng về mặt kỹ thuật (và tôi không nghi ngờ gì về ý của bạn), nhưng nhiều người đọc có thể hiểu "thay đổi giá trị" là "làm biến đổi đối tượng", không phải vậy trường hợp - xs.append(xs.pop(0))hoạt động tốt mà không có global xs.

@delnan Câu trả lời của tôi được diễn đạt cẩn thận, nhưng tôi sẽ làm rõ.
Marcin
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.