Python函数

三元运算

# 书写格式
result = 值1 if 条件 else 值2
# 如果条件成立,那么将 “值1” 赋值给result变量,否则,将“值2”赋值给result变量

比如:

result = "good" if 1 > 0 else "well"
print(result)

输出结果:

good

深浅拷贝

对于 数字 和 字符串 而言,赋值、浅拷贝和深拷贝无意义,因为其永远指向同一个内存地址。对于字典、元祖、列表 而言,进行赋值、浅拷贝和深拷贝时,其内存地址的变化是不同的。

浅拷贝,在内存中只额外创建第一层数据。
深拷贝,在内存中将所有的数据重新创建一份(排除最后一层,即:python内部对字符串和数字的优化)

函数

  1. 定义函数

    def 函数名(形参,形参,形参):
    函数体
    return ‘###’

  2. 执行函数

    //这种方式传的实参默认一一对应形参

    函数名(实参)

    //这种方式传参,可以不用管形参的顺序,因为传的参数已经指定了形参

    函数名(形参=实参,形参=实参,形参=实参)

注意:方法默认有返回值,默认返回None,可以用return 返回任何想要返回的值,可以没有return,最后一行执行的结果作为返回值

函数的参数

位置参数(普通参数)

def power(x):
    return x**2

上述函数,参数x就是一个位置参数,如果有位置参数,必传。

默认参数

def power(x, n=3):
    return x ** n

上述函数,参数n就是一个默认参数,我们可以只传一个参数,也可以传两个,如果只传一个,n参数默认为3。

注意:必选参数在前,默认参数在后,否则Python的解释器会报错。当函数有多个参数时,把变化大的参数放前面,变化小的参数放后面。变化小的参数就可以作为默认参数。默认参数必须指向不变对象。

可变参数

参数前面加了一个号就表示接收一个可变参数,实际上会把传入的参数变成一个tuple,Python允许你在list或tuple前面加一个号,把list或tuple的元素变成可变参数传进去。

def calc(*arg):
    for item in arg:
        print(item)

data = [5, 6, 7]
//如果直接这样传,列表会变成tuple的一个元素([5, 6, 7],)
calc(data)
calc(*data)

关键字参数

可变参数允许你传入0个或任意个参数,这些可变参数在函数调用时自动组装为一个tuple。而关键字参数允许你传入0个或任意个含参数名的参数,这些关键字参数在函数内部自动组装为一个dict 。

def calc(**arg):
    for item in arg:
        print(item)


extra = {'city': 'Beijing', 'job': 'Engineer'}
// Python允许你在dict前面加两个*号,把dict的元素变成可变参数传进去
calc(**extra)

命名关键字参数

如果要限制关键字参数的名字,就可以用命名关键字参数,例如,只接收city和job作为关键字参数。这种方式定义的函数如下:

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

person('Jack', 24, city='Beijing', job='Engineer')

和关键字参数*kw不同,命名关键字参数需要一个特殊分隔符,*后面的参数被视为命名关键字参数。

如果函数定义中已经有了一个可变参数,后面跟着的命名关键字参数就不再需要一个特殊分隔符*了:

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

命名关键字参数必须传入参数名,这和位置参数不同。如果没有传入参数名,调用将报错,因为系统会错认为是位置参数,但是实际上位置参数只有两个。命名关键字参数可以有缺省值,从而简化调用:

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

由于命名关键字参数city具有默认值,调用时,可不传入city参数:

person('Jack', 24, job='Engineer')

使用命名关键字参数时,要特别注意,如果没有可变参数,就必须加一个作为特殊分隔符。如果缺少,Python解释器将无法识别位置参数和命名关键字参数:

def person(name, age, city, job):
    //缺少 *,city和job被视为位置参数
    pass

全局和局部变量

全局变量,在任何位置都可以使用(约定,全局变量名字全部大写)
局部变量,在局部内使用,局部外使用不了。

PERSON = "good"

def fun1():
    # 局部变量,只能内部使用
    a = 123
    # 局部变量和全局变量一样,不影响全局变量,只是局部内部有一个名字和全局一样的变量而已
    # 如果这样声明,则修改的是全局的变量
    # global PERSON
    PERSON = "fanda"
    print(a)
    print(PERSON)

fun1()
print(PERSON)

lambda表达式

def test(args1, args2):
    print(args1 + args2)

test2 = lambda args1, args2: print(args1 + args2)

上述两个方法的的实现是等价的。

内置函数

abs 取绝对值
print(abs(-123))

all 传入可迭代对象,循环参数,如果每个元素都为真,返回True,否则,返回False
假的数据,0,None,"",(),{},即0,None,空值,返回False
print(bool(0))
print(bool(""))
print(bool(()))
print(bool({}))
返回False
print(all([11, "34", ""]))

any 传入可迭代对象,循环参数,如果有一个元素为真,返回True
返回True
print(any([11, "34", ""]))

ascii,传入对象,找到对象中的__repr__方法,返回其执行结果
print(ascii("fsfsdfsf"))

