今天我們來聊聊 Scope 這件事吧!不要小看這件事情,有很多東西還是對進階的 Developer 有所幫助呢!
global_variable),而 Function 內的變數包含參數則是屬於 Local scope,也就是當 Function 結束之後,這些變數也就被消滅了。然而跟其他語言有點不同的地方是,我們並沒有辦法直接在 Function 內對 Global 變數做修改 (純粹存取是可以的),例如下面的情況會在執行 print(global_variable)的時候報錯 UnboundLocalError: local variable 'global_variable' referenced before assignment ,咦?為什麼這邊報錯說的是 local variable 呢?明明 global_variable 是宣告成 global variable 呀!我們先來說,如果我們要能夠改變 global_variable 的值,必須要 Function 內用 global 的關鍵字,例如global global_variable ,表示這是 global scope 的變數,才能做修改。因此如果在 Python 的 Function 中沒有宣告 global 的話,一律是為 Local 變數,和其他語言不大相同。所以為什麼剛才會是UnboundLocalError: local variable 'global_variable' referenced before assignment 呢?因為這裡的 global_variable 其實在 function 內被宣告後 (global_variable = 'ABC')就會被認定是 local variable (因為在 Function 內沒有用 global 關鍵字),而我們又試圖在所謂 local variable 還沒被宣告之前,就試圖去印出它 (print(global_variable)),所以就會報這樣的錯啦!global_variable = 'abc'
def a()
# global global_variable
print(global_variable) # "abc"
global_variable = 'ABC' # UnboundLocalError: local variable 'global_variable' referenced before assignment
a()
var 或 val 所宣告的變數便是 Field variable,他們是可以被其中 (Class 或是 Object) 的 Method 所存取的,至於能否被外面所存取要看其 access modifier 的狀況,例如在宣告的時候,加上了像是 protected (只能被繼承的子類別使用)、或 private (只能在內部被使用)。而預設都是 public。例如下面定義的 Shape ,外界是可以在 new 了一個 object 之後來存取 height , width 而內部方法也可以 access (如果是 private 那麼外界就不能存取了)。第二種是 Method Parameters,指的就是 method 的參數,其在 Method 中被使用,而其總是 immutable (也就是 val)。第三種是 Local Variables,也就是在 method 內所定義的變數,其只能在 Method 中被使用,可以是 var 也可以是 val 。class Shape {
val height = 3
val width = 15
def area() {
println(height * width)
}
}
private[this] 的關鍵字,表示說這個方法是沒有辦法被其他同 Class 的 Object 所存取的,例如下面的情況是無法 Compile 的 (other.isFoo 是無法的)。但如果把 private[this] 改成只有 private 那就可以囉!表示只要是同一個 Class 的 object 都可以存取,但如果是在外部則無法直接存取,另外子類別也無法存取父類別的 private Method (必須要改成 protected)。class Foo {
private[this] def isFoo = true
def doFoo(other: Foo) {
if (other.isFoo) {
// ...
}
}
}
private[<package name>] 就可以被同一個 Package 下的其他 Class 存取,例如下面 Bar 就可以存取 Foo 的 X method。再更細的是,因為 package 有層次,所以我們可以不只定義例如 pp 這層,private[ryan] ,這樣只要是 com.ryan 之下的都可以存取。而最後 public 就是預設的情況,只要透過 object 都可以在外部直接存取囉!package com.ryan.pp {
class Foo {
private[pp] def X {}
}
class Bar {
val f = new Foo
f.X
}
}
fmt.Println(a) 會找不到 a 。而假使我們在內層區塊去修改 a 的值,那麼 a 的值就也會真的被改變,在之後被存取的時候看到改變後的結果。此外 Golang 也允許我們在內層區塊去重新宣告一個同名的變數 ( 又稱 shadowing),例如在第一個 fmt.Println(a)之上加入 a := 2 ,這時候就會看到第一個 fmt.Println(a) 印出來是 2 但是之後的還是印出 1 囉!我們可以發現只要遵循這個規則,其實在 Golang 是很好去做判斷的。func main() {
{
a := 1
{
fmt.Println(a)
}
fmt.Println(a)
}
fmt.Println(a) // "undefined: a" compilation error
}
let mut x = 1; let y = &mut x; 我們先綁定x和 1而 let y = &mut x; 是把 reference 從 x借給了 y這樣才能夠執行 *y += 1; ,但是這時候 println!("{}", x); 就會失敗啦!因為現在 1被 y 借走了。fn main() {
let mut x = 1;
let y = &mut x; // borrowing
*y += 1;
println!("{}", x); // Error
}
y 自己存在一個 Scope 之中,並且在 Scope 結束時,將所有權歸還囉!fn main() {
let mut x = 1;
{
let y = &mut x; // borrowing
*y += 1;
}
println!("{}", x); // ok!
}
今天講了一些比較瑣碎,關於 Scope 在各語言的狀況,但是有時候這往往會是關鍵的細節,沒有處理好就可能會造成錯誤。明天見囉!