抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

[toc]

python 函数(一)

函数

数学定义: y=f(x),y是x的函数,x是自变量

  • python 函数

由苦干语句组成的语句块、函数名称、参数列表构成,它是组织代码的最小单元格
完成一定的功能

  • 函数的作用

结构化编程对代码的最基本的封装,一般按照功能组织一段代码;
封装的目的为了复用,减少冗余代码
代码更加简洁美化、可读易懂

  • 函数的分类

内建的函数,如max()、reversed()等
库函数,如math.ceil()等

函数定义、调用

  • def语句定义函数
    def 函数名(参数列表):
    函数体(代码块)
    [return 返回值]

  • 函数名就是标识符,命名要求一样:

  • 语句块必须缩进,约定4个空格;
  • Python 的函数没有return语句,隐式返回 一个None值
  • 定义中的参数列表成为形式参数,只是一种符号表达,简称形参

  • 调用
    函数定义,只是声明了一个函数,它不会被执行,需要调用;
    调用的方式,就是函数名加上小括号,括号内写上参数;
    调用时写的参数是实际参数,是实实在在传的值,简称实参

  • 函数举例
In [3]: def add(x,y):
   ...:     result = x+y
   ...:     return result
   ...:
   ...:

In [4]: out = add(100,255)
In [5]: out
Out[5]: 355

上面只是一个函数的定义,有一个函数叫做add,接收2个参数
计算的结果,通过返回值返回
调用通过函数名add加2个参数,返回值可使用变量接收;
定义需要在调用前,也就是说调用时,已经被定义过了,否则抛NameError异常
函数是可调用的对象,callable()

函数参数

  • 参数调用时传入的参数要和定义的个数相匹配(可变参数例外)
  • 位置参数
    def f(x, y, z) 调用使用 f(1, 3, 5)
    按照参数定义顺序传入实参

关键字参数
def f(x, y ,z) 调用使用f(x=1,y=3,z=5)
使用形参的名字来出入实参的方式,如果使用了形象名字,那么传参顺序就可以定义顺序不同

  • 传参
f(z=None, y=10, x=[1])
f((1,), z=6, y=4.1)
f(y=5,z=6.2)
# 要求位置参数必须在关键字参数之前传入,位置参数是按位置对应的

函数参数默认值

  • 定义时,在形参后跟上一个值
In [6]: def add(x=1,y=5):
   ...:     return x+y
In [7]: callable(add)
Out[7]: True
In [8]: add()
Out[8]: 6
In [9]: add(6)
Out[9]: 11
In [10]: add(6,7)
Out[10]: 13
In [11]: add(y=8,x=1)
Out[11]: 9
  • 作用
    参数的默认值可以在未传入足够的实参的时候,对没有给定的参数赋值为默认值
    参数的非常多的时候,并不需要用户每次都输入所有的参数,简化函数调用
In [12]: def add(x,y=5):
    ...:     return x+y
In [13]: add(3)
Out[13]: 8
In [14]: add(3,6)
Out[14]: 9
  • 举例
    定义一个函数login,参数名称为host、port、username、passwd
def login(host='127.0.0.1',port='8080',username='rj',password='test'):
    print('{}:{}@{}/{}'.format(host,port,username,password))
login()
login('127.0.0.1',80,'tom','tom')
login('127.0.0.1',username='root')
login('localhost', port=80,password='com')
login(port=80,password='test',host='web')

可变参数

  • 问题
    有多个数,需要求累加求和
In [15]: def add(nums):
    ...:     sum = 0
    ...:     for x in nums:
    ...:         sum += x
    ...:     return sum
In [16]: add([1,2,5])
Out[16]: 8
In [17]: add((2,4,6))
Out[17]: 12
  • 可变参数

一个形参可以匹配任意个参数
有多个数,需要累加求和

In [22]: def add(*nums):
    ...:     sum = 0
    ...:     print(type(nums))
    ...:     for x in nums:
    ...:         sum += x
    ...:     print(sum)
    ...:
In [23]: add(3,6,9)
<class 'tuple'>
18

在形参前使用*表示该形参是可变参数,可以接收多个实参;
收集的实参名称和值组成一个tuple;

可变参数

关键字参数的可变参数

  • 配置信息打印
In [9]: def showconfig(**kwargs):
   ...:     for k,v in kwargs.items():
   ...:         print('{} = {}'.format(k,v))
   ...:

