1、给函数参数增加元信息

写好一个函数,然后想为这个函数的参数增加一些额外的信息,这样的话其他使用者就能清楚的知道这个函数应该怎么使用。

使用函数参数注解是一个很好的办法,它能提示程序员应该怎样正确使用这个函数。 例如,下面有一个被注解了的函数:

def add(x:int, y:int) -> int:
return x + y

python解释器不会对这些注解添加任何的语义。它们不会被类型检查,运行时跟没有加注解之前的效果也没有任何差距。 然而,对于那些阅读源码的人来讲就很有帮助啦。第三方

工具和框架可能会对这些注解添加语义。同时它们也会出现在文档中。

help(add)

Help on function add in module __main__:

add(x: int, y: int) -> int

尽管可以使用任意类型的对象给函数添加注解(例如数字,字符串,对象实例等等),不过通常来讲使用类或者字符串会比较好点。

函数注解只存储在函数的 __annotations__ 属性中。例如:

add.__annotations__
{'x': int, 'y': int, 'return': int}

注解的使用方法可能有很多种,但是它们的主要用途还是文档。 因为python并没有类型声明,通常来讲仅仅通过阅读源码很难知道应该传递什么样的参数给这个函数。 这时候

使用注解就能给程序员更多的提示,从而可以正确的使用函数。

2、有默认参数的函数

定义一个有可选参数的函数是非常简单的,直接在函数定义中给参数指定一个默认值,并放到参数列表最后就行了。

如果默认参数是一个可修改的容器,比如一个列表、集合或者字典,可以使用None作为默认值,就像下面这样:

# 使用一个列表作为默认值
def spam(a, b=None):
if b is None:
b = []
...

如果并不想提供一个默认值,而是想仅仅测试下某个默认参数是不是有传递进来,可以像下面这样写:

_no_value = object()

def spam(a, b=_no_value):
if b is _no_value:
print('No b value supplied')
...

我们测试下这个函数:

spam(1)   >> No b value supplied
spam(1,2) >> a=1,b=2
spam(1,None) >> a=1, b=None # 可以发现到传递一个None值和不传值两种情况是有差别的

注意:默认参数的值仅仅在函数创建的时候赋值一次。

x = 20
def spam(a,b=x):
print(a,b) spam(1) >> 1,20 x = 30
spam(1) >> 1,20

当我们改变x的值的时候对默认参数值并没有影响,这是因为在函数定义的时候就已经确定了它的默认值了。

其次,默认参数的值应该是不可变的对象,比如None、True、False、数字或字符串。 特别的,千万不要像下面这样写代码:

def spam(a,[]):
pass

如果这么做了,当默认值在其他地方被修改后将会遇到各种麻烦。这些修改会影响到下次调用这个函数时的默认值。比如:

def spam(a,b=[])
print(b)
return b x = spam(1) >> [] x.append(10) x = spam(1) >>[10]

这种结果不是我们想要的,为了避免这种结果的发生,尽量将默认值设为None,然后在函数里检查它,前面的例子就是这么做的,在测试None值时,is操作符很重要,也是这种操作的关键点,我们时常会写成下面的形式:

def spam(a, b=None):
if not b:
b=[]

这么写的问题在于尽管None值确实是被当成False, 但是还有其他的对象(比如长度为0的字符串、列表、元组、字典等)都会被当做False。 因此,上面的代码会误将一些其他输入也当成是没有输入。比如:

spam(1) # OK
x = []
spam(1, x) # 和上面一样,参数可以随意改变
spam(1, 0) # 数字,不可变类型
spam(1, '') # 字符串,不可变类型

3、匿名函数捕获变量值

先看下面代码效果:

x = 10
a = lambda y : x+y x = 20 b = lambda y : x+y a(10) >> 30
b(20) >> 30

lambda表达式中的x是一个自由变量, 在运行时绑定值,而不是定义时就绑定,这跟函数的默认值参数定义是不同的。 因此,在调用这个lambda表达式的时候,x的值是执行时的值。例如:

x = 10
a(10) >> 20 x = 20 b(10) >> 30

如果想让某个匿名函数在定义时就捕获到值,可以将那个参数值定义成默认参数即可,就像下面这样:

x = 10
a = lambda y, x=x: x+y
x = 20
b = lambda y,x=x: x+y a(10) >> 20
b(10) >> 30

通过在一个循环或列表推导中创建一个lambda表达式列表,并期望函数能在定义时就记住每次的迭代值。

# 错误写法

a = [lambda x:x+n for n in range(4)]

for f in a:
f(0)
print(f(0))

上面的写法输出全是4,跟刚开始的例子一样,函数运行时,n的值是迭代的最后一个值。

可以通过使用函数默认值参数形式,lambda函数在定义时就能绑定到值。

a = [lambda x,n=n:x+n for n in range(4)]

for f in a:
f(0)
print(f(0))

4、函数返回多个值

希望构造一个返回多个值得函数,函数直接return一个元祖就行。

def func():
return 1,2,3

a,b,c = func()

a >> 1
b >> 2
c >> 3

尽管func()看上去返回了多个值,实际上是先创建了一个元祖然后返回的,这个语法看上去比较奇怪,实际上我们使用的是用逗号生成的一个元祖,而不是用括号。比如下面:

a = (1,2)
a >> (1,2) a = 1,2
a >> (1,2)

当我们调用返回一个元祖的函数时候,通常我们会把结果赋值给多个变量,其实这就是元祖解包,返回结果也可以赋值给单个变量,这时候这个变量就是函数返回的那个元祖本身了。

