Подтвердить что ты не робот

Атрибуты класса Python и модуля

Мне интересно услышать некоторое обсуждение атрибутов класса в Python. Например, что является хорошим вариантом использования атрибутов класса? По большей части я не могу придумать случай, когда атрибут класса предпочтительнее использовать атрибут уровня модуля. Если это так, то почему они вокруг?

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

Не забудьте прокомментировать, как бы вы справились со следующими ситуациями:

  • Константные значения, используемые классом и/или подклассами. Это может включать в себя словарные клавиши "магического номера" или индексы списков, которые никогда не будут меняться, но возможно потребуется однократная инициализация.
  • Атрибут класса по умолчанию, который в редких случаях обновляется для специального экземпляра класса.
  • Глобальная структура данных, используемая для представления внутреннего состояния класса, общего для всех экземпляров.
  • Класс, который инициализирует ряд атрибутов по умолчанию, не подверженных влиянию аргументов конструктора.

Некоторые похожие сообщения:
Разница между атрибутами класса и экземпляра

4b9b3361

Ответ 1

# 4: Я никогда не использую атрибуты класса для инициализации атрибутов экземпляра по умолчанию (те, которые вы обычно помещаете в __init__). Например:

class Obj(object):
    def __init__(self):
        self.users = 0

и никогда:

class Obj(object):
    users = 0

Почему? Поскольку это непоследовательно: он не делает то, что вам нужно, когда вы назначаете что-либо, кроме объекта-инварианта:

class Obj(object):
    users = []

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


Что касается остальных, я обычно помещаю значения класса в класс. Это не так много, потому что глобальные "злые" - они не такие большие, как на некоторых языках, потому что они все еще привязаны к модулю, если только сам модуль слишком велик, - но если внешний код хочет получить к ним доступ, удобно иметь все соответствующие значения в одном месте. Например, в module.py:

class Obj(object):
    class Exception(Exception): pass
    ...

а затем:

from module import Obj

try:
    o = Obj()
    o.go()
except o.Exception:
    print "error"

Помимо того, что подклассы могут изменять значение (которое не всегда требуется в любом случае), это означает, что мне не нужно с трудом импортировать имена исключений и кучу других вещей, необходимых для использования Obj. "из импорта модуля Obj, ObjException,..." становится утомительно быстро.

Ответ 2

что является хорошим вариантом использования атрибутов класса

Случай 0. Методы класса - это только атрибуты класса. Это не просто техническое сходство - вы можете получить доступ и модифицировать методы класса во время выполнения, назначив для них вызывающие вызовы.

Случай 1. Модуль может легко определить несколько классов. Разумно инкапсулировать все о class A в A... и все о class B в B.... Например,

# module xxx
class X:
    MAX_THREADS = 100
    ...

# main program
from xxx import X

if nthreads < X.MAX_THREADS: ...

Случай 2. Этот класс имеет множество атрибутов по умолчанию, которые могут быть изменены в экземпляре. Здесь способность оставить атрибут "глобальным дефолтом" является функцией, а не ошибкой.

class NiceDiff:
    """Formats time difference given in seconds into a form '15 minutes ago'."""

    magic = .249
    pattern = 'in {0}', 'right now', '{0} ago'

    divisions = 1

    # there are more default attributes

Один создает экземпляр NiceDiff для использования существующего или слегка измененного форматирования, но локализатор на другом языке подклассы класса для реализации некоторых функций принципиально иным способом и переопределяет константы:

class Разница(NiceDiff): # NiceDiff localized to Russian
    '''Из разницы во времени, типа -300, делает конкретно '5 минут назад'.'''

    pattern = 'через {0}', 'прям щас', '{0} назад'

Ваши дела:

  • константы - да, я помещаю их в класс. Странно сказать self.CONSTANT = ..., поэтому я не вижу большого риска для их скрещивания.
  • Атрибут по умолчанию - смешанный, как указано выше, может перейти в класс, но также может перейти в __init__ в зависимости от семантики.
  • Глобальная структура данных --- переходит к классу, если класс только, но может также перейти в модуль, в любом случае должен быть очень хорошо документирован.

Ответ 3

Атрибуты класса часто используются для переопределения значений по умолчанию в подклассах. Например, BaseHTTPRequestHandler имеет константы класса sys_version и server_version, последний по умолчанию - "BaseHTTP/" + __version__. SimpleHTTPRequestHandler переопределяет server_version на "SimpleHTTP/" + __version__.

Ответ 4

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

В ваших ситуациях 1-4 я избегал бы глобальных глобалов, насколько могу, и предпочитаю использовать атрибуты класса, которые позволяют использовать инкапсуляцию.