当前位置:   article > 正文

python基础:函数_def func(x,y=3,z=4): pass print(func.__defaults__)

def func(x,y=3,z=4): pass print(func.__defaults__)

1、函数总结

 

2、函数定义

def function(参数1,参数2……):

    ‘’‘函数的功能简介’‘’

    //函数代码块

  • def:用来定义函数
  • arg:函数的参数,非必填,函数可以没有参数
  • 代码块:代码块是函数代码块,如果为空,需用pass来作为占位符。
  • return:函数返回,如没有返回,可写成return None或return

 

3、函数对象、函数调用

a = func()

  • func:函数对象
  • func():函数调用
  • a:用以接收函数调用执行函数后的返回值,因函数执行完后,函数会自行销毁,函数体内的变量引用、函数结果也释放。为保留函数的结果,用变量a来接收函数的返回值。

注意:一定不要弄混淆这几个概念。

函数对象,是python中的一个对象,包括对象的3要素:id,type、value;函数对象,是在解释器启动的时候,已经存放到内存空间,存在的是函数的代码部分,并没有存储数值。

函数调用,是指调用函数,开始执行,并为函数的数据分配栈内存空间,当函数执行完后,分配的数据也销毁。

函数调用中,注意参数是可变对象还是不可变对象。如果是可变对象,函数内的操作会影响原有可变对象,如果是不可变对象,对原来传入的不可变对象没有影响,因为不可变对象会创建新的对象,并不会影响原来的不可变对象。

# 是指b和a同时都指向函数对象

func()

ref_func = func

a = func()    # 函数执行完后,用a来接收func()调用后的执行结果

 

4、函数参数

4.1、位置参数

def func(x, y, z):

    // 代码块

  • x,y,z这3个均为位置参数。位置参数是必填参数。位置参数的类型可以为任意参数类型。位置参数的个数、位置必须一一对应。

 

4.2、默认参数

def  func(name, age, city="bj"):

    // 代码块

# 调用有默认参数的函数

func("Kitty", 18, city="nj")  //city=“nj”会覆盖默认取值bj

func("Mike", 20, "sh")  // city="sh"会覆盖默认取值bj

  • city="bj"是默认参数,默认参数在未明确指定参数的时候,是默认参数;也可指定默认参数的取值。
  • 默认参数在位置参数后面。
  • 默认参数的坑:默认参数尽量为不可变对象,如果为可变对象,每次调用得到的结果可能不一致,下面会有详细讲解。

 

4.3、不定长参数

def func(*args, **kwargs):

    // 代码块

  • *args和**kwargs均为不定长参数
  • *args是元组类型的参数
  • **kwargs是字典类型的参数

 

4.4、关键字参数

def func(city="bj", age=6):

    // 代码块

  • 关键字参数是通过key=value形式,指定参数的键值对。

 

4.5、不同参数的混合使用

  • 不同的参数类型,位置参数、默认参数、*args,**kwargs可以混合使用,混合顺序:位置参数>默认参数>*args>**kwagrs,虽然可以混合使用,但是尽量不要使用操作2种类型的参数,否则会造成代码可读性差
  • 默认参数类型,需要注意:尽量使用不可变对象类型,否则会造成每次使用出现不同的结果。如下例子:
  1. def func_test(alist=[]):
  2. alist.append("END")
  3. return alist
  4. print(func_test())
  5. >> ["END"]
  6. print(func_test())
  7. >> ["END", "END"]

为什么会出现这种情况呢?

函数有__defaults__和__name__等内置变量,在定义一个函数后,内存中会分配函数代码,但并没有分配函数的数据,其中内存中分配了函数的__default__等变量;当调用一个函数,会为该调用的函数分配栈空间,函数执行完成,栈内存空间会被释放。

 对于__defaults__中存储的变量,函数调用会更改__defaults__的值,但并没有销毁这部分取值。

而默认变量就存储在__defaults__变量中。

 

4.6、 踩过的坑

4.6.1、默认参数,参数尽量为不可变对象,否则会造成每次使用,会得到不同的结果。

这里涉及到函数的内存分配。

python中内存分配交给python解释器,函数定义中,会在内存空间中分配空间,包括函数的定义,以及函数的__name__、__defaults__等变量,都在内存中,这里只分配了函数的代码,并没有为函数的数据分配内存空间;当函数被调用,函数的引用会分配新的栈空间,函数执行完后,函数的栈销毁,但此时函数内存中的函数定义,以及函数的__name__、__defaults__变量一直都在,如果函数的调用修改了__name__或__defaults__,会影响函数每次的返回结果。

而默认参数,就是分配在__defaults__所指的内存空间中,是在函数定义中就已被分配,如果是可变对象,会每次都更改__defaults__的取值,可能会影响函数每次执行的结果。

 

4.6.2、参数传递是引用传递

在调用函数的时候,会从实参传递给形参,参数传递只有引用传递。

如果参数是不可变对象,比如数、字符串、元组,参数传递后,并没有分配新的对象,只是新增了一个对象的引用;函数执行完后,函数销毁,形参的引用也减少;

如果参数是可变对象,比如列表、字典、集合,参数传递后,也没有分配新的对象,新增对象的引用;函数执行完后,形参的引用也减少,但需要注意的是,因为并没有分配新的内存空间,经过函数的处理后,会改变原来实参所指向的对象的取值

 

