python函数实参不是python变量的作用域域问题,高手来解释下下面的输出?

只需一步,快速开始
后使用快捷导航没有帐号?
查看: 868|回复: 5
关于函数作用域问题,使用DataFrame作为参数
累计签到:13 天连续签到:1 天&
主要问题是,定义一个函数,其中一个参数为DataFrame,何时会对原来的DataFrame进行操作,何时不会?如何确保不会?如何确保会?
例如:
import pandas as pd
import numpy as np
from pandas import DataFrame,Series
a=DataFrame(np.arange(12).reshape(3,4))复制代码
第一个函数:
def test1(df):
& & df=df+1
& & return df
b=test1(a)
a
b复制代码
这时发现a没有变化,而b为函数运行后的结果
第二个函数
def test2(df):
& & & & df[0]+=1 #对传入的df的第一列+1
& & & & return df
c=test2(a)
a
c复制代码
这时发现a和c均为函数运行后的结果(即a发生了改变)。
求解:何时会对原来的DataFrame进行操作,何时不会?如何确保不会?如何确保会?
先看看你的a是一个什么类型,如果是一个不可变类型,比如整形,调用函数后a就不会改变,如果是可变类型,如列表,就会改变
1. 如果您的提问得到满意的答案,请务必选择【最佳答案】;2. 如果想鼓励一下楼主或帮助到您的朋友,可以给他们【评分】作为奖励;3. 善用【论坛搜索】功能,那里可能有您想要的答案;4. 粘贴代码请点击编辑框上的
按钮,否则您的代码可能会被“吃掉”!
累计签到:320 天连续签到:1 天&
先看看你的a是一个什么类型,如果是一个不可变类型,比如整形,调用函数后a就不会改变,如果是可变类型,如列表,就会改变
1. 如果您的提问得到满意的答案,请务必选择【最佳答案】;2. 如果想鼓励一下楼主或帮助到您的朋友,可以给他们【评分】作为奖励;3. 善用【论坛搜索】功能,那里可能有您想要的答案;4. 粘贴代码请点击编辑框上的
按钮,否则您的代码可能会被“吃掉”!
累计签到:13 天连续签到:1 天&
先看看你的a是一个什么类型,如果是一个不可变类型,比如整形,调用函数后a就不会改变,如果是可变类型,如列表, ...
传入的参数是可变的,类型是DataFrame,如果要进行test2函数的操作,但是不想影响到a,应该如何做?
1. 如果您的提问得到满意的答案,请务必选择【最佳答案】;2. 如果想鼓励一下楼主或帮助到您的朋友,可以给他们【评分】作为奖励;3. 善用【论坛搜索】功能,那里可能有您想要的答案;4. 粘贴代码请点击编辑框上的
按钮,否则您的代码可能会被“吃掉”!
累计签到:320 天连续签到:1 天&
From FishC Mobile
bp2018 发表于
传入的参数是可变的,类型是DataFrame,如果要进行test2函数的操作,但是不想影响到a,应该如何做?
用copy模块的deepcopy
1. 如果您的提问得到满意的答案,请务必选择【最佳答案】;2. 如果想鼓励一下楼主或帮助到您的朋友,可以给他们【评分】作为奖励;3. 善用【论坛搜索】功能,那里可能有您想要的答案;4. 粘贴代码请点击编辑框上的
按钮,否则您的代码可能会被“吃掉”!
累计签到:13 天连续签到:1 天&
用copy模块的deepcopy
如果数据量很大,copy一个新数据的时间也不短。。。有没有不用copy的办法?谢谢
1. 如果您的提问得到满意的答案,请务必选择【最佳答案】;2. 如果想鼓励一下楼主或帮助到您的朋友,可以给他们【评分】作为奖励;3. 善用【论坛搜索】功能,那里可能有您想要的答案;4. 粘贴代码请点击编辑框上的
按钮,否则您的代码可能会被“吃掉”!
累计签到:320 天连续签到:1 天&
试了一下不用copy那么麻烦,用切片就可以:def fun(a):
& & b = a[:]
& & b += [3]
& & return b复制代码
1. 如果您的提问得到满意的答案,请务必选择【最佳答案】;2. 如果想鼓励一下楼主或帮助到您的朋友,可以给他们【评分】作为奖励;3. 善用【论坛搜索】功能,那里可能有您想要的答案;4. 粘贴代码请点击编辑框上的
按钮,否则您的代码可能会被“吃掉”!
小甲鱼强烈推荐 /1
特效不会给你基本工资,但却能让你升职加薪
- - - - - - - - - - - -
有备无患,念念不忘
•••(
Powered by1529人阅读
python中对象和变量是分开的。而类型属于对象和变量无关。变量有局部和全局一说,对象没有。对象由引用计数管理。在函数内部新建一个对象,它的销毁和函数作用域无关,由引用计数决定。
可以认为Python中的变量相当于C语言中的指针变量,指向一个对象。而类型属于对象和变量无关。
a = 1 a是变量,1是一个int类型的对象。
a = 2 &a是变量,2是一个int类型的对象,int类型对象是改变不了的,所以又建了一个对象2,a变量引用这个对象。id(a)参看变量引用对象已经改变。变量是没有类型的,只有对象有类型。
b = [1] &b是变量,[1]是一个list类型的对象。list类型的对象可以修改值。
b[0] = 2 &b变量还是引用的原来的对象 这点可以用id(b)查看。
在函数传参的时候,是变量赋值,如有一个函数如下,在调用时,参数a 引用了 b变量指向的对象,a,b是两个不同变量,但是指向同一对象。当改变a的赋值时,如果a指向的对象的类型是可以改变的,那么b所指向的对象也改变了,因为是同一个。如果不能改变,那么就新建一个对象,再让a 引用这个对象。
def func(a):
下面这个篇文章转自:http://blog.chinaunix.net/uid--id-3275808.html
Python中函数的参数传递问题,函数参数的传递往往是一个难以理解的概念,记得在C语言中有一个经典的例子如下所示:
我想这是大部分学过C语言的人都会遇到的一段代码,printf过后两个变量的值并没有发生改变,这是为什么呢?必然知道这是因为C语言的参数是采用值传递的形式,存在形参与实参的区别,也就是将实参的值复制给形参,在函数内部操作的都只是形参的内容,并不改变实参的值,所以变量在操作过后并没有发生改变。通常采用的方法是传递指针的形式,为什么传递指针又可以解决问题呢?这是因为传递的指针是指上就是一个地址值,也就是说形参和实参都指向了一段内存区域,在函数内部对内存区域的内容进行改变,这样就会影响到实参指向的内存区域,这样就实现了内存中数据的修改,进而实现数据的交换操作,这也是C语言中指针的经典操作之一。
但是到Python以后,我发现与C语言存在较大的差别,Python中万物皆对象,没有指针等特性使得我有些难以理解。Python万物皆对象的特征让我逐渐有了一定的理解。首先说明一下万物皆对象的问题。
这段代码主要是同id(object)判断了变量的ID号,这个ID号实质上也就表明了变量指向的对象(我这样认为的)。
IntNum&= 10,是指IntNum这个变量实质上是指向了一个int类型的对象10,同时Num1 = IntNum则表示Num1这个变量也指向10这个对象。同样Num2 = 10也表明了指向10这个int对象,可以通过id()判断。具体的实现原理是采用了一种叫做引用计数的技术完成的,这是解释器的实现,与使用者关系并不大。因此可以将左值看做变量,就如同10是对象,而IntNum就是对象的引用,是一个变量。变量赋值给变量相当于同一对象引用计数器增加1,而并不重新分配对象。
同样IntNum = 20,则是指重新分配一个对象20,让IntNum指向这个对象,这时10这个对象的引用计数器要减1,因为IntNum不在引用10这个对象啦。
前面单一元素的对象还比较容易理解,但是下面这个的对象就不一定能够理解啦。
上面的代码中我主要分析了列表和字典这两种Python中包含对对象的数据类型,我还是通过简单的id()操作判断指向的对象是否发生改变。
从结果可以看见,对于列表而言,当改变了列表中某一个局部对象后,列表的地址并没有改变,这样对象的id也就不能改变了。说明列表局部内容是可以修改的,但是列表对象的ID号(存储地址)不会发生改变。同样对于字典类型的数据也可以知道,让dict1、dict2分别指向两个字典对象,这两个字典对象的id号存在差别,当修改其中一个的内容使两个字典的内容一样,这时候判断ID,仍然是不同的,说明字典的也是可以修改的。
比如dict1['d']=5是指,原来‘d’指向的对象是4,这时候重新分配一个对象5,让‘d’指向这个对象5。这时候并不改变字典变量dict1的值(已分配字典对象的地址)。
综合上述,Python中的变量是一个对象的引用,变量于变量之间的赋值是对同一个对象的引用,当变量重新赋值对象时,指将这个变量指向一个新分配的对象。这是和C语言中的变量存在差别。但是Python中的变量有点类似C语言中的指针,指向的是一个对象,或者一段内存空间,这段内存空间的内容是可以修改的(这也是为什么对列表或者字典的某一个子对象进行修改并不改变字典或者列表的ID号),但是内存的起始地址是不能改变的,指针变量之间的赋值相当于两个指针变量指向同一块内存区域,在Python中就相当于同一个对象。因此可以认为Python中的变量相当于C语言中的指针变量。
接下来分析函数中的参数传递问题:由于在Python中函数的参数传递是值传递,同时也存在局部和全局的问题,这和C语言中的函数也存在一定的相似性。
函数的定义形式如下:
参数传递过程中存在两个规则:
1、通过引用将参数复制到局部作用域的对象中,意味着被用来访问函数参数的变量于提高给函数的对象无关,因为存在一个复制问题,这和C语言是相同的。而且修改局部对象不会改变原始数据。
2、可以在适当位置修改可变对象。可变对象主要就是列表和字典,这个适当位置实质上就是前面分析的局部子对象的修改不会改变字典对象或者列表对象的ID,也就是存储位置(这是我暂且这么称呼吧)。
通过两个实例说明,第一个还是交换问题:
从上面的结果可以看出来数据交换前后数据并没有发生改变,虽然在函数局部区域对传递进来的参数进行了相应的修改,但是仍然不能改变实参对象的内容。这和C语言中的操作非常相似,因为传递进来的三个指针在函数内部进行了相关的修改,相当于三个指针分别指向了不同的对象(存储区域),但是这三个指针都是局部指针并不改变实际的指针,所以交换前后实参指向的对象并没有发生改变。说明如果在函数内部对参数重新赋值新的对象,这并不会改变实参的对象。这就是函数的第一个规则。
对于不可变的对象,是不可能进行修改的,但是对于可变的对象(字典、列表),局部区域的值倒是可以改变的,这和前面分析的一样,看以参看下面的例子。
从上面的结果可以看出来,在函数内部修改列表、字典的局部对象或者说没有对传递进来的列表、字典变量重新赋值对象,而是修改变量的局部内容,这时候就会导致外部实参指向对象内容的修改,这就相当于在C语言中对指针指向的内存区域进行修改,这样的修改必然会导致实参指向区域内容的改变。这是函数规则的第二条,适当的位置指的是对对象进行修改,而不是重现分配一个对象,重现分配一个对象不会影响实参,而对对象的修改必然影响实参。
在C语言中返回多对象时必然会引入指针的操作,因为对指针的修改实质上会反映到实参,这样就实现了数据的返回操作。而在Python中采用元组的形式返回多个值。但是知道了函数参数的传递特性,我们完全可以采用函数的参数实现一些基本的操作,就比如刚开始讨论的交换问题,如下所示:
从上面的实验结果可知实现了数据的交换问题,这仅仅是为了说明利用参数计算的一些可能性,在实际中怎么运用我也是新手。
&&相关文章推荐
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:4945次
排名:千里之外
转载:11篇
(7)(2)(2)(2)用户名:苏浩智
文章数:82
访问量:2050
注册日期:
阅读量:1297
阅读量:3317
阅读量:455675
阅读量:1140286
51CTO推荐博文
一.使用函数编程的好处。大大的提高了代码的重用行,重复的逻辑或者操作,可以定义到一个函数里,多次调用。下面是关于提高代码重用性的例子。现在老板让你写一个监控程序,监控服务器的系统状况,当cpu\memory\disk等指标的使用量超过阀值时即发邮件报警,你掏空了所有的知识量,写出了以下代码。& while True:& & & if cpu利用率 & 90%:& & & & & #发送邮件提醒& & & & & 连接邮箱服务器& & & & & 发送邮件& & & & & 关闭连接& & & && & & if 硬盘使用空间 & 90%:& & & & & #发送邮件提醒& & & & &连接邮箱服务器& & & & &发送邮件& & & & &关闭连接& & &&& & &if 内存占用 & 80%:& & & & &#发送邮件提醒& & & & &连接邮箱服务器& & & & &发送邮件& & & & &关闭连接上面这段代码可以看出,每一段代码的重复的地方特别多。如果日后还需要增加群发功能,从上到下,所有的代码都需要去修改。def 发送邮件(内容)& & #发送邮件提醒& & 连接邮箱服务器& & 发送邮件& & 关闭连接& & &while True:& & && & if cpu利用率 & 90%:& & & & 发送邮件('CPU报警')& & && & if 硬盘使用空间 & 90%:& & & & 发送邮件('硬盘报警')& & && & if 内存占用 & 80%:& & & & 发送邮件('内存报警')这段代码就是使用了函数式编程写出的代码,从这段代码中可以有个很明显的特点,把重复的代码单独拿出来,定义了一个函数(也就是放到一个公共的空间,也相当于给这段代码起了个名字),如果以后需要多次使用这个代码,只要调用这个名字就可以了。二.关于python中函数的返回值。当定义了一个函数,如果不使用return给任何返回值,默认就会返回None。下面就是例子。def func01():& & print "this is func"def func02():& & print "this is func"& & return 2test1 = func01()&&&this is functest2 = func02()&&&this is funcprint test1&&&Noneprint test2&&&2在上面定义了两个函数,分别是func01和func02,这两个函数的功能都是打印输出同一句话“this is func”,func01没有定义任何返回值,返回给test1变量的是个空值(None),func02使用return定义了一个返回值,返回的是数字2,返回给test2的就是数字2。那么一个函数,在被调用一次的时候,是否可以同时!注意!是同时!返回多个值呢?(不同if分支判断中的return不能算同时!)我们来试一下。def func01():& & print "this is func"#定义的第一个函数func01,没有定义任何返回值。def func02():& & print "this is func"& & return 2#定义的第二个函数func02,定义了一个返回值,返回一个整数2。def func03():& & print "this is func"& & return 1& & return 2#定义的第三个函数func03,使用两次return,定义了两个返回值,分别是1和2。def func04():& & print "this is func"& & return 1,2,3,4#第四个函数func4,使用一个return返回四个整数,这四个整数使用逗号隔开。test1 = func01()test2 = func02()test3 = func03()test4 = func04()#将这四个函数的返回值分别赋给变量,然后看看每个函数的返回值都是什么。print test1&&&None#第一个函数的返回值为空,因为之前说了,函数如果不定义任何返回值,默认就返回None空值。print test2&&&2#第二个函数,返回了一个整数2,因为使用return指定了返回值,所以就返回了一个整数2,如果返回值是字典,那么return返回的也是个字典。(当返回值只有一个的时候,retrun什么数据类型,就会返回什么数据类型!!这点很重要!)print test3&&&1#第三个函数,在定义的时候写了两个return,但是只返回了一个值,这个值是整数1。#明明使用两个return,但是只返回了一个返回值?!~是的,没错,下面要强调一个关于python函数很重要的知识点!#当python函数在之行的时候,一旦遇到return,函数直接退出!return后面的所有代码都不会被执行!!!!!这点很重要!!!不信我们可以试试~现在我将func03函数做了一下修改。def func03():& & print "this is func"& & return 1& & print "hamasaki ayumi"& & return 2#我在两个retrun中间插了一行代码,打印字符串“hamasaki ayumi”,这个字符串放到了第一个return后面,看看会不会被执行test3 = func03()print test3&&&1#见证“奇迹”的时刻到了。#func03函数依旧只返回了第一个rerun中定义的整数1,第一retrun执行结束后,函数直接退出了,retrun 1后面的所有代码都没有被执行!这也就验证了刚才所说的,当函数执行时,遇到retrun,返回相应的返回值之后,函数直接退出,retrun后面的所有代码都不会被执行。print test4&&&(1, 2, 3, 4)#第四个函数,也只返回了一个元组,之前定义的返回值,1,2,3,4四个整数,被装进了同一个元组中,这也说明了一个问题,就是,当retrun要返回多个值的时候,会把这些同时返回的多个值,装进同一个元组中。那么可能有人会问,如果同时返回多种数据类型呢?在同一个返回值中,有字典,有列表,有元组会是什么效果?可以肯定的告诉你,返回的这些字典,列表,元组,字符串,统统会被打包进一个元组~做个测试吧。现在将func04函数做了一下修改。修改了一下func04的返回值,现在这个返回值里面充满了各种数据类型,有字典,有数字,有字符串,有元组,有列表,看看最后会返回值是什么。def func04():& & print "this is func"& & return 1,2,3,4,['a','b','c','d'],'hello',{'k1':'v1','k2':'v2'},(9,8,7,6,5)test4 = func04()print test4&&&(1, 2, 3, 4, ['a', 'b', 'c', 'd'], 'hello', {'k2': 'v2', 'k1': 'v1'}, (9, 8, 7, 6, 5))#可以看到这些不同数据类型的所有对象都被装进了同一个元组~接下来对函数的返回值做个小结:在python中,如果没有给函数定义任何返回值,默认返回值为None。python中的函数不可以同时retrun多次,当函数执行的时候,读到第一个retrun时,函数就会退出执行,retrun后面的代码都将不会被执行。在python的函数中,只return一个对象的话,return的对象是什么类型的数据,返回的就是什么类型的数据。在python函数中,如果要return多个对象,这些对象同时都会被装进一个元组并返回。关于python函数的返回值在这里就说完了,接下来说说函数传参。三.关于函数的参数。说的函数的参数,就要先说说形参和实参的概念了,我理解的型参,就是函数用来接受实际参数的一个“位置”,这个位置只有在函数在调用的时候,才会被分配到内存中去,一旦函数调用结束,型参所在的内存单元会被释放,换句话说,这种形参只能在函数内部使用。def func1(a,b):& & c=a+b&&&&return c#上面定义了一个函数,函数名为func1,里面的a和b就是型参。下面在说说实参,实参可以是表达式,常量,变量,函数,以及各种对象....总之要有实际的值,因为必须要有确定的值,才可以把它传递给型参。(实参,就是调用函数时,给函数传的值或参数。)print func1(1,2) #调用上面定义的func1函数&&&3在调用func1函数时,传给函数内部型参的1,2就是所谓的实参。下面来说说函数的传参方式。按形参的顺序给函数传递参数。def func1(name,city):& & print "name:%s city:%s" %(name,city)#定义了一个函数,里面定义了两个形参,分别是name和city。func1 ("hamasaki ayumi","Fukuoka")#刚刚定义的func1函数定义了两个形参,先定义的name,再定义的city,现在按照刚刚定义的顺序给函数传参。&&&name:hamasaki ayumi city:Fukuoka参数被按顺序传递到了函数中。如果传参时不按顺序呢?func1 ("Fukuoka","hamasaki ayumi")&&&name:Fukuoka city:hamasaki ayumi#所传的参数顺序反过来了。& 2.手动指定形参和实参的对应关系,一个函数中定义了很多型参,这些形参的顺序很难记住,如果不想按照型参的顺序去传递参数,在调用函数的时候,可以手动指定哪个形参去接收哪个实参。(这个也叫叫关键字参数!)def func1(name,city):& & print "name:%s city:%s" %(name,city)func1 (city = "Fukuoka",name = "hamasaki ayumi")&&&name:hamasaki ayumi city:Fukuoka从这可以看出,手动指定了形参和实参的对应关系,即使不按照顺序去传参数,参数传递的位置也不会出错。(前提是形参和实参的顺序一定要对应)3.默认参数,默认参数是函数在定义过程中,在形参中指定的,当调用函数的时候,没有给该函数的形参传递指定的实参,那么这个形参直接等于在创建函数时定义的默认参数。(说简单一点,就是在定义函数的时候,给了某个参数一个默认参数,如果没传参数,这个参数直接等于它的默认值)下面是关于默认参数的例子:def func2(name,sex="male"): & #创建函数,定义了一个默认值sex默认等于male& & print "name:%s sex:%s" %(name,sex)func2("name = ayumi") &#在调用函数的时候,只传了一个参数,在调用函数的时候没有给sex传参,sex默认就等于了male。&&&name:ayumi sex:malefunc2(name = "ayumi",sex = "female")再次调用下func2函数,给sex也传一个参数,看看是否可以覆盖默认值。&&&name:ayumi sex:female三.函数参数收集。一次性传递多个参数,将多个参数传递到一个元组中。如果想要让一个形参可以收集多个值,但值这些值会被装进元组,当作一个参数传递给函数,想实现这种效果,只要在定义函数时,给指定的形参前面加一个*星号就可以做到。def func1(a,*args): #定义了两个形参,分别时a和args,其中在args前面加了一个*,来看看传参的效果是怎么样的。& & print a& & print argsfunc1(1,2,3,4,5,6)&&&1&&&&&&&&(2, 3, 4, 5, 6)参数1给了a,2,3,4,5,6打包成了元组,传给了args。在参数前面加上*就是可变参数。在函数内部,参数args接收得到的是一个tuple元组,调用该函数时,可以传入任意个参数,包括0个参数(如果不给可变的形参传递任何参数,它默认就会等于一个空元组)。我们可以来测试下。def func1(a,*args):& & print a& & print argsfunc1(1)#调用func1函数的时候,没有给args传递任何实参数,看看会返回什么。&& 1&&&&&&()args返回了一个空的元组。当*args遇到关键字参数的时候会出现什么样的效果呢?func1(1,k1='aaa')Traceback (most recent call last):& File "/Users/macbook/PycharmProjects/untitled1/2day.py", line 5, in &module&& & func1(1,k1='aaa')TypeError: func1() got an unexpected keyword argument 'k1'#结果抛出了个异常~如果想传递多个关键字参数给函数,在定义形参的时候,需要在前面加**两个星号,下面就来说说这种可以传递多个关键字参数的写法。&&&&2.一次性传递多组参数,每组参数被传递到一个字典中,作为字典的键值对。让一个形参可以收集多个关键字参数,每个关键字参数都将作为键值对被装进同一个字典,当作一个参数传递给函数,想实现这种效果,只要在定义函数时,给指定的形参前面加两个**星号,就可以实现这种功能。def func1(a,**kwargs):& & print a& & print kwargsfunc1(1,k1='aaa',k2='ccc',k3='ddd')&&&1&&&&{'k3': 'ddd', 'k2': 'ccc', 'k1': 'aaa'}func1(1)&&&1&&&&&&&&{}和一个*星号一样,**星号依旧是可变的形参,两个星号的形参如果不传递任何参数,默认会返回一个空字典。3.当*单个星号和**两个星号,还有普通的形参放在一起用,会出现什么效果?def test_func(a,b,c,*args,**kwargs):& & print "a = %s" %(a)& & print "b = %s" %(b)& & print "c = %s" %(c)& & print "args= ",args& & print "kwargs =",kwargstest_func(1,2,3,4,5,6,k1="v1",k2="v2")输出结果如下:a = 1b = 2c = 3args= &(4, 5, 6)kwargs = {'k2': 'v2', 'k1': 'v1'}四,分解序列传参。在给python函数传递参数时,如果需要把一个列表,或者元组中的每一个元素拆开,依旧可以适用*单个星号来实现。不过需要注意的是!!!这次的星号不是放在定义函数的形参前面的!!而是放在实参前面的。举个例子就可以看明白了。def func1(*args): &#定义了一个函数,给了这个函数一个可变的形参。& & print args接下来调用func1函数,给这个函数的可变的形参传一个列表。func1([1,2,3,4,5,6])&&&([1, 2, 3, 4, 5, 6],)最后,返回了一个元组,整个列表变成了元组中的一个元素。接下来,我们在给函数传参的时候,在序列前面加一个*星号,看看会出现什么效果。func1(*[1,2,3,4,5,6])&&&(1, 2, 3, 4, 5, 6)&#从显示的结果可以看到,列表中的每一个元素,都单独作为一个元素,放在了元组里面。而不是将一个列表当成一个元素放进元组里。补充!*星号基本的序列都可以拆开,比如说字符串~func1(*"hello")&&&('h', 'e', 'l', 'l', 'o')func1("hello")&&&('hello',)这是加星号和不加星号的对比。字符串中每一个字母都被拆开,放进了元组。五.如何把字典直接传进函数。现在有一个字典类型的值,需要把这个字典完整的传到函数中,该怎么做?首先,我们先来试试,在调用函数的时候,只使用一个*信号,看看能不能把字典传到列表中。def func1(*args):& & print args首先,试试第一种方法,在函数中定义了一个可变的形参,接受所有参数,将所有接收到的参数,都放到同一个元组中。func1({'k1':'v1','k2':'v2'})&&&({'k2': 'v2', 'k1': 'v1'},)在调用func1函数的时候,传参,不做任何分解,直接把字典传给那个可变的形参*args,最后的结果就是,这个字典的确是被传到了函数内部,但是这个字典被放到了一个元组中。然后我们在试试第二个方法。还是上面定义的func1函数,在把字典传进去之前,在前面加个*号,看看会是什么效果。func1(*{'k1':'v1','k2':'v2'})&&&('k2', 'k1')只把两个key放到了元组中,没有达到想要的结果。第三种方法,在传参的时候使用两个**星号。def func2(**kwargs):& & print kwargsfunc2(**{'k3': 'v3', 'k2': 'v2', 'k1': 'v1'})&&&&{'k3': 'v3', 'k2': 'v2', 'k1': 'v1'}使用两个**星号,成功的把字典原原本本的传到了函数中。补充!如果只是按照函数赋值的角度来说的话,func2(**{'k3': 'v3', 'k2': 'v2', 'k1': 'v1'}) 和func2(k1 = 'v1',k2 = 'v2',k3 = 'v3'),效果是一模一样的。五.关于函数与作用域。说到作用域,就要先来说说变量,什么是变量,变量就是一个值的名字,现在定义一个变量为x = 1,也就是将1这个整数赋值给了x,然后通过引用x这个变量名,可以获得这个整数1,这种形式和字典特别像,通过一个key,去取对应的value,这个变量名和变量值的对应所用的是个看不见的字典,如果想看这个‘字典’,可以使用var()函数来进行查看。注意啦!!!与作用域息息相关的三个函数var()函数和locals()函数还有global() 这三个函数会放在面向对象的时候一起做补充~~在这里只是简单说一下,先挖个坑~嘿嘿。这种我们看不到的“字典”就是作用域,除了全局的作用域外,每个函数调用都会创建一个新的作用域。下面,来看一个现象。#!/usr/bin/python2.7# -*- coding:utf-8 -*-def foo(): & &#首先,定义了一个函数,这个函数里面定义了一个x=130,然后在函数的外面定义了一个变量,x = 1,执行foo()函数,那么最后x等于多少?& & x = 130x = 1foo()print x&&&1最后,x还是等于1。原本以为执行了foo函数后,会重新定义x变量,但是函数执行结束后,x的值并没有发生改变,这就是因为在调用函数foo的时候,新的命名空间被创建了,x = 130只在这个变量的内部作用域(局部的命名空间)起作用,不会影响到函数外部(全局)作用域中的x变量,因此,函数内部的变量都被称为局部变量。但是如果要在函数的内部访问全局变量该怎么做?如果在函数内部,只是单纯的读取全局变量,是完全没有问题的,只要不重新赋值就没有任何问题。(补充~使用global声明或者全局变量是列表,字典之类的可辨类型除外)不信可以来测试一下。def f1():& & print xx = 1f1()&&& 1从上面的测试可以看出,如果在函数内部只是访问全局变量的话,是没有任何问题的。如果函数中有global关键字,那么被global声明的变量,直接变成全局变量。注意啦!!!!!读取全局变量一般来说,不会出什么问题,但是啊,如果局部变量名或者参数名,和想要访问的全局变量中的变量名有冲突,那么,在这个函数中,就无法直接访问这个全局变量了,局部变量会把全局变量给临时性的“覆盖”。说的通俗点,就是当局部变量和全局变量名相同,在函数内部访问的话,以局部变量为准!!说到了变量和作用域,又不得不提到三个函数,分别是vars(),globals(),locals(),这三个函数在本篇文章中,只做个简单的解释,等博主写到面向对象编程时,会详细说下这三个函数的具体功能。locals() 首先上官方解释:Update and return a dictionary containing the current scope's local variables. 博主英语比较渣,查了下不会的单词,大概可以翻译为,更新或者返回一个字典,这个字典中包含了当前范围的本地变量。locals这个函数,根据博主亲测,当放到哪个作用域,就会以字典的形式返回当前作用域中的所有名称空间的所有值(所有的变量名和变量值)。x = 1def f1():& & a = "abc"& & print "locals = ",locals()f1()locals = &{'a': 'abc'}print locals(){'f1': &function f1 at 0x102efa668&, '__builtins__': &module '__builtin__' (built-in)&, '__file__': '/Users/macbook/PycharmProjects/untitled1/test.py', '__package__': None, 'x': 1, '__name__': '__main__', '__doc__': None}#分别在全局作用域和f1函数中都调用了一次locals()函数,结果已经很明显了,在f1函数内部调用locals函数后,返回了f1函数作用域中所有的变量名以及对应的变量值,接着,又在函数外,也就是全局作用域调用了一下locals()函数,结果返回了全局作用域中的所有变量名和变量值(名称空间)的使用情况。&&&&2.globals()&Return the dictionary containing the current scope's global variables. 返回一个字典,包涵当前范围全局变量。globals函数,用于返回全局变量中所有的变量名以及变量值( 名称空间)的使用情况。globals函数不管放在任何作用域,显示的都是全局作用域中的变量名和变量值。&&&&3.vars() &这个函数原本想放在面向对象的时候在详细说说的,因为涉及到了“对象”相关的知识点。先来看看官方怎么解释吧。def vars(p_object=None): # re restored from __doc__& & """& & vars([object]) -& dictionary& &&& & Without arguments, equivalent to locals().&&&&#没有参数时,相当于locals()函数。#这句话就是说,当vars()函数在没传任何参数之前,和locals()函数是一模一样的。& & With an argument, equivalent to object.__dict__.#有一个参数传进来的时候,作用就相当于一个对象的__dict__方法。& & """& & return {}官方的意思就是说vars()这个函数如果不加参数,功能和local函数一样,如果加了参数,就可以以字典的方式,返回这个对象内部的变量,在python这门语言中,一切皆对象,变量名,函数名,都是对象,如果想看这些对象内部都包涵什么变量的话,vars(对象名)就可以看到了,给vars函数传一个对象的名称。def f1():& & a = 1 #在这个例子中,我特别在函数中定义了一个变量a = 1,是为了防止f1对象中变量和f1函数的变量混淆& & passvars(f1)&&&{'x': 1}vars(对象名) = 对象.__dict__ 这两个作用是一模一样的。下面补充一个关于globals()函数使用的小技巧!当函数内部变量和全局环境下的变量名一模一样,这时两个一模一样的变量名,就会以函数内部的变量值为准,这样,函数就无法访问这个函数外部同名的全局变量了,如果想访问函数外部的全局变量,就可以借助globals() 函数去拿全局变量。下面是例子:在没有使用globals()函数之前,是无法访问同名全局变量的。name = "suhaozhi"def test1():& & name = "hamasaki"& & print nametest1()&&&hamasaki函数test1想要访问外部的name = “suhaozhi”这个变量,但是只能访问到函数内部的name = “hamasaki”。接下来,我们就使用globals()函数去取全局name变量。name = "suhaozhi"def test1():& & name = "hamasaki"& & print globals()['name']test1()&&&suhaozhi全局变量中name的值被成功取到了。六.在python中函数不只可以向后引用,也可以向前引用。下面是两个例子:最常见的向后引用。def func1():&&&&print 'in the func1'def func2():& & print "in the func2"& & func1()func2()&&& in the func2in the func1&&&&2.向前引用。def func2():& & print "in the func2"& & func1()def func1():& & print 'in the func1'func2()&&& in the func2in the func1七.函数嵌套。首先,来看一个函数嵌套的例子,然后分析它的执行过程。name = "suhaozhi"def f1():& & name = "f1"& & def f2():& & & & name = "f2"& & & & print name& & f2()& & print namef1()print name开始执行f1函数。第一步:在f1内部加载局部变量name = “f1”.第二步:加载f2函数。第三步:执行f2函数,并先打印一个字符串“f2”。第四步:f1内部的f2函数执行完毕,退出到f1函数,打印f1中的name变量,打印“f1”字符串。第五步:f1函数执行完毕,退出函数,开始打印全局变量name = "suhaozhi"。八. 函数递归。函数自己调用自己,就是所谓的递归。def func1():& & print "in the func1"& & func1()func1()#上面这个例子,就是基于函数递归的特性完成的一个“死循环”。func1函数做了两个动作,输出一个"in the func1"然后再次执行func1函数,然后再输出"in the func1"...会一直循环下去。所以说,在做函数递归的时候,必须有个明确的结束条件(使用return去终止)。说简单点,就是在函数中使用if条件判断,然后在指定的条件下return。下面是一个使用函数递归实现的数字递减。def func1(n):& & print n& & if n - 1 == 0:& & & & return n& & return func1(n - 1)func1(10)#调用func1函数,给n传了一个整数10.#现在n = 10 打印一次变量n会显示一次10#开始if判断,10 - 1不等于0,条件不成立,则执行下面的return func1(n - 1),也就是在函数内部继续调用func1函数,并且把n - 1#这时,n = 9 再次进入if判断 9 - 1也不等于0,条件不成立,然后继续调用func1函数 n继续 - 1 ,此时n = 8 .........以此类推.#一直到n == 0 执行retrun n第二点,每次更深入一层递归,问题规模要比上次递归有所减少。关于这个问题,前两天看到了一个很不错的例子。# -*- coding:utf-8 -*-import timeperscon_list = ['a','b','c','d','ayumi']def find_person(person_list):& & print '*' * 60& & person = person_list.pop(0)& & if person_list == False:& & & & print "We don't even know her~"& & & & return None& & if person == "ayumi":& & & & return "Everyone is a bitch jolin,right?she is a bitch jolin"& & & & #print "%s:Everyone is a bitch jolin,right?she is a bitch join" %(person)& & print "请问%s,你认识bitch jolin吗?" %(person)& & print "i don't know her~ But I can help you ask ",person_list& & time.sleep(3)& & re = find_person(person_list)& & print "%s问的结果是,%s" %(person,re)& & return reprint find_person(perscon_list)上面这个例子的输出结果是:************************************************************请问a,你认识bitch jolin吗?i don't know her~ But I can help you ask &['b', 'c', 'd', 'ayumi']************************************************************请问b,你认识bitch jolin吗?i don't know her~ But I can help you ask &['c', 'd', 'ayumi']************************************************************请问c,你认识bitch jolin吗?i don't know her~ But I can help you ask &['d', 'ayumi']************************************************************请问d,你认识bitch jolin吗?i don't know her~ But I can help you ask &['ayumi']************************************************************d问的结果是,Everyone is a bitch jolin,right?she is a bitch jolinc问的结果是,Everyone is a bitch jolin,right?she is a bitch jolinb问的结果是,Everyone is a bitch jolin,right?she is a bitch jolina问的结果是,Everyone is a bitch jolin,right?she is a bitch jolinEveryone is a bitch jolin,right?she is a bitch jolin(第二个例子是真正意义上的递归,实现了返回值回传)递归效率不高,递归层次过多会导致栈溢出(在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出)堆栈扫盲/lln7777/archive//2396164.html&尾递归优化:http://egon09./2475关于函数递归的最后补充:函数自己调用自己,就是所谓的递归,当函数调用自身时,当前函数,和在函数内部调用的“自己”实机上已经是两个不同的函数了,(也就是同一个函数使用了两个不同的命名空间)。本文出自 “” 博客,请务必保留此出处
了这篇文章
类别:┆阅读(0)┆评论(0)

我要回帖

更多关于 python变量的作用域 的文章

 

随机推荐