(Python3)- 函数&高级特性

1.函数的调用:

  • 参数检查:isinstance(x, (int, float))可以检查参数x是否是元组tuple中指定的类型。如果不是其中的一种,那么可以用raise TypeError(str)抛出一个类型错误(TypeError),消息为str。
  • 返回多个值:在函数体内return x1,x2,...,xn。就可以返回一个元组tuple。

#coding=utf-8
#这个是函数的使用的PY示例
#检查函数的数据类型
def my_abs(x):
    if not isinstance(x,(int,float)):
        raise TypeError('错误的数据类型'+str(type(x)))
    else:
        if x>=0:
            return x
        else:
            return -x
i=235
str1='ggrgrg'
f=0.963
tuple1=('j','hjh')
dic={'lk':'fgf','gr':'grrefe'}
lis=['fdfe','grgrgr']
print(my_abs(i))
print(my_abs(str1))
print(my_abs(f))
print(my_abs(tuple1))
print(my_abs(dic))
print(my_abs(lis))

2.位置参数:制定了顺序,PY也支持默认参数

def power(x, n):
    s = 1
    while n > 0:
        n = n - 1
        s = s * x
    return s

但是默认参数需要注意:默认的值一般为一个常量,如果制定的是一个变量的话,那么就有可能会发生默认参数被修改的问题。

2.可变参数:需要的参数后面加上 * 。可以接受多个参数,组成一个临时tuple。如果需要传递一个tuple给可变参数,需要将tuple进行处理。

def calc(*numbers):
    sum = 0
    for n in numbers:
        sum = sum + n * n
    return sum
>>> nums = [1, 2, 3]
>>> calc(nums[0], nums[1], nums[2])
14
#coding=utf-8
#返回多个值示例:
import math
def solve_formula(a,b,c):
    delta=math.sqrt(b*b-4*a*c)
    return float((-b+delta)/(2*a)),float((-b-delta)/(2*a))
a=int(input())
b=int(input())
c=int(input())
r1,r2=solve_formula(a,b,c)
print('root1 is %f,root 2 is %.2f' %(r1,r2))

3.关键字参数:需要在变量名前加 **。和可变参数类似,只不过是变为临时的dict类型。传值有些区别:

def person(name, age, **kw):
    print('name:', name, 'age:', age, 'other:', kw)

>>> person('Bob', 35, city='Beijing')
name: Bob age: 35 other: {'city': 'Beijing'}
>>> person('Adam', 45, gender='M', job='Engineer')
name: Adam age: 45 other: {'gender': 'M', 'job': 'Engineer'}

4.命名关键字参数:可以限制关键字参数的关键字名称。这样只接受city和job的关键字参数,而且都不能缺少

def person(name, age, *, city, job):
    print(name, age, city, job)

命名关键字参数之后可以有可变参数,可以这样申明:args是可变参数,city,job是命名关键字参数。

def person(name, age, *args, city, job):
    print(name, age, args, city, job)

命名关键字参数以可以有默认的参数,注意一定要用关键字指定值。

5.参数组合:

在Python中定义函数,可以用必选参数、默认参数、可变参数、关键字参数和命名关键字参数,这5种参数都可以组合使用。但是请注意,参数定义的顺序必须是:必选参数、默认参数、可变参数、命名关键字参数和关键字参数。
比如定义一个函数,包含上述若干种参数:

def f1(a, b, c=0, *args, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'args =', args, 'kw =', kw)

def f2(a, b, c=0, *, d, **kw):
    print('a =', a, 'b =', b, 'c =', c, 'd =', d, 'kw =', kw)

在函数调用的时候,Python解释器自动按照参数位置和参数名把对应的参数传进去。

>>> f1(1, 2)
a = 1 b = 2 c = 0 args = () kw = {}
>>> f1(1, 2, c=3)
a = 1 b = 2 c = 3 args = () kw = {}
>>> f1(1, 2, 3, 'a', 'b')
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {}
>>> f1(1, 2, 3, 'a', 'b', x=99)
a = 1 b = 2 c = 3 args = ('a', 'b') kw = {'x': 99}
>>> f2(1, 2, d=99, ext=None)
a = 1 b = 2 c = 0 d = 99 kw = {'ext': None}

6.总结:

*args是可变参数,args接收的是一个tuple;
**kw是关键字参数,kw接收的是一个dict。

可变参数既可以直接传入:func(1, 2, 3),又可以先组装list或tuple,再通过*args传入:func(*(1, 2, 3));
关键字参数既可以直接传入:func(a=1, b=2),又可以先组装dict,再通过**kw传入:func(**{'a': 1, 'b': 2})。

#coding=utf-8
#参数:
def def_para(l=[]):
    l.append('END')
    return l
