python函数定义

python函数

def函数定义

def <name>(arg1, arg2,... argN):    
    <statements>
    return <value>

与C这样的编译语言不同,Python函数在程序运行之前并不需要全部定义。更确切地讲,def在运行时才进行评估,而在def之中的代码在函数调用后才会评估。

global语句

它并不是一个类型或大小的声明,它是一个命名空间的声明。它告诉Python函数打算生成一个或多个全局变量名。也就是说,存在于整个模块内部作用域(命名空间)的变量名。

X = 88                   # Global X
def func():    
    global X    
    X = 99               # Global X: outside deffunc()
print(X)                 # Prints 99

y, z = 1, 2              # Global variables in module
def all_global():    
    global x             # Declare globals assigned    
    x = y + z            # No need to declare y, z: LEGB rule

原则

  • 最小化全局变量
  • 最小化文件间的修改
    这种隐含的跨文件依赖性,在最好的情况下会导致代码不灵活,最坏的情况下会引发bug。
    ```python

    first.py

    X = 99 # This code doesn’t know about second.py

second.py

import first
print(first.X) # Okay: references a name in another file
first.X = 88 # But changing it can be too subtle and implicit

升级后
```python
# first.py
X = 99
def setX(new):    
    global X    
    X = new

# second.py
import first
first.setX(88)

文件模块使用全局变量

# thismod.py
var = 99            # Global variable == module attribute

def local():    
    var = 0                            # Change local var

def glob1():    
    global var                         # Declare global (normal)    
    var += 1                           # Change global var

def glob2():    
    var = 0                            # Change local var    
    import thismod                     # Import myself    
    thismod.var += 1                   # Change global var

def glob3():    
    var = 0                            # Change local var    
    import sys                         # Import system table    
    glob = sys.modules['thismod']      # Get module object (or use __name__)    
    glob.var += 1                      # Change global var

def test():    
    print(var)  #99    
    local(); glob1(); glob2(); glob3()    
    print(var)  #102

嵌套作用域举例

def f1():    
    X = 88   
    def f2():       
        print(X)     # Remembers X in enclosing def scope   
    return f2        # Return f2 but don't call it   
action = f1()        # Make, return function
action()             # Call it now: prints 88

在这个代码中,我们命名为f2的函数的调用动作的运行是在f1运行后发生的。f2记住了在f1中嵌套作用域中的x,尽管f1已经不处于激活状态。有点费解 这都可以。

通常来说,类是一个更好的像这样进行“记忆”的选择,因为它们让状态变得很明确。不使用类的话,全局变量、像这样的嵌套作用域引用以及默认的参数就是Python的函数能够保留状态信息的主要方法了。为

nonlocal语句

nonlocal和global一样,声明了将要在一个嵌套的作用域中修改的名称。和global的不同之处在于,nonlocal应用于一个嵌套的函数的作用域中的一个名称,而不是所有def之外的全局模块作用域;

>>>def tester(start):
...    state = start              # Each call gets its own state
...    def nested(label):
...        nonlocal state         # state必须提前声明 不然会报错
...        print(label, state)
...        state += 1              # Allowed to change it if nonlocal
...    return nested
...
>>>F = tester(0)
>>>F('spam')                       # Increments state on each call
spam 0
>>>F('ham')
ham 1
>>>F('eggs')
eggs 2

函数参数

  • 不可变参数“通过值”进行传递。像整数和字符串这样的对象是通过对象引用而不是拷贝进行传递的,但是因为你无论怎样都不可能在原处改变不可变对象,实际的效果就很像创建了一份拷贝。
    • 可变对象是通过“指针”进行传递的。例如,列表和字典这样的对象也是通过对象引用进行传递的,这一点与C语言使用指针传递数组很相似:可变对象能够在函数内部进行原处的改变,这一点和C数组很像。
不可变对象
>>> x = 1
>>> id(x)
19852392
>>> x +=1
>>> id(x)
19852368
不可变对象的函数传参
>>>def f(a):    # a is assigned to (references) passed object
...    a = 99   # Changes local variable a only
...
>>>b = 88
>>>f(b)         # a and b both reference same 88 initially
>>>print(b)     # b is not changed
88
可变对象的函数传参
>>>def changer(a, b):  # Arguments assigned references to objects
...    a = 2          # Changes local name's value only
...    b[0] = 'spam'   # Changes shared object in-place
...
>>>X = 1
>>>L = [1, 2]          # Caller
>>>changer(X, L)       # Pass immutable and mutable objects
>>>X, L                # X is unchanged, L is different!
(1, ['spam', 2])
避免可变参数的修改

