В Python операторы присваивания обращаются к переменным класса или экземпляра, когда они передаются в качестве значения по умолчанию в определении метода класса? - PullRequest
0 голосов
/ 06 сентября 2018

для Python 3.7

У меня есть класс с атрибутом класса (общеклассовая переменная):

class foo:
    var="value goes here"

, а также переменная экземпляра, созданная в методе init класса:

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

Передача переменной класса в параметр

Переменная класса имеет то же имя, что и параметр init , и это не смущает интерпретатор, поскольку интерпретатор обрабатывает любое поле слева от знака "=" как новую переменную в рамках метода. Это достигается путем заполнения нового пространства имен (словаря переменных) для области действия метода, реализованного в виде массива: например, parameters[1] = "var" или ассоциативный массив: parameters['var'] = pointer_to_value. Затем интерпретатор просматривает тело метода и заменяет общую ссылку для любых ссылок на «var», которые встречаются на правой стороне знака 101 = . На самом деле, это ложь, но это проще понять, чем то, что она действительно делает:

Интерпретатор идентифицирует соответствующее регулярное выражение .*= *var(,{0,1}| *) *(;{0,1}|\n*) и затем передает соответствующее значение pointer_to_value в стек вызовов программы). Из-за этого интерпретатору все равно, как называются параметры, и он не замечает неоднозначности var = var. Тот факт, что неоднозначность может быть решена, является побочным эффектом структуры языка, а не преднамеренным дизайнерским решением. В конце концов, спросите себя, при определении метода, зачем вам доступ к переменной из внутри метода, который вы определяете? И зачем вам вызывать операцию присваивания переменной в родительской области из определения метода? Это и нелогичные действия, и их возможности пространства имен взаимоисключающие, поэтому интерпретатору никогда не нужно устранять неоднозначность.

И наоборот, интерпретатор обрабатывает правую часть знака "=" как существующее значение и ищет в пространстве имен класса определения переменных.

Сохранение параметра в переменной экземпляра

Внутри метода переменная экземпляра также имеет то же имя, что и переменная класса и параметр, и это работает внутри метода init , поскольку доступ к переменной экземпляра осуществляется через ссылку self, например,

self.varname = varname

проблема

Определение метода

Мне нужно получить доступ к переменной экземпляра из определения метода другого метода, и я хочу использовать то же имя для параметра этой функции:

def lookup(self, var=var):
    print(var)

Получит ли выражение var = var атрибут класса или атрибут экземпляра? Что конкретно делает methodname(self) с собой? Является ли self ссылкой на реальный объект или это только меняет поведение интерпретатора с статического метода на метод экземпляра? Может ли интерпретатор автоматически контекстуализировать правую часть знака "=" как атрибут экземпляра любого объекта, введенного в methodname(object)?

Метод Body

В теле метода, если я назначу var ...

def lookup(self, var=var):
    var = var
  1. Будет ли оно храниться в переменной класса, переменной экземпляра или новой переменной с тем же именем?
  2. Получит ли она переменную класса, переменную экземпляра или переменную метода?
  3. Как явно указать эти переменные?

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

Ответы [ 3 ]

0 голосов
/ 06 сентября 2018

Единственный способ получить доступ к переменной экземпляра - это атрибут self.

Когда вы просто ссылаетесь на var, это никогда не переменная экземпляра; это всегда локальная, включающая, глобальная или встроенная переменная.


В вашем определении метода:

def lookup(self, var=var):
    print(var)

… у вас есть параметр с именем var. Параметры являются локальными переменными. print(var) внутри тела выводит эту локальную переменную.

А как насчет этого?

def lookup(self, var=var):
    var = var

Опять же, var - это локальная переменная - параметр. Итак, вы просто присваиваете текущее значение этой локальной переменной той же самой переменной. Что не имеет никакого полезного эффекта, но, конечно, это совершенно законно.


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

ОК, откуда берется значение по умолчанию?

