Python 函数

我的理解,程序里的函数和数学里的函数,都是对一类操作的抽象、总结。比如y=x^2,不管你传入的x是多少,运算的实质没有变化。同时,我们还可以看到,使用函数的好处,即一次编写,多次使用。

一、基本概念

1.创建和调用

def my_sqr(x):
    return x**2


y = my_sqr(3)
print('3的平方是:', y)


# 输出:3的平方是: 9

2.参数

(1)位置参数,调用时参数的数量和位置必须和定义时一致

def func(name, age):
    print('我的名字是:', name)
    adult = False
    if age >= 18:
        adult = True
    return adult


# 函数定义时,有两个参数,若调用时数量不一致会报错
func('lily')
# 输出:TypeError: func() missing 1 required positional argument: 'age'

# 参数位置不对应时,可能报错可能结果不可知
# 输出:TypeError: '>=' not supported between instances of 'str' and 'int'

(2)关键字参数,即通过key=value的方式传参,优点是不受顺序制约,且可读性强。

def func(name, age):
    print('我的名字是:', name)
    adult = False
    if age >= 18:
        adult = True
    return adult


func(age=20, name='lily')

(3)默认参数,为参数设置默认值

def func(name, age, adult=False):
    print('我的名字是:', name)
    if age >= 18:
        adult = True
    return adult

 如果使用可变对象作为函数默认参数,很容易产生不可知结果,如下例

def func(a, l=[]):
    l.append(a)
    print(l)
 
# 执行两次
func(1)
func(1)
 

一样的函数和参数,输出应该是一样的。实际情况却不是如此,具体参看另一篇博客

Python 避免可变对象作为函数默认参数_Jiangugu的博客-CSDN博客

(4)可变参数

 主要考虑的是,事先不知道调用者会传递多少参数。

①*args,参数传递形式:(p1, p2)

def func(name, age, *args):
    print('我的名字是:', name)
    if age >= 18:
        print('我是成年人')
    for arg in args:
        print('我喜欢的食物有:', arg)


func('lily', 20, '铁锅炖大鹅', '松鼠桂鱼')


# 输出:我的名字是: lily
#       我是成年人
#       我喜欢的食物有: 铁锅炖大鹅
#       我喜欢的食物有: 松鼠桂鱼

②**kwargs,参数传递形式:{p1:value1, p2:value2}

def func(name, age, **kwargs):
    print('我的名字是:', name)
    if age >= 18:
        print('我是成年人')
    for key, value in kwargs.items():
        print('我喜欢的{}是{}'.format(key, value))


func('lily', 20, food='回锅肉', drink='蜜雪冰城')


# 输出:我的名字是: lily
#       我是成年人
#       我喜欢的food是回锅肉
#       我喜欢的drink是蜜雪冰城

3.返回

(1)函数可以没有返回,调用者此时若强行赋值给变量,将得到None

def func(name):
    print('我的名字是:', name)


r = func('lily')
print(r)


# 输出:我的名字是: lily
#       None

 (2)返回多个变量时,会被包装成一个元组

def func():
    return 1, 2, 3


r = func()
print(type(r))
print(r)


# 输出:<class 'tuple'>
#       (1, 2, 3)

 (3)基于Python中一切皆对象的理念,这里也可以提出一切皆可返回的概念

class Adult:
    def say(self):
        print('我是成年人')


class Child:
    def say(self):
        print('我还是个孩子')


def func(age):
    if age >= 18:
        return Adult()
    else:
        return Child()


r = func(17)
r.say()


# 输出:我还是个孩子

二、综合使用

1.良好的注释和断言

编写一个好的函数,应该有一个良好的注释,阐明函数的作用、参数的含义。更好地,能对参数进行一些断言(而不是运行中才抛异常)。

def get_day_of_year(year, month, day):
    """
    这个函数可以求一个日期属于当年的第几天
    :param year: 公元年份,如2021
    :param month: 
    :param day: 
    :return: 第几天,如365
    """
    assert year >= 0 and 1 <= month <= 12 and 1 <= day <= 31,\
        "illegal input, need year >= 0 and 1 <= month <= 12 and 1 <= day <= 31"

    m = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]
    if year % 400 == 0 or (year % 100 != 0 and year % 4 == 0):
        m[1] = 29
    day_of_year = sum(m[:(month-1)])+day
    return day_of_year

这里抛砖引玉,更多优秀函数编写可以查看python源码和第三方库。

2.局部变量和全局变量

函数内部定义的变量属于局部变量,作用范围仅限函数内部。全局变量定在在函数外,作用范围包括函数内外部。

def func():
    # 如果要修改全局的g,需要Global g,否则,不会影响全局变量
    g = -10
    print('局部g的值是:', g)


g = 10
func()
print('全局g的值是:', g)


# 输出:局部g的值是: -10
#       全局g的值是: 10

3.递归

函数里调用本函数,如利用递归生成斐波拉楔数列(前两项相加得第三个数)。递归最重要的一点就是要有结束条件。

def func(n):
    if n == 1:
        return 1
    elif n == 2:
        return 2
    else:
        return func(n-1) + func(n-2)

4.函数闭包

闭包,是指内部函数使用了外部函数得变量。当外部函数执行完,普通变量会消失,但该被使用的变量会存在。

def out():
    p = '我是一个外部变量'

    def inner():
        print(p)

    return inner

# p是out函数的变量,当执行完out(),p应该随之消失
r = out()
# 通过输出可以看到,变量p仍然存在
r()


# 输出:我是一个外部变量

再看另一个例子,内部函数使用了外部函数的变量i。i虽然在for循环中由0-1-2,但闭包的性质决定了,fs列表中的三个内部函数引用的的i的值是其最后一个时刻的值,即i=2。因此,输出都是2

def out():
    fs = []
    # 调用my_func时,内部函数func还没有被执行
    # 闭包的性质决定了,内部函数会保留外部函数变量的最后一个时刻的值
    for i in range(3):
        def inner():
            return i
        # 如果这里改成fs.append(inner()),那就不是闭包问题了
        fs.append(inner)
    return fs


f1, f2, f3 = out()
print(f1())
print(f2())
print(f3())


# 输出:2
#       2
#       2

5.装饰器

装饰器也是利用了闭包的原理,好处是无需修改基本功能函数,只需对原函数进行一层“装饰”,即可扩展其功能。比如我们有个函数是origin,之前设计时没考虑权限问题,现在要求登陆才可以访问。我们就可以在不修改原函数的情况下,通过语法糖@套一层登陆验证。

def login_require(func):
    def wrapper(username, psw):
        if username == 'root' and psw == '123456789':
            print('通过认证')
            print('执行其他功能')
            return func()
        else:
            print('用户名或密码错误')
            return
    return wrapper


@login_require
def origin():
    print('登陆成功才能执行的内容')


origin('root', '123456789')


# 输出:通过认证
#       执行其他功能
#       登陆成功才能执行的内容

6.匿名函数

当你想实现一个简单的函数却又不想为其命名时,就可以使用lambda

f = lambda x: x**3
print('2的3次方是:', f(2))


# 输出:2的3次方是: 8

一般lambda函数都是作为其他函数的参数使用的,比如排序

# 先根据条件1排序,相等时再根据条件2排序
lst = [['lucy', 20], ['lily', 20], ['adam', 18], ['tom', 23], ['tim', 19]]
lst.sort(key=lambda x: (x[1], x[0]))
print(lst)


# 输出:[['adam', 18], ['tim', 19], ['lily', 20], ['lucy', 20], ['tom', 23]]

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值