iT邦幫忙

0

Python問題,在刷Leetcode 1239題時遇到

各位好,以下是本人的code (是用很粗糙的backtracking寫法)
不知為何會出現 local variable "bitArr" referenced before assignment 這個錯誤
但若將bitArr = prevArr這行拿掉,就不會報錯了,蠻詭異的
敝人實在不知該從何debug起,煩請高手指點

class Solution(object):
    def maxLength(self, arr):
        """
        :type arr: List[str]
        :rtype: int
        """
        
        bitArr = [0]*26
        self.res = 0
        
        def backtracking(start):
            self.res = max(self.res, sum(bitArr))
            prevArr = bitArr
            for i in range(start, len(arr)):
                duplicate = False
                for char in arr[i]:
                    if bitArr[ord(char)-ord('a')] == 1:
                        duplicate = True
                        bitArr = prevArr
                        break
                    bitArr[ord(char)-ord('a')] = 1
                if duplicate:
                    continue
                else:
                    for char in arr[i]:
                        bitArr[ord(char)-ord('a')] = 1
                    backtracking(i+1)
                    for char in arr[i]:
                        bitArr[ord(char)-ord('a')] = 0
                    
        backtracking(0)
        
        return self.res
建議看一下邦友的這篇文章
https://ithelp.ithome.com.tw/articles/10230680
之後可能也需要看一下有關深複製與淺複製的文章
ffaanngg iT邦新手 5 級 ‧ 2021-02-16 15:23:22 檢舉
謝謝分享,文章寫得很清楚

1 個回答

1
最佳解答

下方這段程式,會在 func 內產生一個 區域變數 a
在 Python 中對變數賦值,就相當於是在宣告區域變數 (如果函數中變數不存在)

a = 0
def func():
    a = 1
    print(a) #1
func()
print(a) #0

相當於 JS 中的這段程式,差異在 JS 有 var 而 Python 沒有,所以就一律當作宣告變數區域變數處理。

var a = 0
function func(){
    var a = 1
}

那這個錯誤是什麼?
local variable "bitArr" referenced before assignment

下方這段程式,根據上面的範例我們知道 a = 2 會產生一個新的區域變數,那在這之前執行 b = a 會發生什麼? 雖然 a = 2 還未執行,但在函數中 a 已經被視為區域變數了,所以執行 b = a 其實是將 未賦值的區域變數 a 設定給 b,類似 JS 「提升(Hoisting)」 的概念。

a = 0
def func():
    b = a
    a = 2
func()
# local variable 'a' referenced before assignment

那回到你的程式碼,因為這裡使用了未賦值的 bitArr,所以出錯了

self.res = max(self.res, sum(bitArr))

解法

可以在函數的開頭使用 nonlocal 指定 bitArr 為非區域變數,這樣就能像 JS 一樣當作外部變數使用了

def backtracking(start):
    nonlocal bitArr
    ...

詳細內容可以 Google 搜尋 「Python Scope (作用域)」

看更多先前的回應...收起先前的回應...
ffaanngg iT邦新手 5 級 ‧ 2021-02-16 14:18:59 檢舉

謝謝您
簡而言之,以這段code為例

a = 0
def func():
    b = a
    a = 2
func()

如果在函數內再define一次a,a就會成為區域變數,而在函數中,a = 2這行之前出現的a,都會是未賦值的(a有define但未賦值,類似JavaScript的hoisting),因此b=a這行會出錯。

這不知算不算是Python的一個坑。JavaScript中為了避免hoisting帶來的麻煩,已經儘量使用let和const取代var了。

這真的蠻坑的,一般不會特別注意這點 ╰( ̄▽ ̄)╭

  • JS 使用關鍵字(var)宣告區域變數
  • Python 使用關鍵字(nonlocal)宣告非區域變數
ffaanngg iT邦新手 5 級 ‧ 2021-02-16 15:27:35 檢舉

可否再問個基本問題...
我想使用global來解題,但是發現:

class Solution(object):
    def maxLength(self, arr):
        """
        :type arr: List[str]
        :rtype: int
        """
        bitArr = [0]*26
        self.res = 0
        
        def backtracking(start):
            global bitArr
            print(bitArr)          
        
        backtracking(0)
   
        return self.res

會出現 name 'bitArr' is not defined 的錯誤,請問是為什麼呢?用nonlocal就沒問題,不過舊版的Python沒有nonlocal可用

global 是「全域變數」,nonlocal 是「外部變數」,兩者差異如下:

# global

a = 0 #<-全域
def func1():
    a = 1 #<-外部
    def func2():
        global a   # 這裡的 a 是全域變數

# nonlocal

a = 0 #<-全域
def func1():
    a = 1 #<-外部
    def func2():
        nonlocal a   # 這裡的 a 是外部變數

bitArr 宣告在函數中不是全域變數,所以出現 is not defined 未定義的錯誤。

解法

  1. 將 bitArr 放到 self 內,這樣就不用宣告 global 了
  2. 將 bitArr 放到 class 外,這樣 bitArr 就成為全域變數了
ffaanngg iT邦新手 5 級 ‧ 2021-02-17 01:15:42 檢舉

感謝,很清楚

看來global也不太好用

/images/emoticon/emoticon12.gif

我要發表回答

立即登入回答