Класс в конфликте имен аргументов функции с вложенными атрибутами класса - PullRequest
0 голосов
/ 19 февраля 2019

Рассмотрим этот код:

def gee(bool_, int32, int64, str_):

    class S:
        bool_ = bool_
        int32 = int32
        int64 = int64
        str_ = str_

    return S

gee(1, 2, 3, 4)

Выполнение этого дает ошибку:

Traceback (most recent call last):
  File "test_.py", line 36, in <module>
    gee(1, 2, 3, 4)
  File "test_.py", line 27, in gee
    class S:
  File "test_.py", line 28, in S
    bool_ = bool_
NameError: name 'bool_' is not defined

Я понятия не имею, какие правила области действия / закрытия применяются здесь.nonlocal исправляет ошибку, но результат не тот, который я ожидал:

def gee(bool_, int32, int64, str_):

    class S:
        nonlocal bool_, int32, int64, str_
        bool_ = None
        int32 = None
        int64 = None
        str_ = None
    print(bool_, int32, int64, str_ )

    return S

g = gee(1, 2, 3, 4)
g.bool_

Выходы:

None None None None
Traceback (most recent call last):
  File "test_.py", line 38, in <module>
    g.bool_
AttributeError: type object 'S' has no attribute 'bool_'

Кроме переименования, что я могу сделать, чтобы сделать назначения в 1-м фрагменте кодаРабота?И почему он так себя ведет?Потому что есть name = ...?Почему Python не оценивает имя перед присваиванием?

Ответы [ 6 ]

0 голосов
/ 21 февраля 2019

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

0 голосов
/ 19 февраля 2019

Вы можете использовать этот код.Без изменения названия ваших переменных.Вы можете использовать свойства и конструктор класса для решения этой проблемы.

def gee(bool_, int32, int64, str_):

    class S:
        def __init__(self, bool_, int32, int64, str_):
            self.bool_ = bool_
            self.int32 = int32
            self.int64 = int64
            self.str_ = str_

        def get_bool_(self):
            return self.bool_

        def set_bool_(self, bool_):
            self.bool_ = bool_

        def get_int32(self):
            return self.int32

        def set_int32(self, int32):
            self.int32 = int32

        def get_int64(self):
            return self.int64

        def set_int64(self, int64):
            self.int64 = int64

        def get_str_(self):
            return self.str_

        def set_str_(self, str_):
            self.str_ = str_

    print(bool_, int32, int64, str_ )

    return S
g = gee(1, 2, 3, 4)
0 голосов
/ 19 февраля 2019

Давайте ответим на ваш вопрос другим вопросом - как, по вашему мнению, Python знает, что bool_ есть что в строке bool_ = bool_?

def gee(bool_, int32, int64, str_):

    class S:
        bool_ = bool_

Является ли первый bool_ атрибутом класса?Или это nonlocal bool_ из функции?Или это * задыхается * объект global?А как насчет назначенного объекта, к которому относится область = bool_? 1011 *

Вот почему мантра Python гласит: «Явный лучше, чем неявный».Область действия должна быть четко определена, чтобы Python понимал, на что вы ссылаетесь, иначе интерпретатор предполагает самую непосредственную область действия.

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

Другой способ - переместить определение атрибута класса за пределы области действия класса:

def gee(bool_, int32, int64, str_):

    class S:
        pass

    S.bool_ = bool_
    S.int32 = int32
    ...
    return S

Таким образом, вы можете четко определить, какой bool_ принадлежит какой области.

0 голосов
/ 19 февраля 2019

Имена переменных и область действия - ваша проблема.Если вы немного измените свой код, он должен работать.

def gee(some_bool, some_int32, some_int64, some_str):

    class S:
        bool_ = some_bool
        int32 = some_int32
        int64 = some_int64
        str_ = some_str

    return S

print(gee(1, 2, 3, 4))
# 1, 2, 3, 4
0 голосов
/ 19 февраля 2019

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

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

def gee(bool_, int32, int64, str_):

    class S:
        def __init__(self, bool_=bool_, int32=int32, int64=int64, str_=str_):
            self.bool_ = bool_
            self.int32 = int32
            self.int64 = int64
            self.str_ = str_

    return S

my_class = gee(1, 2, 3, 4)
my_instance = my_class()

print(my_instance.bool_)

Этот код печатается 1.Это возможно по той причине, что именованные параметры разрешают выражения значений с использованием стандартных (лексических) правил области видимости, а сами имена параметров вводятся в пространство имен в начале каждого вызова.

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

S.bool_ = bool_

, потому что к моменту запуска метода __init__ имя класса S уже было связано в лексически заключенном пространстве имен.Даже если значения привязаны к классу, на них можно ссылаться относительно экземпляра - поскольку их там нет, интерпретатор продолжает поиск в классе экземпляра.

Однако в любом случае вызовкласс необходим для установки значений атрибута внутри __init__.

0 голосов
/ 19 февраля 2019

В пределах области, если вы присваиваете имя, оно является локальным в этой области.Тело класса - это область.Таким образом, _bool является локальным, и поэтому попытка присвоить ему _bool является ошибкой, поскольку _bool еще не определено.В отличие от некоторых других языков, Python не просматривает внешние области, когда имя еще не определено в области.

nonlocal, как вы заметили, не решает проблему, поскольку вызывает внешнюю область действия.имя, которое будет присвоено.

Простой ответ - использовать разные имена.

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

...