超详解——python函数定义和调用——小白篇

2024-06-15 10:30:20 浏览数 (2)

1. 函数的参数

函数可以接受多种类型的参数,包括位置参数、关键字参数和默认参数。

位置参数

位置参数是最常见的参数类型,它们必须按照定义时的顺序传递给函数。

代码语言:javascript复制
def greet(name, message):
    print(f"{message}, {name}!")

greet("Alice", "Hello")  # 输出:Hello, Alice!

关键字参数

关键字参数允许在调用函数时指定参数名,参数的顺序可以与定义时不同。

代码语言:javascript复制
greet(name="Alice", message="Hello")  # 输出:Hello, Alice!
greet(message="Hi", name="Bob")  # 输出:Hi, Bob!

默认参数

默认参数在定义函数时指定默认值,如果调用时未传递该参数,则使用默认值。

代码语言:javascript复制
def greet(name, message="Hello"):
    print(f"{message}, {name}!")

greet("Alice")  # 输出:Hello, Alice!
greet("Bob", "Hi")  # 输出:Hi, Bob!

2. 关键字参数

关键字参数的使用使得函数调用更加灵活和清晰,尤其是当函数有多个参数时。

代码语言:javascript复制
def describe_pet(animal_type, pet_name):
    print(f"I have a {animal_type} named {pet_name}.")

describe_pet(animal_type="dog", pet_name="Rex")  # 输出:I have a dog named Rex.
describe_pet(pet_name="Whiskers", animal_type="cat")  # 输出:I have a cat named Whiskers.

使用关键字参数可以使代码更具可读性,因为每个参数的意义都显而易见。

3. 参数组

Python提供了 *args**kwargs 来处理不定长参数。

*args*args 接收任意数量的位置参数,传递给函数时以元组的形式存在。

代码语言:javascript复制
def make_pizza(size, *toppings):
    print(f"Making a {size} inch pizza with the following toppings:")
    for topping in toppings:
        print(f"- {topping}")

make_pizza(12, "pepperoni", "mushrooms", "green peppers")
# 输出:
# Making a 12 inch pizza with the following toppings:
# - pepperoni
# - mushrooms
# - green peppers

**kwargs

**kwargs 接收任意数量的关键字参数,传递给函数时以字典的形式存在。

代码语言:javascript复制
def build_profile(first, last, **user_info):
    profile = {
        "first_name": first,
        "last_name": last
    }
    profile.update(user_info)
    return profile

user_profile = build_profile("Albert", "Einstein", location="Princeton", field="Physics")
print(user_profile)
# 输出:
# {'first_name': 'Albert', 'last_name': 'Einstein', 'location': 'Princeton', 'field': 'Physics'}

4. 函数重载

Python 不支持传统意义上的函数重载(即同名函数的不同定义),但是可以通过使用可变参数和默认参数来实现类似的功能。这种方法使得一个函数可以根据传递的参数数量和类型表现出不同的行为。下面我们详细讨论如何利用这些特性实现函数的“重载”。

使用默认参数

默认参数允许我们为函数参数指定默认值,从而使函数能够接受不同数量的参数。

代码语言:javascript复制
def add(a, b=0, c=0):
    return a   b   c

# 调用add函数,不传递b和c,使用默认值0
print(add(1))       # 输出:1

# 调用add函数,传递b,使用c的默认值0
print(add(1, 2))    # 输出:3

# 调用add函数,传递所有参数
print(add(1, 2, 3)) # 输出:6

在这个例子中,add函数通过提供默认参数bc,可以灵活地接受1个、2个或3个参数。

使用可变参数

可变参数允许函数接受任意数量的位置参数或关键字参数。*args用于接受任意数量的位置参数,**kwargs用于接受任意数量的关键字参数。

使用*args
代码语言:javascript复制
def add(*args):
    return sum(args)

# 调用add函数,传递不同数量的参数
print(add(1))            # 输出:1
print(add(1, 2))         # 输出:3
print(add(1, 2, 3, 4))   # 输出:10

