MK
摩柯社区 - 一个极简的技术知识社区
AI 面试

Python函数编写的实用技巧

2021-08-166.4k 阅读

函数参数的灵活运用

位置参数与关键字参数

在Python中,函数定义时可以指定参数。位置参数是按照参数定义的顺序依次传递值。例如:

def add_numbers(a, b):
    return a + b


result = add_numbers(3, 5)
print(result)  

这里ab就是位置参数,调用函数时传递的35分别对应ab

而关键字参数则是通过参数名来传递值,不受位置顺序的限制。例如:

def describe_person(name, age):
    return f"{name} is {age} years old."


description = describe_person(age=25, name="Alice")
print(description)  

在这个例子中,我们通过关键字参数明确指定了agename的值,即使顺序与函数定义时不同也能正确赋值。

默认参数

默认参数为函数参数提供了一个默认值。当调用函数时没有传递该参数的值,就会使用默认值。例如:

def greet(name, greeting="Hello"):
    return f"{greeting}, {name}!"


print(greet("Bob"))  
print(greet("Charlie", "Hi"))  

greet函数中,greeting参数有默认值"Hello"。如果调用时只传递name参数,就会使用默认的问候语;如果传递了greeting参数,就会使用传递的值。

可变参数

*args

*args用于接受任意数量的位置参数。它将所有额外的位置参数收集到一个元组中。例如:

def sum_all(*args):
    total = 0
    for num in args:
        total += num
    return total


print(sum_all(1, 2, 3))  
print(sum_all(10, 20, 30, 40))  

sum_all函数中,*args收集了所有传递的位置参数,然后我们可以通过循环对这些参数进行操作。

**kwargs

**kwargs用于接受任意数量的关键字参数。它将所有额外的关键字参数收集到一个字典中。例如:

def print_info(**kwargs):
    for key, value in kwargs.items():
        print(f"{key}: {value}")


print_info(name="David", age=30, city="New York")  

print_info函数中,**kwargs将传递的关键字参数收集到字典中,然后我们可以通过字典的items方法遍历并打印所有的键值对。

函数返回值的处理

单个返回值

函数最常见的就是返回单个值。例如,前面的add_numbers函数返回两个数相加的结果:

def multiply_numbers(a, b):
    return a * b


product = multiply_numbers(4, 6)
print(product)  

这里multiply_numbers函数返回ab的乘积,调用函数后将返回值赋给product变量。

多个返回值

Python函数可以返回多个值,实际上是返回一个元组。例如:

def divmod_result(a, b):
    quotient = a // b
    remainder = a % b
    return quotient, remainder


result = divmod_result(10, 3)
print(result)  
q, r = divmod_result(17, 5)
print(f"Quotient: {q}, Remainder: {r}")  

divmod_result函数中,返回了商和余数。我们可以将返回值赋给一个变量,这个变量实际上是一个元组;也可以使用多个变量同时接收返回值,按照顺序依次对应。

返回None

如果函数没有显式的return语句,或者return语句后面没有值,那么函数会隐式地返回None。例如:

def print_message(message):
    print(message)


return_value = print_message("This is a message.")
print(return_value)  

print_message函数中,没有return语句,所以它返回None,我们将这个返回值赋给return_value并打印出来。

函数作用域

局部作用域

在函数内部定义的变量具有局部作用域,只能在函数内部访问。例如:

def local_scope_example():
    local_variable = 10
    print(local_variable)


local_scope_example()
# print(local_variable)  # 这行代码会报错,因为local_variable在函数外部不可访问

local_scope_example函数中定义的local_variable变量,其作用域仅限于该函数内部。如果在函数外部尝试访问它,会导致NameError

全局作用域

在模块顶层定义的变量具有全局作用域,可以在整个模块中访问。例如:

global_variable = 20


def access_global_variable():
    print(global_variable)


access_global_variable()
print(global_variable)  

这里的global_variable定义在模块顶层,函数access_global_variable可以访问它,在模块的其他地方也可以访问。

嵌套函数与闭包

嵌套函数

函数内部可以定义另一个函数,这就是嵌套函数。例如:

def outer_function():
    outer_variable = 10

    def inner_function():
        inner_variable = 20
        result = outer_variable + inner_variable
        return result

    return inner_function()


print(outer_function())  

outer_function内部定义了inner_functioninner_function可以访问outer_function中的变量outer_variable

闭包

闭包是一种特殊的嵌套函数情况,内部函数记住并访问其外部函数作用域中的变量,即使外部函数已经返回。例如:

def make_multiplier(factor):
    def multiplier(number):
        return number * factor

    return multiplier


double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5))  
print(triple(5))  

make_multiplier函数中,返回的multiplier函数记住了factor变量的值,形成了闭包。doubletriple是不同的闭包实例,分别将传入的数字乘以2和3。

函数装饰器

什么是函数装饰器

函数装饰器是一种特殊的可调用对象,它接受一个函数作为参数,并返回一个新的函数。它可以在不修改原函数代码的情况下,为函数添加额外的功能。例如,我们要为一个函数添加日志记录功能:

import functools