In [10]:

In [10]: showconfig(host='127.0.0.1',port='8080',username='asjin',password='ssjinyao')
host = 127.0.0.1
port = 8080
username = asjin
password = ssjinyao
  • 形参前使用**符号,表示可以接收多个关键字参数
  • 收集的实参名称和值组成一个字典

混着写,关键的默认定义好,其它的用kwargs

def showconfig(username, password, **kwargs)
def showconfig(username, *args, **kwargs)
def showconfig(username, password,**kwargs, *args)

总结

  • 有位置可变参数和关键字可变参数;
  • 位置可变参数在形参前使用一个星号*;
  • 关键字可变参在形参前使用两个星号**;
  • 位置可变参数和关键字可变参数都可以收集若干个实参,位置可变参数收集形成一个tuple,关键字可变参数收集形成一个dict;
  • 混合使用参数的时候,可变参数要放到参数列表的最后,普通参数要放到参数列表前面,位置可变参数需要在关键字可变参数之前。

举例

def fn(x, y, *args, **kwargs):
    print(x)
    print(y)
    print(args)
    print(kwargs)
fn(3, 5, 7, 9, 10,a=1,b='python')
fn(3, 5)
fn(3, 5, 7)
fn(3, 5, a=1,b='python')

举例

def fn(*args, x, y, **kwargs)
    print(x)
    print(y)
    print(args)
    print(kwargs)
fn(7, 9, y=5, x=3, a=1, b='python' )

keyword-only 参数

  • keyword-only 参数(Python3 加入)

如果在一个星号参数后,或者一个位置可变参数后,出现的普通参数,实际上已经不是普通的参数了,而是keyword-only参数

def fn(*args, x):
    print(x)
    print(args)
fn(3, 5, x=7)

args 可以看做已经截获了所有的位置参数,x不使用关键字参数就可能拿到实参

  • keyword-only 参数另一种形式
def fn(*, x, y):
    print(x,y)

fn(x=5, y=6)

*号之后,普通形参都变成了必须给出的keyword-only参数

  • 可变参数和参数默认值

举例

def fn(*args, x=5):
    print(x)
    print(args)

fn() # 等价于fn(x=5)
fn(5)
fn(x=6)
fn(1 , 2, 3, x=10)

x 是keyword-only 参数

举例

def fn(x=5, **kwargs):
    print('x={}'.format(x))
    print(kwargs)
fn()
fn(5)
fn(x=6)
fn(y=3,x=10)
fn(3,y=10)

函数参数

  • 参数规则
    参数列表参数一般顺序是,普通参数、缺省参数、可变位置参数、keyword-only参数(可带缺省值)、可变关键字参数
def fn(x, y, z=3, *arg, m=4, n, **kwargs):
    print(x, y, z, m,n)
    print(args)
    print(kwargs)
def connect(host='localhost',port='3306',user='admin',password='admin',**kwargs):
    print(host,port)
    print(user,passowrd)
    print(kwargs)
connect(db='cmdb')
connect(host='172.16.0.8',db='cmdb')
connect(host='172.160.0.9',db='cmdb',password='mysql')

参数解构

举例: 加法函数

def add(x, y):
    return x+y

add(4, 5)
add((4, 5))
t = (4, 5)
add(t[0], t[1])
# add(*t) 或 add(*(4, 5)) add(*[4,5]) add(*{4,6})
# add(*range(1,3))
In [1]: def add(x, y):
   ...:     return x+y
In [3]: add((1,2)[0],[3][0])
Out[3]: 4
In [8]: def add(x, y):
   ...:     return x+y
In [9]: lst = [1,3]
In [10]: add(*lst)
Out[10]: 4
# 当给定一个集合
In [12]: add(*{5,6})
Out[12]: 11

例:

In [28]: def add(*keys):
    ...:     sumnum = 0
    ...:     for i in keys:
    ...:         sumnum = sumnum + i
    ...:     return sumnum
In [29]: add(*range(1,101))
Out[29]: 5050
  • 参数解构

给函数提供实参的时候,可以在集合类型前使用* 或者** , 把集合类型的结构解开,提取所有元素做为函数的实参
非字典类型使用*解构成位置参数
字典类型使用*解构成位置参数
提取出来的元素数王要和参数的要求匹配,也要和参数的类型匹配

def add(x, y):
    return x+y