在这个例子中,add函数通过*args接收任意数量的位置参数,并使用内置的sum函数计算它们的总和。

使用**kwargs
代码语言:javascript复制
def build_profile(first, last, **kwargs):
    profile = {
        'first_name': first,
        'last_name': last
    }
    profile.update(kwargs)
    return profile

# 调用build_profile函数,传递不同数量的关键字参数
user_profile = build_profile('Albert', 'Einstein', location='Princeton', field='Physics')
print(user_profile)
# 输出:{'first_name': 'Albert', 'last_name': 'Einstein', 'location': 'Princeton', 'field': 'Physics'}

在这个例子中,build_profile函数通过**kwargs接收任意数量的关键字参数,并将它们添加到profile字典中。

结合使用默认参数和可变参数

结合使用默认参数和可变参数,可以创建功能强大的函数,模拟重载的效果。

代码语言:javascript复制
def add(a, b=0, *args):
    result = a   b
    for arg in args:
        result  = arg
    return result

# 调用add函数,传递不同数量的参数
print(add(1))            # 输出:1
print(add(1, 2))         # 输出:3
print(add(1, 2, 3, 4))   # 输出:10

在这个例子中,add函数使用默认参数b和可变参数*args,能够处理不同数量的参数,并计算它们的总和。

使用类型检查实现更复杂的重载

虽然Python不支持函数重载,但可以通过类型检查实现类似的效果。使用isinstancetype函数可以根据参数类型执行不同的操作。

代码语言:javascript复制
def process_data(data):
    if isinstance(data, list):
        return [x * 2 for x in data]
    elif isinstance(data, dict):
        return {k: v * 2 for k, v in data.items()}
    else:
        return data * 2

# 调用process_data函数,传递不同类型的参数
print(process_data(10))           # 输出:20
print(process_data([1, 2, 3]))    # 输出:[2, 4, 6]
print(process_data({'a': 1, 'b': 2}))  # 输出:{'a': 2, 'b': 4}

在这个例子中,process_data函数根据输入数据的类型执行不同的操作,从而实现了函数重载的效果。

使用装饰器实现函数重载

装饰器可以用来创建一个重载系统,根据传递的参数类型和数量选择不同的实现。

代码语言:javascript复制
from functools import singledispatch

@singledispatch
def fun(arg):
    print(f"Default implementation: {arg}")

@fun.register(int)
def _(arg):
    print(f"Integer implementation: {arg}")

@fun.register(list)
def _(arg):
    print(f"List implementation: {arg}")

# 调用fun函数,传递不同类型的参数
fun(10)          # 输出:Integer implementation: 10
fun([1, 2, 3])   # 输出:List implementation: [1, 2, 3]
fun('Hello')     # 输出:Default implementation: Hello

在这个例子中,singledispatch装饰器用于创建一个基于类型的重载系统。通过注册不同类型的实现,可以根据参数类型选择适当的函数版本。

5. 函数的返回值

函数可以返回单个值或多个值。多个返回值会被打包成元组。

单个返回值

代码语言:javascript复制
def get_full_name(first_name, last_name):
    return first_name   " "   last_name

full_name = get_full_name("John", "Doe")
print(full_name)  # 输出:John Doe

多个返回值

代码语言:javascript复制
def get_coordinates():
    return 10.0, 20.0

coordinates = get_coordinates()
print(coordinates)  # 输出:(10.0, 20.0)

# 也可以使用拆包
x, y = get_coordinates()
print(f"x: {x}, y: {y}")  # 输出:x: 10.0, y: 20.0

6. 函数的内置属性

函数对象有一些内置属性,可以用来获取函数的相关信息。

__name__

代码语言:javascript复制
def sample_function():
    """This is a sample function."""
    pass

print(sample_function.__name__)  # 输出:sample_function

__doc__

代码语言:javascript复制
print(sample_function.__doc__)  # 输出:This is a sample function.

这些内置属性可以用于调试和文档生成。

__annotations__

