最近几天在看python , 看到函数这一段的时候 , 突然发现一个问题 , 字符串在函数体中重新赋值 , 不会对函数体外 , 该字符串本身的值产生影响 .
例如 :
def changestr (str):
str = "inside"
print("这是function中 , 值为:",str)
mystr = "outside"
changestr(mystr)
print("这是函数外边 , 值为:",mystr)
运行结果为 :
这是function中 , 值为: inside
这是函数外边 , 值为: outside
考虑到字符串不可变的特性 (python的六种标准类型 , 其中有三种是不可变的类型 , 即 : Number ,String , Tuple) , 我分别测试了Number和Tuple , 发现同以上结果是一样的 , 因为这三种类型只能通过重新赋值来改变对象的值 , 另外三种类型(List , Set , Dictionary) 是可以改变内部的元素 , 于是又测试了这三种seq类型 , 第一次测试如下 :
def change(mylist):
mylist.append([1,2,3,4])
mylist = ["aa",21]
print(mylist)
change(mylist)
print(mylist)
结果如下:
['aa', 21]
['aa', 21, [1, 2, 3, 4]]
发现在函数体中的修改 , 对对象本身的值发生了改变 , 在函数之外 , 该列表的内容依然发生了改变 , 这是事先就能猜测到的结果 , 因为python中的参数 , 传入的是变量引用的副本 , 它与变量指向同一个值.
鉴于前三种类型的参数是直接重新赋值的 , 于是继续下一步测试 :
def change2(list):
list = [1,2,3,4]
mylist = ["aa",21]
print(mylist)
change2(mylist)
print(mylist)
结果如下:
['aa', 21]
['aa', 21]
有些意外 , 出现了和三种不可变类型参数一样的情况 , 在函数体中的重新赋值 , 没有对外部变量的值产生影响 , 不过仔细一想 , 却又在情理之中 .
我对python还不够熟悉 , 但是从近两天的学习中发现 , 其存储模型与java相似 , 即变量中存储的是引用 , 是指向真正内容的内存地址(当然 ,java中的八大基本数据类型 , 变量名和值都是存储在堆栈中的 ) , 对变量重新赋值 , 相当于修改了变量副本存储的内存地址 , 而这时的变量已经和函数体外的变量不是同一个了, 在函数体之外的变量 , 依旧存储的是原本的内存地址 , 其值自然没有发生改变 .
简单来说 :
- 函数体传入的参数 , 为函数体外变量引用的副本 .
- 在函数体中改变变量指向的堆中的值 , 对函数外变量有效.
- 在函数体中改变变量的引用 , 对函数外变量无效
大致图如下 :(红色线代表重新赋值动作)
当然 , 测试时不能少的 , 我在java中也做了相应的测试 , 结果与猜想一致 , 测试代码如下:
import java.util.ArrayList;
import java.util.List;
/**
* Created by dell on 2017/3/27.
*
* @author dell
* @date 2017/03/27
*/
public class DemoScope {
public static void main(String[] args) {
{
String str1 = "aaa";
testStr(str1);
System.out.println("方法外:"+str1);
}
{
int a = 5;
testNumber(a);
System.out.println("方法外:"+a);
}
{
List list = new ArrayList();
list.add("a");
list.add("b");
testList(list);
System.out.println("方法外:"+list.toString());
}
{
char a = 'a';
testChar(a);
System.out.println("方法外:"+a);
}
{
List list = new ArrayList();
list.add("a");
list.add("b");
testList2(list);
System.out.println("方法外:"+list.toString());
}
{
boolean fl = true;
testBoolean(fl);
System.out.println("方法外:"+fl);
}
{
Tree tree = new Tree(50 , "银杏");
testObject(tree);
System.out.println("方法外:"+tree.toString());
}
{
Tree tree = new Tree(50 , "银杏");
testObject2(tree);
System.out.println("方法外:"+tree.toString());
}
}
static class Tree{
int height;
String type;
Tree(int height, String type){
this.height = height;
this.type = type;
}
@Override
public String toString() {
return "Tree{" +
"height=" + height +
", type='" + type + '\'' +
'}';
}
}
private static void testStr(String str){
System.out.println("1.这是function中,str="+str);
str = "新的值";
System.out.println("2.这是function中,str="+str);
}
private static void testNumber(int num ){
System.out.println("重新赋值前:"+num);
num = 10;
System.out.println("重新赋值后:"+num);
}
private static void testList(List list){
System.out.println("重新赋值前:"+ list.toString());
list.set(0, "A");
}
private static void testList2(List list){
System.out.println("重新赋值前:"+ list.toString());
list = new ArrayList();
list.add("A");
System.out.println("重新赋值后:"+list.toString());
}
private static void testChar(char a){
System.out.println("重新赋值前:"+ a);
a = 'A';
System.out.println("重新赋值后:"+a);
}
private static void testBoolean(boolean flag){
System.out.println("重新赋值前:"+ flag);
flag = false;
System.out.println("重新赋值后:"+flag);
}
private static void testObject(Tree obj){
System.out.println("重新赋值前:"+obj.toString());
obj.height = 100 ;
System.out.println("重新赋值后:"+obj.toString());
}
private static void testObject2(Tree obj){
System.out.println("重新赋值前:"+obj.toString());
obj = new Tree(100 , "琵琶") ;
System.out.println("重新赋值后:"+obj.toString());
}
}