add(*(4 ,5)) 
add(*[4,5])
add(*{4,6})
d = {'x': 5, 'y':6}
add(**d)
add(**{'a':5, 'b':6})
In [39]: def add(x, y):
    ...:     return x+y
In [40]: dct = {'x': 1, 'y':6}
In [41]: add(*dct.values())
Out[41]: 7

练习

编写一个函数,能够接受至少2个参数,返回最小值和最大值。

In [49]: def mums(x, y, *args):
    ...:     print(min(x,y,*args))
    ...:     print(max(x,y,*args))
In [50]: mums(*range(1,100001))
1
100000

方法二

In [51]: import random

In [52]: def double_values(*nums):
    ...:     print(nums)
    ...:     return max(nums), min(nums)
In [53]: print(*double_values(*[random.randint(10,20) for _ in range(10)]))
(14, 11, 19, 11, 17, 12, 13, 18, 12, 13)
19 11

编写 一个函数,接受一个参数n, n为正整数, 左右两种打印方式, 要求数字必须对齐


In [59]: def trangle_print(n):
    ...:     for i in range(1, n+1):
    ...:         for j in range(n, 0, -1):
    ...:             if i < j:
    ...:                 print(' '*len(str(j)), end=' ')
    ...:             else:
    ...:                 print(j, end=' ')
    ...:         print()
    ...:

In [60]: trangle_print(16)
                                     1
                                   2 1
                                 3 2 1
                               4 3 2 1
                             5 4 3 2 1
                           6 5 4 3 2 1
                         7 6 5 4 3 2 1
                       8 7 6 5 4 3 2 1
                     9 8 7 6 5 4 3 2 1
                  10 9 8 7 6 5 4 3 2 1
               11 10 9 8 7 6 5 4 3 2 1
            12 11 10 9 8 7 6 5 4 3 2 1
         13 12 11 10 9 8 7 6 5 4 3 2 1
      14 13 12 11 10 9 8 7 6 5 4 3 2 1
   15 14 13 12 11 10 9 8 7 6 5 4 3 2 1
16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1

方法二

In [61]: def show(n):
    ...:     tail = " ".join([str(i) for i in range(n,0,-1)])
    ...:     print(tail)
    ...:     width = len(tail)
    ...:     for i in range(1,n):
    ...:         print("{:>{}}".format(" ".join([str(j) for j in range(i,0,-1)]), width))
    ...:     print(tail)
    ...:

In [62]: show(12)
12 11 10 9 8 7 6 5 4 3 2 1
                         1
                       2 1
                     3 2 1
                   4 3 2 1
                 5 4 3 2 1
               6 5 4 3 2 1
             7 6 5 4 3 2 1
           8 7 6 5 4 3 2 1
         9 8 7 6 5 4 3 2 1
      10 9 8 7 6 5 4 3 2 1
   11 10 9 8 7 6 5 4 3 2 1
12 11 10 9 8 7 6 5 4 3 2 1
In [63]: def showtail(n):
    ...:     tail = " ".join([str(i) for i in range(n,0,-1)])
    ...:     print(tail)
    ...:     for i in range(len(tail)):
    ...:         if tail[i] == ' ':
    ...:             print(' '*i, tail[i+1:])
    ...:

In [64]: showtail(12)
12 11 10 9 8 7 6 5 4 3 2 1
   11 10 9 8 7 6 5 4 3 2 1
      10 9 8 7 6 5 4 3 2 1
         9 8 7 6 5 4 3 2 1
           8 7 6 5 4 3 2 1
             7 6 5 4 3 2 1
               6 5 4 3 2 1
                 5 4 3 2 1
                   4 3 2 1
                     3 2 1
                       2 1
                         1

直接插入排序

在未排序序列中,构建一个子排序序列,直至全部数据排序完成;
将待排序的数,插到已经排序的序列中合适的位置;
增加一个哨兵,放入待比较值,让它和后面已经排好的序列比较,找到适合的插入点;

  • 增加一个哨兵位,每轮比较将待比较数入;
  • 哨兵依次和待比较数的前一个数据比较,大数靠右移动,找到哨兵中值的插入位置;
  • 每一轮结束后,得到一个从开始到待比较数位置的一个有序序列

实例