5、匿名函数lambda

# 匿名函数格式

变量 = lamdba 参数1, 参数2, 参数3: 表达式

lamdba是函数的匿名表示方法,在表示一些较简短的函数时,可以使用。

lamdba函数有其自身的函数作用域。

 

6、函数的作用域

6.1、函数作用域的分类

函数的作用域,也叫名称空间。主要有3种类型:

  • 内置作用域:是内置变量、内置函数的作用域,是python解释器加载到内存中的。内置作用域可以在整个python代码中随时使用。
  • 全局作用域:在函数外的变量。全局变量可以在一个py模块中,也可以在一个类中但是不在函数中的变量,也即类变量,全局变量可以被整个模块或整个类来使用。
  • 局部作用域:在函数内

需要注意的是,可以引入新的作用域的只有:模块、类、函数(包括嵌套的函数)。如果是其他代码块,比如try...except...或其他for/while等语句,并不会引入新的作用域。

 

6.2、作用范围、搜索顺序

作用范围:内置作用域built-in > 全局作用域global > 闭包中父类的作用域enclosing > 局部作用域local

搜索顺序:局部作用域local > 闭包中父类的作用域enclosing > 全局作用域global > 内置作用域built-in,可以理解为,一个变量,首先会在局部搜索查找该变量,如果没有在到全局变量中查看,如果在没有继续扩大范围到内置变量中查找。这一点可以说明,局部变量,是可以访问全局变量的,而反之,全局变量是不能访问局部变量。

 

6.3、修改全局变量,关键字global、nonlocal

局部变量可访问全局变量,前提是:如果局部变量中搜索不到变量,会继续扩大范围搜索全局变量。但是局部变量并没有修改全局变量,如果想修改全局变量,需要使用关键字global。

6.3.1、global

  1. name = "Test"
  2. def var_domain():
  3. global name
  4. name = "local name"
  5. print(name)
  6. var_domain()
  7. print(name)

>> 全局变量name已修改成local name

6.3.2、nonlocal

 nonlocal关键字,主要用以局部变量来修改局部变量的父类变量时使用,即在闭包的函数中,子类变量修改父类变量。

  1. name = "Test"
  2. def var_domain():
  3. name = "local name"
  4. def inner_func():
  5. nonlocal name
  6. name = "inner name"
  7. print(name)
  8. inner_func()
  9. print(name)
  10. var_domain()
  11. print(name)

>> 闭包的局部变量local name,已经被修改为inner name

6.3.3、并没有local关键字,只有locals()方法

 

6.4、方法globals()、locals()

globals():返回字典,获取全局作用域内的变量-值的字典,包括全局变量以及内置全局变量。

locals():返回字典,可获取所在位置的局部作用域内的变量-值的字典

 

7、函数的高级用法

函数的高级函数有一个共同的特定:传入的参数都为函数,对一个序列进行处理。

7.1、map

用法:map(__func, __iter1, __iter2, ……),第2个参数,第3个,第n个参数的个数,与func的参数个数有关

返回:python3.7返回的是迭代器

  1. def square(x):
  2. return 9*x
  3. res = list(map(square, [1, 2, 3, 4]))

>> [9, 18, 27, 36]

  1. def square(x, y):
  2. return x+y
  3. res = list(map(square, [1, 2, 3, 4], [9, 10, 11, 19]))

>> [10, 12, 14, 23]

 

7.2、reduce

注意:在python3.7后,reduce已经不是内置函数,如果需要使用reduce,需引入包from functools import reduce

  1. from functools import reduce
  2. def add(x, y):
  3. return x + y
  4. res = reduce(add, [1, 9, 10, 11])

>> 31

 

7.3、filter

用法:filter(__function, __iterable),对序列iterable中的每个元素作为参数,传递给function进行判断,如果为True,则放入列表中,如果为False,则过滤掉

返回:python3.7返回的是迭代器,如果想得到列表,可直接用内置list方法

  1. def is_odd(n):
  2. return n%2 == 1
  3. res = list(filter(is_odd, [1, 2, 3, 4, 5, 6, 7, 8, 9]))

>> [1, 3, 5, 7, 9]

 

8、偏函数

偏函数可以理解为,是对原有一个函数的改动,在原有函数上新增默认参数。

经常使用的偏函数模块是:from functools import partial

  1. from functools import partial
  2. def part_test(x, age=10):
  3. return "x is at age {}".format(age)
  4. part_test_20 = partial(part_test, age=20) # 创建一个偏函数part_test_20,注意这里的part_test_20和part_test均是函数的对象
  5. print(part_test_20("hello"))

>> hello is at age 20

先创建一个偏函数,给定一个默认参数的取值;在调用偏函数,相当于调用了一个有固定默认参数的函数。

 

9、递归函数

调用自身的函数,即递归函数。递归函数都可以用循环来实现,用递归简洁且逻辑清晰。

递归函数最大的问题是:会出现栈溢出。因为调用函数,会使用栈内存空间,函数调用,会非分配栈内存空间,函数执行完后,会释放栈内存空间,因为是递归调用,会有较多入栈操作,但是栈空间并不是无限的,可能导致栈内存溢出的问题

 

 

 

 

 

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/知新_RL/article/detail/544275
推荐阅读
相关标签
  

闽ICP备14008679号