a = bin(11)  # 二进制
b = oct(11)  # 八进制
c = int(11)  # 十进制
d = hex(11)  # 十六进制

print(a, b, c, d)
结果 ,0b1011 0o13 11 0xb

结果是一个元祖,元素分别是商和余数
result = divmod(10,3)
print(result)

把字符串变成表达式,只处理简单的表达式,有返回值
print(eval("1+3"))
print(eval("a+100+b", {"a": 100, "b": 50}))

这个方法才可执行复杂的表达式,没返回值,相当于直接执行python代码
exec("for i in range(10) : print(i)")

li = [22, 3, 53]
判断某个对象是否是某个类型的
print(isinstance(li, list))

map() 方法,map()函数接收两个参数,一个是函数,一个是Iterable,map将传入的函数依次作用到序列的每个元素,并把结果作为新的Iterator返回。

result = filter(lambda x: x > 10, [1, 4, 9, 11, 34, 43, 64])
print(type(result))
print(list(result))
for item in result:
    print(item)

由于结果result是一个Iterator,Iterator是惰性序列,因此通过list()函数让它把整个序列都计算出来并返回一个list。

再看reduce的用法。reduce把一个函数作用在一个序列[x1, x2, x3, …]上,这个函数必须接收两个参数,reduce把结果继续和序列的下一个元素做累积计算,其效果就是:

reduce(f, [x1, x2, x3, x4]) = f(f(f(x1, x2), x3), x4)

如果要把序列[1, 3, 5, 7, 9]变换成整数13579,用reduce实现如下:

print(reduce(lambda x, y: x * 10 + y, [1, 3, 5, 7, 9]))

和map()类似,filter()也接收一个函数和一个序列。和map()不同的是,filter()把传入的函数依次作用于每个元素,然后根据返回值是True还是False决定保留还是丢弃该元素。

result = filter(lambda x: x > 10, [1, 4, 9, 11, 34, 43, 64])
print(type(result))
print(list(result))
for item in result:
    print(item)

可见用filter()这个高阶函数,关键在于正确实现一个“筛选”函数。

注意到filter()函数返回的是一个Iterator,也就是一个惰性序列,所以要强迫filter()完成计算结果,需要用list()函数获得所有结果并返回list。

sorted()函数可以对list进行排序:

li = [1, 4, 5, -3, -64, 0, -43]
这个方法返回一个新的值 ,之前的值不变
print(sorted(li))

//结果[-64, -43, -3, 0, 1, 4, 5]

此外,sorted()函数也是一个高阶函数,它还可以接收一个key函数来实现自定义的排序,例如按绝对值大小排序:

print(sorted(li, key=abs))

//结果[0, 1, -3, 4, 5, -43, -64]

key指定的函数将作用于list的每一个元素上,并根据key函数返回的结果进行排序,然后按照对应关系返回list相应的元素。默认是从小到大排序的,如果想要从大到小排序,可以再增加一个参数,如下:

print(sorted(li, key=abs, reverse=True))

//结果[-64, -43, 5, 4, -3, 1, 0]

sorted()是一个高阶函数。用sorted()排序的关键在于实现一个映射函数。

递归函数

在函数内部,可以调用其他函数。如果一个函数在内部调用自身本身,这个函数就是递归函数。

举个例子,我们来计算阶乘n! = 1 x 2 x 3 x … x n,用函数fact(n)表示,可以看出:

fact(n) = n! = 1 x 2 x 3 x … x (n-1) x n = (n-1)! x n = fact(n-1) x n

所以,fact(n)可以表示为n x fact(n-1),只有n=1时需要特殊处理。

于是,fact(n)用递归的方式写出来就是:

def fact(n):
    if n==1:
        return 1
    return n * fact(n - 1)

如果我们计算fact(5),可以根据函数定义看到计算过程如下:

===> fact(5)
===> 5 * fact(4)
===> 5 * (4 * fact(3))
===> 5 * (4 * (3 * fact(2)))
===> 5 * (4 * (3 * (2 * fact(1))))
===> 5 * (4 * (3 * (2 * 1)))
===> 5 * (4 * (3 * 2))
===> 5 * (4 * 6)
===> 5 * 24
===> 120

利用函数编写如下数列:

斐波那契数列指的是这样一个数列 0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, 144, 233,377,610,987,1597,2584,4181,6765,10946,17711,28657,46368…

def func(arg1, arg2):
    if arg1 == 0:
        print(arg1, arg2)
    arg3 = arg1 + arg2
    print(arg3)
    if arg3 > 10000:
        return
    func(arg2, arg3)

使用递归函数需要注意防止栈溢出。在计算机中,函数调用是通过栈(stack)这种数据结构实现的,每当进入一个函数调用,栈就会加一层栈帧,每当函数返回,栈就会减一层栈帧。由于栈的大小不是无限的,所以,递归调用的次数过多,会导致栈溢出。上述代码如果不加判断来终结函数,将会报错