Нужно различать внутренние и внешние переменные класса

В Python для объявления переменных класса обычно используется объект self, но это, строго говоря, способ объявления внутренних переменных экземпляра. Для доступа к переменной класса таким способом требуется создание экземпляра. Если нужно гарантировать одинаковое значение для всех объектов или добиться эффекта, похожего на ключевое слово static в C# или Java, переменную следует объявлять вне __init__().

class Person:
    ''' Можно объявить следующим образом.
    '''
    type = "person"
    
    def __init__(self):
        self.name = name
        self.age  = age
        # ...

Эта переменная будет доступна всем экземплярам с одним и тем же значением. При этом следует помнить, что доступ к ней, даже внутри самого объекта, осуществляется как Имя_класса.Имя_переменной. Например, если внутри класса People нужно изменить значение переменной type на another_people_type, можно обратиться к ней как People.type = another_people_type.

В Python тоже можно использовать геттеры и сеттеры

Хотя они не называются getter и setter, как в C# или Java, концепция соединения внутренней переменной класса и публичного свойства та же. Это полезно не только для сокрытия внутренних переменных, но и, что более удобно, для привязки логики к изменению значения переменной.

class Example:
    def __init__(self):
        self._variable = None
    
    @property
    def variable(self):
        return self._variable

    @variable.setter(self, value):
        self._variable = value

Как и в других языках, если объявить только @property без сеттера, попытка изменить значение через это свойство вызовет ошибку, так что нужно быть внимательным.

В имени модуля можно отражать количество классов

По умолчанию функции — это единица классификации кода, классы — единица классификации функций и переменных, модули — единица классификации классов. Один из способов лучше реализовать эту концепцию — если в одном модуле несколько классов, просто указывать имя модуля во множественном числе.

myproject/

├── app/
   ├── users/
   ├── models.py
   ├── views.py
   └── controllers.py
   ├── products/
   └── orders/

└── main.py

В этом случае в models.py можно использовать имена формата «имя + тип», например UserModel() или ProductModel (хотя это не конкретный пример из реального проекта). Такая структура даёт более чёткую классификацию классов по сравнению со случаем, когда в одном модуле только один класс, и позволяет использовать естественные конструкции при импорте: from models import UserModel as UM.

Интуитивно понятные имена лучше метафор

Интуитивность метафор в большинстве случаев половинчатая. Первая половина делает код лаконичным и более интересным для чтения при первом знакомстве, но вторая половина делает намерение, стоящее за кодом, размытым. Поэтому, на мой взгляд, по возможности лучше их избегать.

def main():
    ''' Пример с темой ювелира
    '''
    self.mining()  
    self.cutting() 
    self.crafting()
    self.selling()

def main()
    ''' Пример с темой шеф-повара высокой кухни
    '''
    self.washing()
    self.cutting()
    self.cooking()
    self.plating()

Я пробовал структурировать код с помощью различных метафор, например, как показано выше — сравнивая общий процесс с продажей драгоценностей (добыча-огранка-обработка-продажа) или имитируя процесс приготовления блюда шеф-поваром (мойка-нарезка-приготовление-сервировка). Но проблема была в том, что имена функций, построенные на метафорах, нечётко отражали реальную роль кода.

Из-за этого добавлялся дополнительный шаг в процессе понимания смысла, что создавало излишние трудности для других людей (или для меня самого в будущем) при чтении кода. Иногда даже происходила путаница, и писатель начинал больше думать о соблюдении темы класса или функции, чем о сути.

Вместо этого, пусть и менее занимательно, оказалось чище описывать роль класса стандартным образом.

def main():
    ''' Просто обычный случай
    '''
    download_data()
    basic_process()
    save_results()

Возможно, метафоры уместны, если вспомогательный образ, используемый для сравнения (например, цитаты из исторических событий или известные истории), обладает подавляющей интуитивностью. Но в большинстве остальных случаев более практичным оказалось просто и лаконично описывать то, что делает код.