今天我們來聊聊 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 在各語言的狀況,但是有時候這往往會是關鍵的細節,沒有處理好就可能會造成錯誤。明天見囉!