Определение функций и аргументы
Функции -- основной строительный блок программ на Python. Python предоставляет гибкую систему аргументов с позиционными, именованными, positional-only и keyword-only параметрами.
Определение функций
# Basic function definition
def greet(name: str) -> str:
"""Return a greeting message."""
return f"Привет, {name}!"
print(greet("Иван")) # Привет, Иван!
# Function without return value (returns None)
def log_message(message: str) -> None:
"""Print a log message with timestamp."""
from datetime import datetime
timestamp = datetime.now().strftime("%H:%M:%S")
print(f"[{timestamp}] {message}")
# Multiple return values (actually returns a tuple)
def divide(a: float, b: float) -> tuple[float, float]:
"""Return quotient and remainder."""
return a // b, a % b
quotient, remainder = divide(17, 5)
print(f"{quotient=}, {remainder=}") # quotient=3.0, remainder=2.0
# Early return
def find_first_negative(numbers: list[int]) -> int | None:
"""Find the first negative number in the list."""
for n in numbers:
if n < 0:
return n
return None
print(find_first_negative([1, 2, -3, 4])) # -3
print(find_first_negative([1, 2, 3])) # None
Docstrings (Google Style)
def calculate_bmi(weight: float, height: float) -> float:
"""Calculate Body Mass Index (BMI).
Args:
weight: Body weight in kilograms.
height: Height in meters.
Returns:
BMI value as a float.
Raises:
ValueError: If weight or height is not positive.
Examples:
>>> calculate_bmi(70, 1.75)
22.857142857142858
"""
if weight <= 0 or height <= 0:
raise ValueError("Weight and height must be positive")
return weight / height ** 2
# Access docstring
print(calculate_bmi.__doc__)
help(calculate_bmi)
Типы аргументов
Позиционные и именованные
def create_user(name: str, age: int, city: str = "Москва") -> dict:
"""Create a user dictionary."""
return {"name": name, "age": age, "city": city}
# Positional arguments
user1 = create_user("Иван", 25)
user2 = create_user("Мария", 30, "Петербург")
# Keyword arguments (in any order)
user3 = create_user(age=28, name="Пётр", city="Казань")
# Mixed (positional first, then keyword)
user4 = create_user("Анна", age=22)
Значения по умолчанию
# Default values
def connect(host: str = "localhost", port: int = 5432) -> str:
return f"Connected to {host}:{port}"
print(connect()) # localhost:5432
print(connect("db.example.com")) # db.example.com:5432
print(connect(port=3306)) # localhost:3306
# DANGER: mutable default arguments!
def add_item_bad(item: str, items: list[str] = []) -> list[str]:
"""BAD: mutable default is shared between calls!"""
items.append(item)
return items
print(add_item_bad("a")) # ['a']
print(add_item_bad("b")) # ['a', 'b'] - BUG! Expected ['b']
# CORRECT: use None as default for mutable arguments
def add_item(item: str, items: list[str] | None = None) -> list[str]:
"""GOOD: create a new list on each call."""
if items is None:
items = []
items.append(item)
return items
print(add_item("a")) # ['a']
print(add_item("b")) # ['b'] - correct!
*args и **kwargs
# *args - variable positional arguments (tuple)
def sum_all(*args: int) -> int:
"""Sum any number of arguments."""
return sum(args)
print(sum_all(1, 2, 3)) # 6
print(sum_all(1, 2, 3, 4, 5)) # 15
# **kwargs - variable keyword arguments (dict)
def build_html_tag(tag: str, **attrs: str) -> str:
"""Build an HTML tag with attributes."""
attr_str = " ".join(f'{k}="{v}"' for k, v in attrs.items())
return f"<{tag} {attr_str}>" if attr_str else f"<{tag}>"
print(build_html_tag("div", id="main", class_="container"))
# <div id="main" class_="container">
# Combined: regular + *args + **kwargs
def log(level: str, *messages: str, **context: str) -> None:
"""Log messages with context."""
msg = " ".join(messages)
ctx = ", ".join(f"{k}={v}" for k, v in context.items())
print(f"[{level}] {msg}" + (f" ({ctx})" if ctx else ""))
log("INFO", "User", "logged", "in", user="ivan", ip="192.168.1.1")
# [INFO] User logged in (user=ivan, ip=192.168.1.1)
# Passing *args and **kwargs to another function
def wrapper(*args, **kwargs):
"""Pass all arguments to the wrapped function."""
print(f"Calling with args={args}, kwargs={kwargs}")
return sum_all(*args)
Positional-only (/) и Keyword-only (*)
# Positional-only parameters (before /)
# Cannot be passed as keyword arguments
def pow(base: float, exp: float, /) -> float:
"""Power function with positional-only parameters."""
return base ** exp
print(pow(2, 10)) # 1024
# pow(base=2, exp=10) # TypeError!
# Keyword-only parameters (after *)
# Must be passed as keyword arguments
def create_file(path: str, *, mode: str = "w", encoding: str = "utf-8") -> None:
"""Create a file. mode and encoding must be keyword arguments."""
print(f"Creating {path} with mode={mode}, encoding={encoding}")
create_file("test.txt") # OK
create_file("test.txt", mode="a") # OK
# create_file("test.txt", "a") # TypeError!
# Combined: positional-only + regular + keyword-only
def search(query: str, /, *, limit: int = 10, offset: int = 0) -> str:
"""
query: positional-only (before /)
limit, offset: keyword-only (after *)
"""
return f"Search '{query}' limit={limit} offset={offset}"
print(search("Python", limit=5)) # Search 'Python' limit=5 offset=0
# search(query="Python") # TypeError! (positional-only)
# search("Python", 5) # TypeError! (keyword-only)
# Real-world example: Python's built-in sorted
# sorted(iterable, /, *, key=None, reverse=False)
# iterable is positional-only, key and reverse are keyword-only
Type Hints для функций
from typing import Callable, Any
from collections.abc import Sequence, Iterable
# Basic type hints
def greet(name: str, excited: bool = False) -> str:
suffix = "!!!" if excited else "."
return f"Привет, {name}{suffix}"
# Optional (Union with None)
def find(items: list[str], target: str) -> int | None:
try:
return items.index(target)
except ValueError:
return None
# Generic sequences
def first(items: Sequence[Any]) -> Any:
return items[0]
# Callable type hints
def apply(func: Callable[[int], int], value: int) -> int:
return func(value)
result = apply(lambda x: x ** 2, 5)
print(result) # 25
# Complex return types
def parse_config(path: str) -> dict[str, str | int | bool]:
return {"host": "localhost", "port": 8080, "debug": True}
# TypeVar for generic functions
from typing import TypeVar
T = TypeVar("T")
def first_or_default(items: Sequence[T], default: T) -> T:
"""Return first item or default if empty."""
return items[0] if items else default
print(first_or_default([1, 2, 3], 0)) # 1
print(first_or_default([], "default")) # 'default'
Аннотации в Python 3.14
# Python 3.14: deferred evaluation of annotations (PEP 649)
# Forward references work without quotes!
class Node:
def __init__(self, value: int, children: list[Node] | None = None):
self.value = value
self.children = children or []
def add_child(self, child: Node) -> None:
self.children.append(child)
# In earlier Python, you'd need:
# from __future__ import annotations
# or use string: children: list['Node'] | None = None
# Type aliases (Python 3.12+ syntax)
type Vector = list[float]
type Matrix = list[Vector]
type Callback = Callable[[str, int], bool]
def transform(matrix: Matrix, callback: Callback) -> Matrix:
...
Функции как объекты первого класса
# Functions are objects
def add(a: int, b: int) -> int:
return a + b
# Assign to variable
operation = add
print(operation(2, 3)) # 5
# Store in data structures
operations = {
"+": add,
"-": lambda a, b: a - b,
"*": lambda a, b: a * b,
}
print(operations["+"](10, 5)) # 15
print(operations["-"](10, 5)) # 5
print(operations["*"](10, 5)) # 50
# Pass as argument
def apply_twice(func: Callable[[int], int], value: int) -> int:
return func(func(value))
print(apply_twice(lambda x: x * 2, 3)) # 12
# Return from function
def make_multiplier(factor: int) -> Callable[[int], int]:
def multiplier(x: int) -> int:
return x * factor
return multiplier
double = make_multiplier(2)
triple = make_multiplier(3)
print(double(5)) # 10
print(triple(5)) # 15
# Function attributes
print(add.__name__) # 'add'
print(add.__annotations__) # {'a': <class 'int'>, 'b': <class 'int'>, 'return': <class 'int'>}
print(add.__defaults__) # None (no defaults)
Итоги
defопределяет функцию,returnвозвращает значение (или None)- Mutable defaults -- опасная ловушка, используйте
Noneи создавайте объект внутри *args-- переменное число позиционных аргументов (tuple)**kwargs-- переменное число именованных аргументов (dict)/-- positional-only параметры (до/)*-- keyword-only параметры (после*)- Type hints обязательны для читаемого и поддерживаемого кода
- Python 3.14:
PEP 649-- ленивое вычисление аннотаций, forward references без кавычек - Функции -- объекты первого класса: можно присваивать, передавать, возвращать