Классы в Python: поля экземпляра и переменные уровня класса

Раздел: Основы Python -> Функции/Классы

Класс и его параметры

Параметры класса в Python разделяются на два уровня. Уровень класса содержит атрибуты, общие для всех экземпляров. Уровень экземпляра содержит атрибуты, которые создаются в методе __init__. Аргументы, переданные после имени класса, попадают в __init__ и становятся индивидуальными параметрами объекта.

Основной способ определить индивидуальные параметры объекта, это объявить метод __init__ с аргументами и сохранить их через self.


class Car:
    def __init__(self, brand, price):
        self.brand = brand
        self.price = price

car = Car('Skoda', 1200000)
print(car.brand, car.price)

параметры класса python (параметры класса в python)

Skoda 1200000

При вызове Car('Skoda', 1200000) значения brand и price сразу связываются с объектом car. Обращение car.brand и car.price даёт переданные данные.

Ошибка избыточности. Если параметр одинаков для всех экземпляров, его не следует класть в __init__. Такое значение объявляется как переменная класса.

Как задать параметр, общий для всех экземпляров класса?

Переменную, объявленную в теле класса, называют атрибутом класса. Она доступна через сам класс и через объекты.


class Car:
    wheels = 4

car1 = Car()
car2 = Car()
print(Car.wheels, car1.wheels, car2.wheels)
4 4 4

Запись Car.wheels = 6 изменит значение для всех экземпляров одновременно.

Как сделать параметр необязательным?

В сигнатуре __init__ присваивается значение по умолчанию, и тогда аргумент можно не передавать.


class Car:
    def __init__(self, brand, price=None):
        self.brand = brand
        self.price = price

car = Car('Lada')
print(car.brand, car.price)
Lada None

Значение None удобно как маркер отсутствия данных. Позже этому атрибуту присваивается реальное значение.

Как принять переменное число параметров при создании объекта?

Параметр *args собирает позиционные аргументы в кортеж, а **kwargs собирает именованные аргументы в словарь.


class Notebook:
    def __init__(self, **options):
        self.options = options

note = Notebook(cpu='Intel', ram=16)
print(note.options)
{'cpu': 'Intel', 'ram': 16}

Такой способ подходит, когда у разных объектов набор параметров не фиксирован.

Как создать вычисляемый параметр без сохранения значения?

Декоратор property превращает метод в свойство, доступное для чтения.


class Square:
    def __init__(self, side):
        self.side = side

    @property
    def area(self):
        return self.side ** 2

s = Square(5)
print(s.area)
25

Опасный изменяемый аргумент по умолчанию. Список или словарь в качестве значения по умолчанию создаётся один раз. Все будущие объекты получат один и тот же объект.


class Order:
    def __init__(self, goods=[]):
        self.goods = goods

order1 = Order()
order1.goods.append('apple')
order2 = Order()
print(order2.goods)
['apple']

Решение состоит в том, чтобы использовать в параметре значение None и внутри метода создавать новый список.


class Order:
    def __init__(self, goods=None):
        if goods is None:
            goods = []
        self.goods = goods

order1 = Order()
order1.goods.append('apple')
order2 = Order()
print(order2.goods)
[]

Примеры работы с параметрами класса

В этом разделе показаны дополнительные варианты использования параметров класса и параметров экземпляра.

Атрибут класса, который изменяется через класс или через объект

Пример

class Server:
    port = 8080

srv1 = Server()
srv2 = Server()
Server.port = 9090
print(srv1.port, srv2.port)

srv1.port = 7070
print(srv1.port, Server.port, srv2.port)
9090 9090
7070 9090 9090

Пока атрибут не переопределён в объекте, объект получает значение из класса. Присваивание srv1.port создаёт новый атрибут только у srv1.

Подсчёт экземпляров через атрибут класса

Пример

class Item:
    total = 0

    def __init__(self):
        self.__class__.total += 1

first = Item()
second = Item()
print(Item.total, first.total, second.total)
2 2 2

Ссылка self.__class__ ведёт на класс Item, поэтому запись total += 1 меняет значение во всём классе.

Ключевые аргументы в методе __init__

Пример

class AppConfig:
    def __init__(self, *, host, port=80):
        self.host = host
        self.port = port

cfg = AppConfig(host='localhost')
print(cfg.host, cfg.port)
localhost 80

Звёздочка в сигнатуре запрещает передавать host и port позиционно, порядок параметров не влияет на вызов.

Переменное число позиционных и именованных параметров

Пример

class Event:
    def __init__(self, name, *tags, **meta):
        self.name = name
        self.tags = tags
        self.meta = meta

event = Event('release', 'v1', 'stable', date='2025-01-01')
print(event.name)
print(event.tags)
print(event.meta)
release
('v1', 'stable')
{'date': '2025-01-01'}

Параметр *tags собирает лишние позиционные значения, параметр **meta собирает именованные значения.

Вычисляемый параметр экземпляра на основе других параметров

Пример

class Rectangle:
    def __init__(self, width, height):
        self.width = width
        self.height = height

    @property
    def area(self):
        return self.width * self.height

rect = Rectangle(3, 4)
print(rect.area)
12

Свойство area не хранится как отдельная переменная. Его значение пересчитывается каждый раз при обращении.

Проверка значения в setter

Пример

class BankAccount:
    def __init__(self, owner, balance):
        self.owner = owner
        self.balance = balance

    @property
    def balance(self):
        return self._balance

    @balance.setter
    def balance(self, amount):
        if amount < 0:
            raise ValueError('Баланс не может быть отрицательным')
        self._balance = amount

account = BankAccount('Ivan', 1000)
print(account.balance)
account.balance = 1500
print(account.balance)
1000
1500

Через _balance внутри класса хранится исходное значение, а доступ к нему извне проходит через свойство balance.

Автоматическая генерация __init__ с dataclass

Пример

from dataclasses import dataclass

@dataclass
class Book:
    title: str
    author: str
    pages: int = 0

book = Book('Мастер и Маргарита', 'Булгаков', 480)
print(book)
Book(title='Мастер и Маргарита', author='Булгаков', pages=480)

Декоратор dataclass сам создаёт метод __init__, а также строковое представление объекта.

Фиксированный набор параметров со __slots__

Пример

class Point3D:
    __slots__ = ('x', 'y', 'z')

    def __init__(self, x, y, z):
        self.x = x
        self.y = y
        self.z = z

p = Point3D(1, 2, 3)
try:
    p.q = 5
except AttributeError as exc:
    print(exc)
'Point3D' object has no attribute 'q'

Поле __slots__ ограничивает список возможных параметров экземпляра, за счёт этого уменьшается память объекта.

Параметры класса в Python - comments

En
параметры класса python (python)