m_list = [[1, 9, 8, 5, 6, 7, 4, 3, 2 ], [1, 2, 3, 4, 5, 6, 7, 8, 9],
          [9, 8, 7, 6, 5, 4, 3, 2, 1 ], [1, 1, 1, 1, 1, 1, 1, 1, 1],
          [1, 1, 1, 1, 1, 1, 1, 1, 2]]
nums = [0] + m_list[0]
sentinel, *origin = nums  #哨兵位,待比较数字
count_swap = 0
count_iter = 0
length = len(nums)
for i in range(2,length): #从2开始
    nums[0] = nums[i] # 放置哨兵
    j = i -1
    count_iter +=1
    if nums[j] > nums[0]: # 大数右移,找到插入位置
        while nums[j] > nums[0]:
            nums[j+1] = nums[j] # 依次右移
            j -= 1
            count_swap +=1
        nums[j+1] = nums[0] # 将哨兵插入,注意挺好入的右侧要+1
print(nums, count_swap, count_iter)

作用域

  • 一个标识符的可见范围,这就是标识符的作用域。一般常说的是变量的作用域
  • 举例,对比左右2个函数
In [4]: x = 50

In [5]: def show():
   ...:     print(x)
In [7]: show()
50

看似是可见的,但尝试以下语句

In [8]: x = 5
In [9]: def foo():
   ...:     x += 1
   ...:     print(x)
In [10]: foo()
---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
<ipython-input-10-c19b6d9633cf> in <module>()
----> 1 foo()
<ipython-input-9-69a7705e0064> in foo()
      1 def foo():
----> 2     x += 1
      3     print(x)
      4
UnboundLocalError: local variable 'x' referenced before assignment
In [11]:
  • 全局作用域
    在整个程序运行环境中都可见

  • 局部作用域
    在函数、类等内部可见
    局部变量使用范围不能超过期所有的局部作用域

def fn1():
    x = 1 # 局部作用域,在fn1内
def fn2():
    print(x) 
print(x)
In [16]: def outer():
    ...:     o = 65
    ...:     def inner():
    ...:         print("inner {}".format(o))
    ...:         print(chr(o))
    ...:     print("outer {}".format(o))
    ...:     inner()
In [18]: outer()
outer 65
inner 65
A

对比

In [19]: def outer2():
    ...:     o = 65
    ...:     def inner():
    ...:         o = 97
    ...:         print("inner {}".format(o))
    ...:         print(chr(o))
    ...:     print("outer {}".format(o))
    ...:     inner()
    ...:
In [20]: outer2()
outer 65
inner 97
a

全局变量global

x = 5
def foo():
    global x 
    x += 1
  • 使用global关键字的变量,将foo内的x声明为使用外部的全局作用域定义的x
  • 全局作用域中必须有x的定义
x = 100

def foo():
    global x
    x = 10
    x += 1
    print(x)
foo()
print(x)
11
11
  • 但是, x=10 赋值即定义, x 在内部作用域为一个外部作用域的变量赋值, 所以x+=1会报错。
  • 注意,这里的x的作用域还是全局的

global 总结

  • x+=1这种是特珠形式产生的错误的原因? 先引用后赋值,而python动态语言是赋值才算定义,才能被引用。解决办法,在这条语句前加x=0之类的赋值语句,或者使用global告诉内部作用域,去全局作用域查变量定义
  • 内部作用域作用x =5 之类的赋值语句会重新定义局部作用域使用的变量x,但是,一旦这个作用域中使用global声明x为全局的,那么x=5相当于在为全局作用域的变量x赋值
  • global的使用原则
  • 外部作用域亦是会内部作用域可见,但也不要在这个内部的局部作用域中直接使用,因为函数的目的就是为了封装,尽量与外界隔离
  • 如果函数需要使用外部全局变量,请使用函数的形参传参解决

闭包

  • 自由变量: 未在本地作用域中定义的变量。例如定义在内存函数外的外层函数作用域中的变量
  • 闭包: 就是一个概念,出现在嵌套函数中,指的内层函数引用到了外层函数的自由变量就形成了闭包。很多语言都有这个概念,最熟悉就是JavaScript
In [8]: def counter():
   ...:     c = [0]
   ...:     def inner():
   ...:         c[0] +=1
   ...:         return c[0]
   ...:     return inner
In [9]: foo = counter()
In [11]: print(type(foo))
<class 'function'>
In [12]: print(type(foo()))
<class 'int'>
In [13]: print(foo(),foo())
2 3
In [15]: print(foo())
4

评论