def log_function_call(func):
    @functools.wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling function {func.__name__}")
        result = func(*args, **kwargs)
        print(f"Function {func.__name__} returned {result}")
        return result

    return wrapper


@log_function_call
def add(a, b):
    return a + b


add_result = add(3, 5)
print(add_result)  

在这个例子中,log_function_call是一个装饰器,它接受add函数作为参数,返回一个新的函数wrapperwrapper函数在调用原add函数前后添加了日志记录功能。@log_function_call语法是一种简洁的方式来应用装饰器。

带参数的装饰器

装饰器本身也可以接受参数。例如,我们要根据不同的日志级别记录函数调用:

import functools


def log_with_level(level):
    def log_function_call(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            log_prefix = f"[{level}] " if level else ""
            print(f"{log_prefix}Calling function {func.__name__}")
            result = func(*args, **kwargs)
            print(f"{log_prefix}Function {func.__name__} returned {result}")
            return result

        return wrapper

    return log_function_call


@log_with_level("INFO")
def subtract(a, b):
    return a - b


subtract_result = subtract(10, 5)
print(subtract_result)  

在这个例子中,log_with_level是一个接受参数level的函数,它返回一个装饰器log_function_calllog_function_call再返回wrapper函数,实现了根据不同日志级别记录函数调用的功能。

递归函数

基本概念

递归函数是指在函数的定义中使用自身来解决问题的函数。例如,计算阶乘的递归函数:

def factorial(n):
    if n == 0 or n == 1:
        return 1
    else:
        return n * factorial(n - 1)


print(factorial(5))  

factorial函数中,当n为0或1时,直接返回1;否则,通过调用自身factorial(n - 1)来计算n的阶乘。

递归的终止条件

递归函数必须有终止条件,否则会导致无限递归,最终耗尽系统资源。在上述factorial函数中,n == 0 or n == 1就是终止条件。

递归与迭代的比较

递归通常代码简洁直观,但在处理大规模问题时可能会因为栈溢出而失败,因为每次递归调用都会在栈中增加一个新的帧。而迭代(使用循环)通常更高效,因为它不会占用额外的栈空间。例如,用迭代方式计算阶乘:

def factorial_iterative(n):
    result = 1
    for i in range(1, n + 1):
        result *= i
    return result


print(factorial_iterative(5))  

迭代方式通过循环来计算阶乘,避免了递归可能带来的栈溢出问题,在性能上更有优势,尤其是对于较大的n值。

生成器函数

生成器的概念

生成器是一种特殊的迭代器,它的创建使用生成器函数。生成器函数使用yield语句而不是return语句返回值。例如,一个简单的生成器函数来生成斐波那契数列:

def fibonacci_generator():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b


fib = fibonacci_generator()
for _ in range(10):
    print(next(fib))  

fibonacci_generator函数中,yield语句每次返回一个值,并暂停函数的执行,下次调用next时从暂停的地方继续执行。

生成器的优势

生成器的优势在于它不需要一次性生成所有的值,而是按需生成。这在处理大数据集或无限序列时非常有用,可以节省大量的内存。例如,生成一个包含一百万个数的列表会占用大量内存:

big_list = list(range(1000000))  

而使用生成器生成同样数量的数,只会在需要时生成一个数,不会占用大量内存:

def number_generator():
    for i in range(1000000):
        yield i


gen = number_generator()
for num in gen:
    if num == 100:
        break
    print(num)  

这里通过生成器,我们可以在需要时获取值,而不是一次性生成所有的值并占用大量内存。

函数式编程风格

纯函数

纯函数是指函数的返回值只取决于输入参数,并且没有副作用。例如:

def pure_add(a, b):
    return a + b


result1 = pure_add(3, 5)
result2 = pure_add(3, 5)
print(result1 == result2)  

pure_add函数是一个纯函数,只要输入相同,输出就一定相同,并且不会对外部环境产生任何影响。

高阶函数

高阶函数是指接受一个或多个函数作为参数,或者返回一个函数的函数。例如,map函数就是一个高阶函数,它接受一个函数和一个可迭代对象,对可迭代对象的每个元素应用该函数:

def square(x):
    return x * x


numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(square, numbers))
print(squared_numbers)  

这里map函数接受square函数和numbers列表作为参数,将square函数应用到numbers列表的每个元素上,并返回一个迭代器,我们通过list将其转换为列表。

匿名函数(lambda函数)

匿名函数是一种没有函数名的小型函数,使用lambda关键字定义。例如:

add = lambda a, b: a + b
print(add(3, 5))  

lambda函数通常用于需要一个简单函数但又不想定义一个完整函数的场景。例如,结合map函数使用:

numbers = [1, 2, 3, 4, 5]
squared_numbers = list(map(lambda x: x * x, numbers))
print(squared_numbers)  

这里使用lambda函数直接定义了平方操作,而不需要单独定义一个square函数。

通过以上这些实用技巧,可以让我们在编写Python函数时更加灵活、高效,编写出更简洁、健壮且易于维护的代码。无论是处理复杂的业务逻辑,还是进行数据处理和算法实现,这些技巧都能发挥重要作用。在实际编程过程中,需要根据具体的需求和场景,合理地运用这些技巧,以达到最佳的编程效果。