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

Присвоение переменной класса в качестве значения по умолчанию аргументу метода класса

Я хотел бы создать метод внутри класса со значениями по умолчанию, взятыми из этого класса. В общем, я фильтрую некоторые данные. Внутри моего класса у меня есть метод, где обычно передаю вектор данных. Иногда у меня нет вектора, и я беру смоделированные данные. Каждый раз, когда я не передаю конкретный вектор, я бы хотел поместить имитированные данные по умолчанию. Я думал, что это должна быть легкая конструкция, где внутри моего определения метода я говорю a=self.vector. Но по какой-то причине у меня есть ошибка NameError: name 'self' is not defined. Упрощенная конструкция:

class baseClass(object):  # This class takes an initial data or simulation
    def __init__(self):
        self.x = 1
        self.y = 2

class extendedClass(baseClass): # This class does some filtering
    def __init__(self):
        baseClass.__init__(self)
        self.z = 5
    def doSomething(self, a=self.z):
        self.z = 3
        self.b = a

if __name__ == '__main__':
    a = extendedClass()
    print a.__dict__
    a.doSomething()
    print a.__dict__

Ожидаемый результат должен быть:

{'y': 2, 'x': 1, 'z': 5}
{'y': 2, 'x': 1, 'z': 3, 'b': 5}

Я попробовал назначение по умолчанию как def doSomething(self, a=z):, очевидно, что он не работает. Насколько я понимаю, self.z виден в этой области и не должен быть проблемой, чтобы иметь его как значение по умолчанию. Не знаю, почему у меня есть эта ошибка и как это сделать. Это, наверное, простой вопрос, но я пытаюсь понять это или найти решение без каких-либо недостатков. Я нашел похожие вопросы только для других языков.

4b9b3361

Ответ 1

Ваше понимание неверно. self сам по себе является параметром определения этой функции, поэтому он не может быть в области видимости в этой точке. Это только в области видимости внутри самой функции.

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

def doSomething(self, a=None):
    if a is None:
        a = self.z
    self.z = 3
    self.b = a

Ответ 2

Здесь разбор кода для простого примерного модуля. Объект кода является контейнером только для чтения для байт-кода, константами и именами, которые он использует, и метаданными о количестве локальных переменных, требуемом размере стека и т.д. Обратите внимание, что все объекты кода скомпилированы как константы. Они создаются во время компиляции. Но объекты class A и function test создаются во время выполнения (например, когда модуль импортируется).

Чтобы сделать класс, BUILD_CLASS берет имя 'A', базы tuple (object,) и dict, которые содержат атрибуты пространства имен классов. Это похоже на создание экземпляра типа вручную, вызывая type(name, bases, dict). Чтобы сделать dict, функция создается из объекта кода A и вызывается. Наконец, объект класса хранится в пространстве имен модулей через STORE_NAME.

В объекте кода A, self.z загружается в стек как аргумент MAKE_FUNCTION. Байт-код op LOAD_NAME будет искать self в текущих локалях (т.е. Определяемое пространство имен классов), глобальные модули и встроенные модули. Это, очевидно, не удастся, если self не определено в глобальной или встроенной области; он явно не определен в локальной области.

Однако, если это удалось, функция будет создана с помощью (self.z,) в качестве ее атрибута __defaults__, а затем будет сохранена в локальном имени test.

>>> code = compile('''
... class A(object):
...   def test(self, a=self.z): pass
... ''', '<input>', 'exec')

>>> dis.dis(code)
  2           0 LOAD_CONST               0 ('A')
              3 LOAD_NAME                0 (object)
              6 BUILD_TUPLE              1
              9 LOAD_CONST               1 (<code object A ...>)
             12 MAKE_FUNCTION            0
             15 CALL_FUNCTION            0
             18 BUILD_CLASS         
             19 STORE_NAME               1 (A)
             22 LOAD_CONST               2 (None)
             25 RETURN_VALUE

>>> dis.dis(code.co_consts[1]) # code object A
  2           0 LOAD_NAME                0 (__name__)
              3 STORE_NAME               1 (__module__)

  3           6 LOAD_NAME                2 (self)
              9 LOAD_ATTR                3 (z)
             12 LOAD_CONST               0 (<code object test ...>)
             15 MAKE_FUNCTION            1
             18 STORE_NAME               4 (test)
             21 LOAD_LOCALS         
             22 RETURN_VALUE       

@uselpa: Пример вашего pastebin (переписан для 2.x):

>>> code = compile('''
... default = 1
... class Cl(object):
...     def __init__(self, a=default):
...         print a
... Cl()
... default = 2
... Cl()
... ''', '<input>', 'exec')
>>> dis.dis(code)
  2           0 LOAD_CONST               0 (1)
              3 STORE_NAME               0 (default)

  3           6 LOAD_CONST               1 ('Cl')
              9 LOAD_NAME                1 (object)
             12 BUILD_TUPLE              1
             15 LOAD_CONST               2 (<code object Cl ...>)
             18 MAKE_FUNCTION            0
             21 CALL_FUNCTION            0
             24 BUILD_CLASS         
             25 STORE_NAME               2 (Cl)

  6          28 LOAD_NAME                2 (Cl)
             31 CALL_FUNCTION            0
             34 POP_TOP             

  7          35 LOAD_CONST               3 (2)
             38 STORE_NAME               0 (default)

  8          41 LOAD_NAME                2 (Cl)
             44 CALL_FUNCTION            0
             47 POP_TOP             
             48 LOAD_CONST               4 (None)
             51 RETURN_VALUE        

Как вы можете видеть, объект класса Cl (и объект функции __init__) только создается и сохраняется в локальном имени 'Cl' один раз. Модуль выполняется последовательно во время выполнения, поэтому последующее переименование имени default не повлияет на значение по умолчанию в __init__.

Вы можете динамически создавать новую функцию с использованием ранее скомпилированного кода и нового значения по умолчанию:

>>> default = 1
>>> class Cl(object):
...     def __init__(self, a=default):
...         print a
... 

>>> from types import FunctionType
>>> default = 2
>>> Cl.__init__ = FunctionType(
...   Cl.__init__.__code__, globals(), '__init__', (default,), None)
>>> c = Cl()
2

Повторное использование уже скомпилированного объекта кода из __init__.__code__ для создания функции с новым кортежем __defaults__:

>>> Cl.__init__.__defaults__
(2,)

Ответ 4

Это вставит self.z если a None/False/empty_value:

def doSomething(self, a=None):
        self.z = 3
        self.b = (a or self.z)