__annotations__属性是一个字典,包含了函数参数和返回值的类型注解。类型注解在大型项目中非常有用,因为它们可以提高代码的可读性,并帮助静态类型检查工具进行类型检查。

代码语言:javascript复制
def add(a: int, b: int) -> int:
    return a   b

print(add.__annotations__)  # 输出:{'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
__defaults__

__defaults__属性返回一个包含函数默认参数值的元组。如果函数没有默认参数值,则返回None。这个属性可以帮助我们了解函数在调用时使用的默认值。

代码语言:javascript复制
def greet(name, message="Hello"):
    print(f"{message}, {name}!")

print(greet.__defaults__)  # 输出:('Hello',)

__code__

__code__属性是一个代码对象,包含了函数的字节码以及其他实现细节。通过这个属性,我们可以访问许多与函数实现相关的信息,比如局部变量和代码行号。

代码语言:javascript复制
def sample_function():
    pass

print(sample_function.__code__)  # 输出:<code object sample_function at 0x..., file "<stdin>", line 1>

访问代码对象的属性

代码对象本身也有一些属性,例如:

co_argcount: 函数的参数个数

co_varnames: 函数中定义的变量名

co_filename: 函数所在文件的名称

co_name: 函数的名称

co_firstlineno: 函数定义的行号

代码语言:javascript复制
def example_function(a, b):
    c = a   b
    return c

code = example_function.__code__
print(code.co_argcount)     # 输出:2
print(code.co_varnames)     # 输出:('a', 'b', 'c')
print(code.co_filename)     # 输出:<当前文件的路径>
print(code.co_name)         # 输出:example_function
print(code.co_firstlineno)  # 输出:1(函数定义所在的行号)

__globals__

__globals__属性返回一个引用函数全局符号表的字典。这个符号表包含了在函数定义时所在模块中的所有全局变量。通过这个属性,我们可以访问函数的全局上下文。

代码语言:javascript复制
x = 10

def sample_function():
    return x

print(sample_function.__globals__)  # 输出全局变量的字典,包括x: 10

__closure__

__closure__属性返回一个包含函数闭包的元组。闭包是函数定义体中引用的自由变量的绑定。这对于理解和调试闭包和高阶函数非常有用。

代码语言:javascript复制
def outer_function(x):
    def inner_function(y):
        return x   y
    return inner_function

closure_function = outer_function(10)
print(closure_function.__closure__)  # 输出:(<cell at 0x..., cell_contents=10>,)

__dict__

__dict__属性是一个包含函数属性的字典。我们可以动态地向函数添加属性,从而在运行时修改函数的行为。

代码语言:javascript复制
def sample_function():
    pass

sample_function.custom_attr = "This is a custom attribute"
print(sample_function.__dict__)  # 输出:{'custom_attr': 'This is a custom attribute'}

7. 函数也可以作为函数的参数

函数作为参数传递给其他函数,使得函数非常灵活和强大

代码语言:javascript复制
def apply_function(func, value):
    return func(value)

def square(x):
    return x * x

result = apply_function(square, 5)
print(result)  # 输出:25

通过这种方式,可以将不同的函数传递给 apply_function,从而实现不同的功能。

8. 函数也可以作为函数的返回值

函数可以返回另一个函数,从而创建高阶函数。

代码语言:javascript复制
def create_adder(x):
    def adder(y):
        return x   y
    return adder

add_five = create_adder(5)
print(add_five(10))  # 输出:15

在这个例子中,create_adder 返回一个新的函数 adder,该函数可以使用创建时的环境变量 x

总结:

  • 位置参数、关键字参数和默认参数使得函数调用灵活多样。
  • *args**kwargs 提供了处理不定长参数的能力。
  • 虽然Python不支持函数重载,但通过可变参数和默认参数可以实现类似功能。
  • 函数可以返回单个或多个值。
  • 函数的内置属性如 __name____doc__ 提供了函数的相关信息。
  • 函数可以作为参数传递给其他函数,也可以作为返回值返回,从而创建高阶函数。

0 人点赞