前段时间学习过python,但是只是学了个皮毛。现在想用它来做数据分析,因此决定好好学习!本篇文章是在阅读python官方文档的过程中抽取出来的一些知识点。
python 函数调用过程中,变量的引用过程:
The execution of a function introduces a new symbol table used for the local variables of the function. More precisely, all variable assignments in a function store the value in the local symbol table; whereas variable references first look in the local symbol table, then in the local symbol tables of enclosing functions, then in the global symbol table, and finally in the table of built-in names. Thus, global variables cannot be directly assigned a value within a function (unless named in a global statement), although they may be referenced.
python的函数 调用会为函数局部变量生成一个新的符号表。确切的说,所有函数中的变量赋值都是将值存储在局部符号表。变量引用首先在局部符号表中查找,然后是包含函数的局部符号表,然后是全局符号表,最后是内置名字表。因此,全局变量不能在函数中直接赋值(除非用 global 语句命名),尽管他们可以被引用。
其中,“包含函数的局部符号表”,就是调用函数的那个块的局部符号表。
被调用的函数(callee)引用的实参在它被调用时引入局部符号表,因此,实参总是 传值调用 (这里的 值 总是一个 对象引用 ,而不是该对象的值)。一个函数被另一个函数调用时,一个新的局部符号表在调用过程中被创建。
有关python 函数参数传递的一些问题:
例子:
i = 5f ( ) 仍会打印5
def f(arg=i):
print(arg)
i = 6
f()
对应list、dictionary这些可变的数据结构,又有所不同:
def f(a, L=[]):输出为:
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
[1]
[1, 2]
[1, 2, 3]
你可能会有疑问,明明每次调用函数f 的时候,都让 L=[]了,为什么还是累计了。
这是因为:在python的实现中,如果赋值方是可变类型(mutable type)的话,这个可变类型的变量是存在于 “包含函数f的局部符号表中的”,并不是在函数f的局部符号表中。
[],列表类型,是可变的,因此“[]”对应一个在函数外部定义的列表。而L=[],实际上是将这个外部列表的指针赋给L,并不是真的把一个空列表赋给L。调用f(1)、f(2)、f(3)的过程中,这个外部定义的列表的内容就会增加。
这种情况当且仅当函数参数列表里赋给L的是一个可变参数类型(mutable type)的时候,才会如此实现。
python内部这样的实现让人费解,不知道它为什么要这样做。
如果不想让L累计元素,可以这样写:(None是空类型)
def f(a, L=None):
if L is None:
L = []
L.append(a)
return L
print(f(1))
print(f(2))
print(f(3))
输出为:
[1]
[2]
[3]
和上一个例子的解释互补,因为None是一个不可变的类型(immutable type),所以None定义在函数 f 的局部符号表中。每次调用函数的时候,都真的会使L=None。进而,每次调用都会使L=[]。
python里边,函数的调用可能需要传递参数,有多种方式:default arguments、keyword arguments ...
default arguments 就不说了
Keyword Arguments
调用函数的时候,传参方式为:"argName=xxx"的参数,就是Keyword Arguments。不是这样传参的参数就叫做Positional Arguments,顾名思义,它们是看位置来对号入座的。有几点要注意:
keyword arguments一定要放在positional arguments后面
keyword arguments指定的argName一定是函数的prototype中有定义的
keyword arguments之间的位置可以随意
一个argument不能被传多于一个的值
此外,所有传参的方法都要遵守的规则就是 “参数个数不能传多”
在python函数的prototype中,参数名前加了" * " 则说明需要传入一个元组(tuple),参数名前加了“ ** " 则说明需要传入一个字典(dictionary)。规定函数的prototype中,元组类型参数 *name 要在 字典类型参数**name之前。比如这样:
def cheeseshop(kind, *arguments, **keywords):
print("-- Do you have any", kind, "?")
print("-- I'm sorry, we're all out of", kind)
for arg in arguments:
print(arg)
print("-" * 40)
keys = sorted(keywords.keys())
for kw in keys:
print(kw, ":", keywords[kw])
Arbitrary Argument Lists
python里边比较少用的是这种“任意参数列表”。使用一个元组 *argName 来接受任意多个参数,如果后面有一个没有设置默认参数的参数,则须用‘Keyword-only'即Keyword argument的方式来表明, "argName=xxx"。
例子:
>>> def concat(*args, sep="/"):
... return sep.join(args)
...
>>> concat("earth", "mars", "venus")
'earth/mars/venus'
>>> concat("earth", "mars", "venus", sep=".")
'earth.mars.venus'
上面这个例子给sep设置了默认参数"/",所以可以不用Keyword方式来表明。
如果没设默认参数,在调用函数的时候,也没有用Keyword方式来表明,就会出错:
Unpacking Argument Lists
这个讲的是当要给函数传的参数蕴含在元组、列表、字典中的时候,如何把它们从这些数据结构里面拆出来并传给函数--用 * 来拆list、tuple,用 ** 来拆dictionary。具体的例子,请参考python官方文档。
Lambda Expressions
这个讲的是python的lambda表达式。lambda表达式一行写就,主要有两个用途:
1. 充当一个简短的匿名函数 2. 在1的基础上,作为一个函数传递给别的函数
作用1例子:
>>> def make_incrementor(n): #函数make_incrementor(n),返回一个lambda函数
... return lambda x: x + n #返回的lambda函数->参数列表: x;返回值: x+n
...
>>> f = make_incrementor(42) #得到一个lambda函数->参数列表: x;返回值: x+42
>>> f(0)
42
>>> f(1)
43
>>> pairs = [(1, 'one'), (2, 'two'), (3, 'three'), (4, 'four')]
>>> pairs.sort(key=lambda pair: pair[1])
>>> pairs
[(4, 'four'), (1, 'one'), (3, 'three'), (2, 'two')]
文档字符串:
在函数体的前面用文档字符串来描述函数是一个很好的习惯,具体请看官方文档
函数批注:
这个是大概是用来描述函数的参数,还有传参情况的。
编程风格:
python提供了PEP-8这个风格标准供程序员参考,具体请看官方文档。个人感觉只要简洁美观就是好的风格。
References:
[1] https://docs.python.org/3/tutorial/controlflow.html
[2] http://www.pythondoc.com/pythontutorial3/controlflow.html#range