Python基础—初识函数(二)的更多相关文章

  1. python基础 (初识函数&函数进阶)

    函数基础部分 .什么是函数? 函数是组织好的,可重复使用的,用来实现单一,或相关联功能的代码段. 函数能提高应用的模块性,和代码的重复利用率. 2.定义函数 定义:def 关键词开头,空格之后接函数名 ...

  2. Python基础之函数二

    函数的嵌套 通过名字就能理解,函数里是还可以套着函数用的.这么牛,下面就来看看几段代码,看看是怎么回事.注意:函数一定是先定义后使用. x=1234 def f1(): #定义一个主函数 x = 1 ...

  3. python基础(12):函数(二)

    1. 函数参数 之前我们说过了传参,如果我们需要给⼀个函数传参,⽽参数⼜是不确定的,或者我给⼀个函数传很多参数,我的形参就要写很多,很⿇烦,怎么办呢,我们可以考虑使⽤动态参数. 形参的第三种: 动态参 ...

  4. Python 基础 面向对象之二 三大特性

    Python 基础 面向对象之二 三大特性 上一篇主要介绍了Python中,面向对象的类和对象的定义及实例的简单应用,本篇继续接着上篇来谈,在这一篇中我们重点要谈及的内容有:Python 类的成员.成 ...

  5. python基础篇(二)

    PYTHON基础篇(二) if:else,缩进 A:if的基础格式和缩进 B:循环判断 C:range()函数和len()函数 D:break,contiue和pass语句 for,while循环 函 ...

  6. Python基础初识

    一.安装 暂时没空写,预留 二.python基础初识 2.1 注释 当行注释:# 被注释内容 多行注释:'''被注释内容''',或者"""被注释内容"" ...

  7. python基础之函数详解

    Python基础之函数详解 目录 Python基础之函数详解 一.函数的定义 二.函数的调用 三.函数返回值 四.函数的参数 4.1 位置参数 4.2 关键字参数 实参:位置实参和关键字参数的混合使用 ...

  8. python基础——匿名函数

    python基础——匿名函数 当我们在传入函数时,有些时候,不需要显式地定义函数,直接传入匿名函数更方便.  在Python中,对匿名函数提供了有限支持.还是以map()函数为例,计算f(x)=x2时 ...

  9. python基础——返回函数

    python基础——返回函数 函数作为返回值 高阶函数除了可以接受函数作为参数外,还可以把函数作为结果值返回.  我们来实现一个可变参数的求和.通常情况下,求和的函数是这样定义的: def calc_ ...

  10. python基础——sorted()函数

    python基础——sorted()函数 排序算法 排序也是在程序中经常用到的算法.无论使用冒泡排序还是快速排序,排序的核心是比较两个元素的大小.如果是数字,我们可以直接比较,但如果是字符串或者两个d ...

随机推荐

  1. 安装卸载GNOME

    只需要三步:sudo yum -y groups install "GNOME Desktop"sudo systemctl set-default graphical.targe ...

  2. CSAPP学习笔记——chapter4 处理器体系结构

    CSAPP学习笔记--chapter4 处理器体系结构 这一章相对于其它的章节,是相对来说比较困难的一章:其它章节的一些内容都在计组,计网,操作系统等课程里面已经接触过一些概念,但是有关处理器,我才发 ...

  3. Lua程序设计笔记

    未学:第10章URL编码及以后的示例 13章位和字节 Lua语言基础 一组命令或表达式组成的序列叫chunk程序段,因为Lua语言可以被用作数据定义语言,chunk的大小没有限制,几MB的程序段也很常 ...

  4. 【C语言】转义字符及其对应英文

    对于很多人来说,用转义字符都是熟能生巧,而不清楚为什么是那样的转义字符,所以我在这列了一个表,翻译了其对应的英文. 转义字符分为一般转义字符.八进制转义字符.十六进制转义字符. 一般转义字符:\0. ...

  5. 【Ubuntu】安装OpenSSH启用远程连接

    [Ubuntu]安装OpenSSH启用远程连接 零.安装软件 使用如下代码安装OpenSSH服务端: sudo apt install openssh-server 壹.启动服务 使用如下代码启动Op ...

  6. Docker 初始镜像 scratch

    初识 scratch 有那么一天,我们在这里邂逅了镜像scratch... 先来搜索下这个镜像 docker search scratch NAME DESCRIPTION STARS OFFICIA ...

  7. study Rust-4【所有权】这个太重要了!

    由于Rust内存垃圾自动回收,那就得搞清楚这个所有权玩意.这个太重要了.因为关系到贯穿于你以后的程序编写. 几个概念: 一.移动 1.咱们一般语言,自己申请内存,自己管理和释放.就是new和free. ...

  8. # 🤖 **DeepSeek 深度解析 PasteForm:一个让管理端开发爽到飞起的全栈解决方案**

    DeepSeek 深度解析 PasteForm:一个让管理端开发爽到飞起的全栈解决方案 各位开发者注意啦!今天我要带大家全方位解剖 PasteForm 这个神奇框架--不仅介绍核心思想,更要重点展示它 ...

  9. 洛谷P4198 楼房重建 题解

    Part1.自己一开始是怎么想的 我一开始的想法是先考虑什么情况下是看不见的. 如果是 \(i < j\) 的话可以直接看 \(j\) 的斜率和 \(i\) 的斜率就是比较 \(\frac{h_ ...

  10. hashtable底层

    一.单线程环境下 底层:hash表结构 (数组 + 链表) 使用无参构造创建对象时 会默认长度11的数组 加载因子0.75 Hashtable<Object, Object> hashta ...