Python参数传递
和其他语言不一样,传递参数的时候,python不允许程序员选择采用传值还是传引用。Python参数传递采用的是“传对象引用”的方式,这种方式相当于传值和传引用的一种综合:
- 如果函数收到的是一个可变对象(比如字典或者列表)的引用,就能修改对象的原始值,相当于通过“传引用”来传递对象;
- 如果函数收到的是一个不可变对象(比如数字、字符或者元组)的引用,就不能直接修改原始对象,相当于通过“传值’来传递对象。
Python变量作用域
Python变量的作用域大概分为以下四类:
- L(local) 局部作用域
- E(Enclosing) 闭包函数外的函数中
- G(Global) 全局作用域
- B(Built-in) 内建作用域
访问顺序是: L → E → G → B L\rightarrow E\rightarrow G\rightarrow B L→E→G→B,即:在局部找不到,便会去局部外的局部找(例如闭包),再找不到就会去全局找,再者去内建中找。
x = 3 # 内建作用域
g_count = 0 # 全局作用域
def outer():
o_count = 1 # 闭包函数外的函数中
def inner():
i_count = 2 # 局部作用域
在Python中,模块(module),类(class)、函数(def、lambda)会产生新的作用域,其他代码块是不会产生作用域的,也就是说,类似条件判断(if……else)、循环语句(for x in data)、异常捕捉(try…catch)等的变量是可以全局使用的。
应用
修改函数内的变量
全局变量是指在函数外的变量,可以在程序全局使用,局部变量是指定义在函数内的变量,只能在函数内被声明使用。
若内部作用域的想要修改外部作用域的变量,就要使用global
关键字:
x=1
def func():
global x
x+=2
func()
print(x)
输出
3
错误示例:
x=1
def func(x):
x+=2
func(x)
print(x)
输出
1
nonlocal
关键字的使用方法和global
关键字类似,修改嵌套作用域(enclosing 作用域,外层非全局作用域)中的变量:
def outer():
num = 20
def inner():
nonlocal num # nonlocal关键字声明
num = 10
print(num)
inner()
print(num)
outer()
输出:
10
10
回溯
- 可变对象(比如字典或者列表)通过“传引用”来传递对象,因此在结算时要做一个拷贝,在回溯时要重置现场;
- 不可变对象(比如数字、字符或者元组)通过“传值’来传递对象,每次都用一个新的,因此无需状态重置。
使用回溯算法时,有时候需要一个数字变量ans
存储所有可能的结果数量,这个变量定义在回溯函数外,并且需要在函数内修改。
下面以《剑指offer》46. 把数字翻译成字符串为例,说明使用Python编程回溯算法时,搞懂参数传递与变量作用域的重要性。
题目描述:
给定一个数字,我们按照如下规则把它翻译为字符串:0 翻译成 “a” ,1 翻译成 “b”,……,11 翻译成 “l”,……,25 翻译成 “z”。一个数字可能有多个翻译。请编程实现一个函数,用来计算一个数字有多少种不同的翻译方法。
回溯算法思路:
- 由于对应字母的数字在0~25之间,从头开始遍历,每一步面临选择:一位数代表的字母OR两位数代表的字母,即向前移动一位还是两位;
- 向前移动两位的前提是,这个两位数在10~25之间。
正确程序一:
nums=[5,0,9]
n=len(nums)
ans=0#内建变量
def backtrack(i):
global ans#声明为全局变量
if i>=n:
ans+=1
return
backtrack(i+1)
if i+1<n and 10<=nums[i]*10+nums[i+1]<26:
backtrack(i+2)
backtrack(0)
print(ans)
正确程序二:
def translateNum(num):
nums=[int(_) for _ in str(num)]
n=len(nums)
ans=0
def backtrack(i):
nonlocal ans
if i>=n:
ans+=1
return
backtrack(i+1)
if i+1<n and 10<=nums[i]*10+nums[i+1]<26:
backtrack(i+2)
backtrack(0)
return ans
translateNum(509)
错误程序一:
def translateNum(num):
nums=[int(_) for _ in str(num)]
n=len(nums)
ans=0
def backtrack(i):
if i>=n:
ans+=1
return
backtrack(i+1)
if i+1<n and 10<=nums[i]*10+nums[i+1]<26:
backtrack(i+2)
backtrack(0)
return ans
translateNum(509)
错误原因:
local variable 'ans' referenced before assignment
错误程序二:
backtrack(i,ans)
传入ans
是“传值”,函数内部ans+=1
重新创建了一个同名变量,其作用域仅在函数内部,最后返回的是外层函数的变量ans
,所以无论输入什么,结果都是0.
def translateNum(num):
nums=[int(_) for _ in str(num)]
n=len(nums)
ans=0
def backtrack(i,ans):
if i>=n:
ans+=1
return
backtrack(i+1,ans)
if i+1<n and 10<=nums[i]*10+nums[i+1]<26:
backtrack(i+2,ans)
backtrack(0,ans)
return ans
translateNum(509)