Python: за __init __ (self, foo) всегда следует self.foo = foo? - PullRequest
4 голосов
/ 16 марта 2012

В течение трех дней я очень старался, чтобы обернуть голову вокруг __init__ и «себя», начиная с упражнения «Изучай Python 42» и продолжая читать части документации Python, Алан Глава Голда по объектно-ориентированному программированию , Нити стека, как этот, на "self" , и этот , и, честно говоря, я готовлюсь ударить себя в лицо с кирпичом, пока я не потерял сознание.

При этом я заметил действительно распространенное соглашение в начальных определениях __init__, которое заключается в том, чтобы следовать (self, foo), а затем немедленно объявить в этом определении, что self.foo = foo.

Из LPTHW, ex42:

class Game(object): 
    def __init__(self, start): 
        self.quips = ["a list", "of phrases", "here"]
        self.start = start

От Алана Голда:

def __init__(self,val): self.val = val

Я нахожусь в этом ужасном пространстве, где я вижу, что есть только одна большая вещь, которую я не получаю, и она остается непрозрачной, независимо от того, сколько я об этом читаю и пытаюсь это выяснить. Возможно, если кто-нибудь сможет объяснить мне эту последовательность, свет включится. Это потому, что мы должны сказать, что переменная "foo" всегда будет равна параметру (foo), который сам содержится в параметре "self", который автоматически присваивается определению, к которому он присоединен?

Ответы [ 3 ]

6 голосов
/ 16 марта 2012

Возможно, вы захотите изучить объектно-ориентированное программирование .

Грубо говоря, когда вы говорите

class Game(object):
    def __init__(self, start):
        self.start = start

вы говорите:

  • У меня есть тип "вещи" с именем Game

  • Всякий раз, когда создается новый Game, он потребует у меня дополнительную информацию, start. (Это потому, что инициализатор Game с именем __init__ запрашивает эту информацию.)

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

  • В игре, вероятно, нужно вспомнить, что за start я дал. Таким образом, он хранит эту информацию «внутри», создавая переменную экземпляра и с именем start (ничего особенного, это просто имя, которое вы хотите), и присваивая значение start параметр для переменной start.

    • Если он не хранит значение параметра, эта информация не будет доступна для дальнейшего использования.

Надеюсь, это объясняет, что происходит.

2 голосов
/ 16 марта 2012

Я не совсем уверен, что вам не хватает, поэтому позвольте мне затронуть некоторые основные предметы.

В объекте Python class есть два "специальных" имени инициализации, одно из которых относительно редко беспокоит пользователей, называется __new__, а другое - гораздо более обычное, называется __init__. * 1006. *

Когда вы вызываете конструктор объекта класса, например, (на основе вашего примера) x = Game(args), это сначала вызывает Game.__new__, чтобы получить память для хранения объекта, а затем Game.__init__, чтобы заполнить эту память. В большинстве случаев вы можете позволить базовому object.__new__ выделять память, и вам просто нужно заполнить ее. (Вы можете использовать свой собственный распределитель для особых странных редких случаев, таких как объекты, которые никогда не меняются и могут иметь общие идентификационные данные, как обычные целые числа, например. Это также для "метаклассов", которые делают странные вещи. Но это тема гораздо позже.)

Ваша Game.__init__ функция вызывается со «всеми аргументами конструктора» плюс один, спрятанный впереди, который является памятью, выделенной для самого объекта. (Для «обычных» объектов это в основном словарь «атрибутов», плюс волшебный клей для классов, но для объектов с __slots__ словарь атрибутов опущен.) Присвоение имени первому аргументу self - это просто соглашение, но не не нарушайте это, люди будут ненавидеть вас, если вы делаете. : -)

Нет ничего, что требовало бы от вас сохранения всех аргументов в конструкторе. Вы можете установить любой или все атрибуты экземпляра, которые вам нравятся:

class Weird(object):
    def __init__(self, required_arg1, required_arg2, optional_arg3 = 'spam'):
        self.irrelevant = False
    def __str__(self):
        ...

Дело в том, что экземпляр Weird () довольно бесполезен после инициализации, потому что вам необходимо передать два аргумента, которые просто отбрасываются, и дан третий необязательный аргумент, который также отбрасывается:

x = Weird(42, 0.0, 'maybe')

Единственный пункт, требующий, чтобы эти отброшенные аргументы были для будущего расширения, как это было (у вас могли быть эти неиспользуемые поля во время ранней разработки). Так что, если вы не сразу используете и / или не сохраняете аргументы в __init__, что-то определенно странное в Weird.

Кстати, единственная причина использования (object) в определении класса - указать Python 2.x, что это класс "нового стиля" (в отличие от очень старых классов Python "только для экземпляров") , Но, как правило, лучше использовать его - он делает то, что я сказал выше о object.__new__, например, правда :-) - до Python 3, где все в старом стиле полностью исчезло.

0 голосов
/ 16 марта 2012

Имена параметров должны быть осмысленными, чтобы передать роль, которую они играют в функции / методе, или некоторую информацию об их содержании.

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

Представьте, что у вас есть класс Game, который принимает playerList.

class Game:

    def __init__(self, playerList):
        self.playerList = playerList    # or self.players = playerList

    def printPlayerList(self):
        print self.playerList           # or print self.players

Этот список необходим в различных методах класса. Следовательно, имеет смысл присвоить его self.playerList. Вы также можете назначить его на self.players, что бы вы ни чувствовали себя более комфортно, и вы считаете понятным. Но если вы не назначите его self.<somename>, он не будет доступен в других методах.

Так что нет ничего особенного в том, как называть параметры / атрибуты / и т. Д. ( хотя есть и некоторые специальные методы класса ), но использование значимых имен облегчает понимание кода. Или вы бы поняли значение вышеприведенного класса, если бы имели:

class G:

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

    def ppl(self):
        print self.y

? :) Он делает то же самое, но его сложнее понять ...

...