Класс Python против атрибутов модуля - PullRequest
13 голосов
/ 09 августа 2009

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

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

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

  1. Постоянные значения, используемые классом и / или подклассами. Сюда могут входить ключи словаря «магического числа» или списочные индексы, которые никогда не изменятся, но, возможно, потребуется однократная инициализация.
  2. Атрибут класса по умолчанию, который в редких случаях обновляется для особого экземпляра класса.
  3. Глобальная структура данных, используемая для представления внутреннего состояния класса, совместно используемого всеми экземплярами.
  4. Класс, который инициализирует ряд атрибутов по умолчанию, на которые не влияют аргументы конструктора.

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

Ответы [ 4 ]

7 голосов
/ 09 августа 2009

# 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, ..." быстро становится утомительным.

4 голосов
/ 10 августа 2009

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

Случай 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__ в зависимости от семантики.
  • Глобальная структура данных --- идет в класс, если используется только классом, но может также переходить в модуль, в любом случае должна быть очень хорошо документированной.
2 голосов
/ 09 августа 2009

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

1 голос
/ 09 августа 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...