方式一

L = [1, 2]
changer(X, L[:])   # Pass a copy, so our 'L' does not change

方式二

def changer(a, b):   
    b = b[:]             # Copy input list so we don't impact caller
    a = 2
    b[0] = 'spam'            # Changes our list copy only

方式三 转换为元组

L = [1, 2]
changer(X, tuple(L))       # Pass a tuple, so changes

特定的参数匹配模型

  • 位置:从左至右进行匹配
    一般情况下,也是我们迄今为止最常使用的那种方法,是通过位置进行匹配把参数值传递给函数头部的参数名称,匹配顺序为从左到右。

  • 关键字参数:通过参数名进行匹配
    调用者可以定义哪一个函数接受这个值,通过在调用时使用参数的变量名,使用name=value这种语法。

  • 默认参数:为没有传入值的参数定义参数值
    如果调用时传入的值过于少的话,函数能够为参数定义接受的默认值,再一次使用语法name=value。

  • 可变参数:收集任意多基于位置或关键字的参数
    函数能够使用特定的参数,它们是以字符*或**(*对应序列,**对应key=value)开头,收集任意多的额外参数(这个特性常常叫做可变参数,类似C语言中的可变参数特性,也能够支持可变长度参数的列表)。

  • 可变参数解包:传递任意多的基于位置或关键字的参数
    调用者能够再使用*语法去将参数集合打散,分成参数。这个“*”与在函数头部的“*”恰恰相反:在函数头部它意味着收集任意多的参数,而在调用者中意味着传递任意多的参数。

  • Keyword-only参数:参数必须按照名称传递
    在Python 3.0中(不包括Python2.6中),函数也可以指定参数,参数必须用带有关键参数的名字(而不是位置)来传递。这样的参数通常用来定义实际参数以外的配置选项。

关键字参数
>>>def f(a, b, c): print(a, b, c)
#普通调用
>>>f(1, 2, 3)

#关键字参数
>>>f(c=3, b=2, a=1)

#默认参数
>>>def f(a, b=2, c=3): print(a, b, c)
>>>f(1)


#收集参数

>>>def f(*args): print(args)
>>>f(1, 2, 3, 4)(1, 2, 3, 4)

>>>def f(a, *pargs, **kargs): print(a, pargs, kargs)
>>>f(1, 2, 3, x=1, y=2)
1 (2, 3) {'y': 2, 'x': 1}

#解包参数

#元组
>>>def func(a, b, c, d): print(a, b, c, d)
...
>>>args = (1, 2)
>>>args += (3, 4)
>>>func(*args)
1 2 3 4

#字典
>>>args = {'a': 1, 'b': 2, 'c': 3}
>>>args['d'] = 4
>>>func(**args)

varargs调用语法 暂时不动有卵用

def tracer(func, *pargs, **kargs):         # Accept arbitrary arguments    
    print('calling:', func.__name__) 
    return func(*pargs, **kargs)           # Pass along arbitrary arguments

def func(a, b, c, d):    
    return a + b + c + d

print(tracer(func, 1, 2, c=3, d=4))

Keyword-Only参数

keyword-only参数——即必须只按照关键字传递并且不会由一个位置参数来填充的参数。

>>>def kwonly(a, *, b, c='spam'):
...    print(a, b, c)
...
>>>kwonly(1, b='eggs')
1 eggs spam

>>>kwonly(1, c='eggs')
TypeError: kwonly() needs keyword-only argument b

>>>kwonly(1, 2)
TypeError: kwonly() takes exactly 1 positional argument (2 given)

keyword-only参数必须在一个单个星号后面指定,而不是两个星号——命名的参数不能出现在**args任意关键字形式的后面,并且一个**不能独自出现在参数列表中。这两种做法都将产生语法错误:

#这里不用怀疑 语法分析 那块估计会有问题
>>>def kwonly(a, **pargs, b, c):
SyntaxError: invalid syntax
>>>def kwonly(a, **, b, c):
SyntaxError: invalid syntax

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 68813175@qq.com