Во время определения функции (когда выполняется оператор def), var ищется в текущей области видимости, то есть в теле определения class, и его значение сохраняется как значение по умолчанию в объект функции (он должен быть виден как foo.lookup.__defaults__[0]).

Итак, значением по умолчанию является "value goes here".

Обратите внимание, что не - захват закрытия или другая ссылка на атрибут класса. Когда выполняется оператор class, он использует то же самое пространство имен class body для построения атрибутов класса, так что в итоге вы получите foo.var в качестве другого имени для того же значения, что и в foo.lookup.__defaults__[0]. Но это совершенно независимые имена для этого значения; Вы можете переназначить foo.var = 3, и значением по умолчанию для параметра lookup будет по-прежнему "value goes here".


Итак, чтобы ответить на ваши конкретные вопросы:

Будет ли оно храниться в переменной класса, переменной экземпляра или новой переменной с тем же именем?

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

Получит ли она переменную класса, переменную экземпляра или переменную метода?

Если под «переменной метода» вы подразумеваете параметр, то он является последним.

Как явно указать эти переменные?

Точно так же, как вы явно ссылаетесь на что-то еще:

  • var - локальная переменная global-or-встроенная.
  • self.var - это атрибут экземпляра или атрибут класса, если атрибут экземпляра отсутствует.
  • type(self).var - это атрибут класса, даже если есть атрибут экземпляра.
0 голосов
/ 06 сентября 2018

Я сделаю все возможное, чтобы ответить на ваши вопросы, но если я смогу сначала выделить вашу проблему:

Я хочу использовать то же имя для параметра этой функции

Не делай этого. Нет логической причины делать это, и если вас это смущает, теперь представьте, что вы почувствуете, когда посмотрите на свой код через несколько часов / дней / недель / месяцев / лет. Представьте, что будет чувствовать кто-то другой, смотрящий на ваш код.

Для всего, что я пишу, представьте, что у нас есть экземпляр с именем bob

Это приведет к тому, что bob.var будет "значение здесь" каждый раз, когда оно создается. Для каждого нового объекта var будет установлено значение «здесь идет»

class foo:
    var="value goes here"

Это заставит bob.var быть тем значением, которое передается в foo во время создания экземпляра. Если ничего не пропущено, по умолчанию будет указано «значение идет сюда»

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

Это напечатает все, что передано ИЛИ «значение идет сюда», если ничего не передано

def lookup(self, var=var):
    print(var)

Это ничего не делает, так как рассматриваемый var локально ограничен и существует только до конца метода

def lookup(self, var=var):
    var = var

Надеюсь, это поможет и просто повторю: Используйте разные имена переменных для разных переменных .

0 голосов
/ 06 сентября 2018

Неявный аргумент метода self - это экземпляр . Таким образом, в то время как self.var является переменной экземпляра, вам необходимо добавить префикс к объекту класса для доступа к переменной класса. Иногда вы можете не знать объект класса, и тогда вы можете просто использовать атрибут __class__, то есть переменная класса self.__class__.var.

Чтобы ответить на ваши три вопроса 1. Ваше назначение в методе lookup создаст новую переменную с тем же именем var. 2. Он получит аргумент, который вы передали методу (я думаю, это то, что вы подразумеваете под «переменной метода») 3. Приведенный ниже код показывает, как вы можете явно обращаться к этим переменным (это показано в __init__, но на самом деле не имеет значения, в каком методе вы находитесь.

class A(object):
    var = 'class_variable'

    def __init__(self, var=var):
        print('Argument var={}'.format(var))
        var = var
        self.var = var
        print('Instance var={}'.format(self.var))
        print('Class var={}'.format(self.__class__.var))
        print('Alternative access to Class var={}'.format(A.var))

Это дает

>>> a = A()
Argument var=class_variable
Instance var=class_variable
Class var=class_variable
Alternative access to Class var=class_variable
>>> b = A('v')
Argument var=v
Instance var=v
Class var=class_variable
Alternative access to Class var=class_variable
>>> c = A()
Argument var=class_variable
Instance var=class_variable
Class var=class_variable
Alternative access to Class var=class_variable
...