#print(def_para())
#print(def_para())#默认参数必须是不变的对象
#可变的参数
"""
def accumulate(*nums):
    sum=0
    for n in nums:
        sum=sum+n
    return sum
print(int(accumulate(2,3,5,5)))
"""
"""
def person(name,age,**kw):
    print('name:',name,' age:',age,' other:',kw)
    print(str(type(kw)))
person('Shu',19,city='Xiangtan',sex='Male')
"""
#命名关键字参数 需要制定全部的关键字
def job(ty,*salary,days):
    print(ty,salary,days)
job('Cleaner')
job('Traveler',days=7)
def f1(x,y=56):
    print(x,y)
f1(5)
f1(6,7)
#Python对尾递归没有优化

尾递归优化(这个可能和后面的列表生成式有关):http://www.jianshu.com/p/d36746ad845d

使用尾递归的阶乘:

#coidng=utf-8
#递归
import types
def f(n,pre=1):
    if n==1 or n==0 :
        return 1*pre
    else:
        return f(n-1,n*pre)
def tramp(gen, *args, **kwargs):
    g = gen(*args, **kwargs)
    while isinstance(g, types.GeneratorType):
        g=g.next()
    return g
print(tramp(f, 996))

7.迭代:

  • 对于dict类型,可以使用方法values()、keys()、items()获取值、键、键值对(tuple)。判断是否是可以迭代的对象:可以用isinstance就可以判断是否式可迭代的类型Iterable
  • 可以将列表类型转换为以下表-值组成的dict:enumerate(list)

8.列表生成式List Comprehensions:

  • [exp]表示生成一个列表。可以看看例子:
#coding=utf-8
#列表生成式
lis=[x*x for x in range(1,11) if x % 2==0]
print(lis)
#全排列
lis2=[m+n for m in 'ABC' for n in 'XYZ']
print(lis2)
#全部转换为小写
lis3=[m.lower() for m in ['FGFG','HREHR','SHTTR']]
print(lis3)

9.生成器generator:

9.1 创建生成器函数:

  • 列表生成器会生成许多中间的内容,有时候我们只需要最后的结果不关心中间的变量。
  • 列表生成器:(EXP)
  • 使用next函数可以获取下一个值,如果已经是最后一个了,就会得到StopIteration的错误。可以利用PY : try和except语句捕获。比较安全的办法是利用生成器是可迭代对象,在for循环中使用。

9.2 使用生成器:

  • 需要将算法的输出语句改为yield

例如斐波那契数列:

def fib(max):
    n,a,b=0,0,1
    while n < max:
        yield b
        a,b=b,a+b
        n=n+1
    print('Done')
f=fib(8)
print(f) #f是一个生成器对象
for s in f:
    print(s)

使用了生成器的函数的执行流程不一样:函数是顺序执行,遇到return语句或者最后一行函数语句就返回。而变成生成器的函数,在每次调用next()的时候执行,遇到yield语句返回,再次执行时从上次返回的yield语句处继续执行。

例子:杨辉三角

#coding=utf-8
#杨辉三角
def triangles(n):
     L=[1]#定义一个list[1]
     while True:
        yield L#打印出该list
        L=[L[x]+L[x+1] for x in range(len(L)-1)]#计算下一行中间的值(除去两边的1)
        L.insert(0,1)#在开头插入1
        L.append(1)#在结尾添加1
        if len(L)>10:#仅输出10行
            break
a=triangles(10)
for i in a:
    print(i)
  • 列表生成式可以改为生成器,就需要用next()来获取下一个值。函数也可以,只需要将输出的语句改为yield,每次next()获取一次值就会继续执行yield之后的语句。

10.迭代器:凡是Iterable的类型都可以通过iter(obj)函数返回一个迭代的类型。需要使用库:collections 。导入:from collections import Iterator

  • Iterable类型:list、tuple、dict、set、str
  • Iterator类型:next()、iter()返回的对象、生成器。next函数就需要一个Iterator的对象。

因为Python的Iterator对象表示的是一个数据流,Iterator对象可以被next()函数调用并不断返回下一个数据,直到没有数据时抛出StopIteration错误。可以把这个数据流看做是一个有序序列,但我们却不能提前知道序列的长度,只能不断通过next()函数实现按需计算下一个数据,所以Iterator的计算是惰性的,只有在需要返回下一个数据时它才会计算。Iterator甚至可以表示一个无限大的数据流,例如全体自然数。而使用list是永远不可能存储全体自然数的。

for循环本质就是通过不断调用next()实现的,知道遇到StopIteration的错误。

11.切片:animal[m:n]:获取从m开始,以n为尾后的子表。如果m、n或m和n都为空,那么就是从[0,n)、[m,size)和[0,size)的子表。如果m、n没有写,就相当于复制一个列表。

[l:u::n]:在[l,u)的范围内,表示从左到右,每n个就截取一个。

list、tuple和str都可以使用切片。

 

整理&感谢: