functools高级编程技巧,Python代码优雅神器

functools高级编程技巧,Python代码优雅神器

partial()

从概念上讲, partial是一种通过将部分参数应用于现有函数来创建新函数的便捷方法 functools.partial 允许你创建一个新函数,该函数是现有函数的修改版本。你可以为一个或多个参数指定默认值,这意味着你可以使用比原始函数更少的参数来调用新函数。

functools高级编程技巧,Python代码优雅神器
from functools import partial

# Define a regular function
def func(arg1, arg2, arg3):
    # Do something
    pass

# Create a partial function by fixing some arguments
partial_func = partial(func, fixed_arg1, fixed_arg2)

# Now call it with the remaining arguments
result = partial_func(arg3)

示例1

from functools import partial

def calculate_bmi(height: float, weight: float) -> str:
    bmi = weight / (height ** 2)
    return f"Your Body Mass Index (BMI) is {bmi:.2f}."

# Set my height once and reuse the function
bmi_for_my_height = partial(calculate_bmi, height=1.75)

# Track weight changes over time
print(bmi_for_my_height(weight=63))  # 01/01/2024
# → Your Body Mass Index (BMI) is 20.57.

print(bmi_for_my_height(weight=66))  # 01/06/2024
# → Your Body Mass Index (BMI) is 21.55.

示例2:

from functools import partial

def weather_report(city: str, temp: int, rain: bool) -> str:
    return f"""Temperature in {city} is {temp}ºC.
It is {'raining' if rain else 'not raining'}."""

# Regular usage
print(weather_report('Lima', 17, True))
# → Temperature in Lima is 17ºC.
# → It is raining.

# Lock in the city as 'Madrid'
madrid_weather_report = partial(weather_report, city="Madrid")

# Now just update the weather
print(madrid_weather_report(temp=24, rain=False))
# → Temperature in Madrid is 24ºC.
# → It is not raining.

将 partial() 与内置函数一起使用

from functools import partial

# Create a version of print with a custom separator
print_with_sep = partial(print, sep="\n")

# Now this prints each item on a new line
print_with_sep("Name: Jaume", "Age: 25", "Pet: Mimi")
# → Name: Jaume
# → Age: 25
# → Pet: Mimi

无需包装器,无需额外函数——只需一行代码,即可完成。这种调整可立即让你的代码更简洁、更易读。

Partial Functions vs. Lambda

Partial Functions:作用是为现有函数预填充参数。语法 : partial(existing_func, fixed_args)

与现有功能绑定——保持代码结构化和可读性。非常适合重复使用和简化现有功能。更明确、更易于阅读,尤其是在较大的项目中。

from functools import partial

def power(base, exponent):
    return base ** exponent

# Regular function call for squaring
print(power(5, 2))
# → 25

# Using partial to create a new function for squaring
square = partial(power, exponent=2)
print(square(5))
# → 25

 Lambda Functions: 创建匿名内联函数。语法 : lambda args: expression

超轻且灵活,可实现快速、一次性功能。非常适合短期、临时的操作。当函数变得复杂时,可能会变得混乱且难以阅读。

# Using a lambda function to square a number
square = lambda base: base ** 2
print(square(5))
# → 25

partial() :当我想重用现有函数并固定某些参数时,我会使用它,尤其是当可读性和结构很重要时。lambda :当我不需要完整的函数定义时,我会使用 lambda 来完成快速的内联任务。

Reduce

functools.reduce 将两个参数的函数从左到右累积应用于可迭代对象中的项,从而将可迭代对象减少为单个值。

from functools import reduce
def add(x, y):
    return x + y
# Sum the elements of the list [1, 2, 3, 4]
result = reduce(add, [1, 2, 3, 4])
print(result)  # Output: 10

lru_cache

functools.lru_cache 是一个实现最近最少使用(LRU: Least Recently Used cache)缓存的装饰器。它可以用于缓存函数的结果,以便后续使用相同参数的调用可以更快地返回结果。

from functools import lru_cache

@lru_cache(maxsize=None)
def fib(n):
    if n < 2:
        return n
    return fib(n-1) + fib(n-2)
print(fib(10))  # Output: 55
from functools import lru_cache
import time

@lru_cache(maxsize=5)  # Store up to 5 recent results
def slow_function(n):
    time.sleep(2)  # Simulate a time-consuming computation
    return n * n

# First call (takes time)
# 第一次呼叫(需要时间)
print(slow_function(4))  # Output: 16 (after 2 seconds)

# Second call (instant)  # 第二次呼叫(即时)
print(slow_function(4))  # Output: 16 (retrieved from cache)

cmp_to_key

functools.cmp_to_key 函数用于将比较函数转换为键函数。它可以用来通过自定义比较函数对列表进行排序。

from functools import cmp_to_key

def compare(x, y):
    if x < y:
        return -1
    elif x > y:
        return 1
    else:
        return 0
# Sort the list [3, 1, 2] using the comparison function
sorted_list = sorted([3, 1, 2], key=cmp_to_key(compare))
print(sorted_list)  # Output: [1, 2, 3]

扩展自定义排序以处理更复杂的数据结构,例如按多个键对字典列表进行排序。

from functools import cmp_to_key

# Comparison function for multi-key sorting
def compare_multi_keys(a, b):
    if a['age'] != b['age']:
        return a['age'] - b['age']
    return (a['name'] > b['name']) - (a['name'] < b['name'])


# Sample data
data = [
    {'name': 'Alice', 'age': 30},
    {'name': 'Bob', 'age': 24},
    {'name': 'Charlie', 'age': 25},
    {'name': 'Dave', 'age': 35}
]
# Sorting using cmp_to_key
sorted_data = sorted(data, key=cmp_to_key(compare_multi_keys))
print(sorted_data)

singledispatch

functools.singledispatch 是一个函数装饰器,它实现了单次调度(single dispatch),这是一种根据单个参数的类型选择要调用的函数的机制。它类似于内置的 dispatchmethod 装饰器,但它作用于普通函数而非方法。它将一个函数转换为一个通用函数 ——该函数可以根据第一个参数的类型有不同的实现。

from functools import singledispatch

@singledispatch
def func(arg):
    print("Unknown type")
@func.register(int)
def _(arg):
    print("Got an int")
@func.register(str)
def _(arg):
    print("Got a string")
func(5)  # Output: "Got an int"
func("hello")  # Output: "Got a string"
func([1, 2, 3])  # Output: "Unknown type"

在 @serialize.register(<type>) 上,每当你用这种类型调用 serialize() 时,都使用这个版本的函数。基本函数( serialize )是默认的处理程序 。每个 .register(type) 函数都是一个专门的版本 。仅考虑第一个参数的类型 。这就是为什么它被称为单次分派。

from functools import singledispatch
import json

@singledispatch
def serialize(value):
    raise NotImplementedError(f"Cannot serialize object of type {type(value)}")

@serialize.register(str)
def _(value):
    return value

@serialize.register(dict)
def _(value):
    return json.dumps(value)

@serialize.register(list)
def _(value):
    return ','.join(map(str, value))

# Example usage
print(serialize("hello"))       # hello
print(serialize({"a": 1}))      # {"a": 1}
print(serialize([1, 2, 3]))     # 1,2,3

total_ordering

functools.total_ordering 是一个类装饰器,它基于单个比较方法为类生成所有丰富的比较方法(例如 __lt__ 、 __le__ 等)。定义自定义类时,实现所有比较方法( <, >, <=, >=, ==, != )可能非常繁琐。total_ordering 装饰 total_ordering 会为您生成缺少的比较方法,只需要 __eq__ 和另一个比较方法即可。

from functools import total_ordering

@total_ordering
class Person:
    def __init__(self, name, age):
        self.name = name
        self.age = age
    def __eq__(self, other):
        return self.age == other.age
    def __lt__(self, other):
        return self.age < other.age
person1 = Person("Alice", 30)
person2 = Person("Bob", 25)
person3 = Person("Charlie", 30)
print(person1 > person2)  # Output: True
print(person1 < person3)  # Output: False
print(person1 == person3)  # Output: True

wraps

在 Python 中创建装饰器时,它们可能会遮挡原始函数的元数据(例如其名称和文档字符串)。functools 中的 wraps 装饰 functools 可以保留这些信息。

from functools import wraps

def log_function(func):
    @wraps(func)
    def wrapper(*args, **kwargs):
        print(f"Calling {func.__name__} with {args}, {kwargs}")
        return func(*args, **kwargs)
    return wrapper

@log_function
def greet(name):
    """Says hello to the given name."""
    return f"Hello, {name}!"

print(greet("Sam"))
print(greet.__name__)  # Output: greet (instead of wrapper)
print(greet.__doc__)   # Output: Says hello to the given name.

update_wrapper wrapper

functools.update_wrapper 函数用于更新包装函数,使其看起来像被包装函数。它通常与 functools.wrap 装饰器结合使用,后者是用于装饰包装函数的装饰器,用于确保包装函数保留被包装函数的文档字符串、名称和其他属性。

如何使用 update_wrapper 和 wrap 创建一个简单的装饰器,为函数名称添加前缀:

import functools

def prefix_decorator(prefix):
    def decorator(func):
        @functools.wrap
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        wrapper.__name__ = f"{prefix}_{func.__name__}"
        return wrapper
    return decorator
@prefix_decorator("test")
def add(x, y):
    """Add two numbers together"""
    return x + y
print(add.__name__)  # Output: "test_add"
print(add.__doc__)  # Output: "Add two numbers together"

update_wrapper 函数也可以用于直接更新包装器函数的属性,而无需使用 wrap 装饰器。以下是示例:

import functools

def prefix_decorator(prefix):
    def decorator(func):
        def wrapper(*args, **kwargs):
            return func(*args, **kwargs)
        functools.update_wrapper(wrapper, func)
        wrapper.__name__ = f"{prefix}_{func.__name__}"
        return wrapper
    return decorator
@prefix_decorator("test")
def add(x, y):
    """Add two numbers together"""
    return x + y
print(add.__name__)  # Output: "test_add"
print(add.__doc__)  # Output: "Add two numbers together"

原创文章。转载请注明: 作者:meixi 网址: https://www.icnma.com
Like (0)
meixi管理
Previous 30/05/2025 15:39
Next 14/11/2023 10:59

猜你想看

Leave a Reply

Please Login to Comment